Я пытаюсь использовать функции в C ++ DLL из C #, но я получил ошибку: «попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена»
Кто-нибудь знает, как это исправить?
Вот функции C ++:
typedef void *DGNHandle;
__declspec(dllexport) DGNHandle CPL_DLL DGNOpen( const char *, int );
__declspec(dllexport) DGNElemCore CPL_DLL *DGNReadElement( DGNHandle )
Вот структура в C ++:
typedef struct {
int offset;
int size;
int element_id; /*!< Element number (zero based) */
int stype; /*!< Structure type: (DGNST_*) */
int level; /*!< Element Level: 0-63 */
int type; /*!< Element type (DGNT_) */
int complex; /*!< Is element complex? */
int deleted; /*!< Is element deleted? */
int graphic_group; /*!< Graphic group number */
int properties; /*!< Properties: ORing of DGNPF_ flags */
int color; /*!< Color index (0-255) */
int weight; /*!< Line Weight (0-31) */
int style; /*!< Line Style: One of DGNS_* values */
int attr_bytes; /*!< Bytes of attribute data, usually zero. */
unsigned char *attr_data; /*!< Raw attribute data */
int raw_bytes; /*!< Bytes of raw data, usually zero. */
unsigned char *raw_data; /*!< All raw element data including header. */
} DGNElemCore;
И ниже преобразованные коды находятся в C #:
[StructLayout(LayoutKind.Sequential )]
public class DGNElemCore
{
public int attr_bytes;
public byte[] attr_data;
public int color;
public int complex;
public int deleted;
public int element_id;
public int graphic_group;
public int level;
public int offset;
public int properties;
public int raw_bytes;
public byte[] raw_data;
public int size;
public int style;
public int stype;
public int type;
public int weight;
}
[DllImport("DgnLib.dll", EntryPoint = "DGNOpen")]
public static extern IntPtr DGNOpen(string fileName, int bUpdate);
[DllImport("DgnLib.dll", EntryPoint = "DGNReadElement")]
public static extern DGNElemCore DGNReadElement(IntPtr DGNHandle)
Коды для тестирования:
DGNElemCore element = new DGNElemCore();
element = DgnFile.DGNReadElement(dgnFile.oDgnFile) **//Throw error**
Ваша декларация DGNElemCore
в вашем коде C # неверно — он должен точно соответствовать вашей структуре C (особенно по размеру), так как в противном случае код маршаллинга попытается неправильно распределить память. Пример определения, которое будет работать (например, не вызывает проблем при сортировке), будет следующим
[StructLayout(LayoutKind.Sequential )]
public class DGNElemCore
{
int offset;
int size;
int element_id;
int stype;
int level;
int type;
int complex;
int deleted;
int graphic_group;
int properties;
int color;
int weight;
int style;
int attr_bytes;
IntPtr attr_data;
int raw_bytes;
IntPtr raw_data;
}
Обратите внимание, в частности
char*
поля распределяются как IntPtr
s — попытка упорядочить указатели на массивы как массивы не будет работать по умолчанию, так как массивы больше указателей, в результате чего маршаллер попытается упорядочить больше памяти, чем доступно.Также я заметил, что ваши объявления метода P / Invoke неверны. DGNOpen
Функция возвращает саму структуру (не указатель) и поэтому должна выглядеть следующим образом.
public static extern DGNElemCore DGNOpen(string fileName, int bUpdate);
DGNReadElement
функция принимает структуру (не указатель) и возвращает указатель на эту структуру (не структуру) и поэтому должна выглядеть примерно так
public static extern IntPtr DGNReadElement(DGNHandle handle);
Атрибуты могут использоваться для изменения способа работы маршаллера, который, в свою очередь, может использоваться для изменения сигнатуры этих методов, однако, если вы делаете это, вы должны быть осторожны, чтобы гарантировать, что маршаллинг все еще будет соответствовать вашей функции C ++ деклараций.
Проблема в том, что заголовки #include могут содержать объявления, которые могут быть неправильно интерпретированы компиляторами C ++ / CLI. Например, объявления функций Си. Лучше всего явно сообщить об этом компилятору.
#pragma managed(push, off)
#include "c_include.h"#pragma managed(pop)
Затем вы можете использовать библиотеки C ++ из приложения C ++ / CLI, как вы делаете это с приложениями C ++. Единственное, что я всегда пытаюсь сделать, — это обернуть третью библиотеку за шаблон проектирования Proxy или Facade, чтобы клиент всегда работал с управляемыми классами. Это особенно важно, если ваше приложение C ++ / CLI представляет собой библиотеку, используемую другими приложениями .NET.
Более того, обычно публичные API-классы (или функции) вашей DLL должны быть представлены с использованием следующей конструкции:
#ifdef YOUR_DLL_EXPORTS
#define YOUR_API __declspec(dllexport)
#else
#define YOUR_API __declspec(dllimport)
#endif
class YOUR_API ClassToExpose {};
Надеюсь это поможет