错误:在C#中调用C ++ DLL函数

时间:2012-10-22 07:14:14

标签: c# c++ marshalling dllexport

我正在尝试使用C#中的C ++ DLL中的函数,但是我收到了一个错误:“尝试读取或写入受保护的内存。这通常表明其他内存已损坏”

任何人都知道如何解决?

这是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**

2 个答案:

答案 0 :(得分:3)

您在C#代码中声明DGNElemCore是错误的 - 它需要与您的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;
}

特别注意

  • C#类中的成员顺序与C结构中的成员顺序相匹配(虽然这在调用函数时不会导致错误,但在访问编组结构的成员时会给出错误的值)
  • char*字段被编组为IntPtr s - 尝试编组指向数组的指针,因为数组默认不会工作,因为数组比指针大,导致编组程序试图编组更多的内存比可用。

另外我注意到你的P / Invoke方法声明是错误的。 DGNOpen函数返回结构本身(不是指针),所以应该看起来更像下面这样。

public static extern DGNElemCore DGNOpen(string fileName, int bUpdate);

DGNReadElement函数接受结构(不是指针)并返回指向该结构(不是结构)的指针,所以看起来应该更像这样

public static extern IntPtr DGNReadElement(DGNHandle handle);

属性可以用来改变编组程序的工作方式,这可以反过来用来改变这些方法的签名,但是如果你这样做,你需要小心确保编组仍然匹配到你的C ++函数声明。

答案 1 :(得分:2)

问题是#include标头可能包含可能被C ++ / CLI编译器误解的声明。例如,C函数声明。最好的办法是明确告诉编译器。

#pragma managed(push, off)
#include "c_include.h"
#pragma managed(pop)

然后,您可以像使用C ++应用程序一样在C ++ / CLI应用程序中使用C ++库。我唯一要做的就是将第三个库包装在Proxy或Facade设计模式之后,这样客户端就可以使用托管类了。如果您的C ++ / CLI应用程序是其他.NET应用程序使用的库,这一点尤其重要。

此外,通常使用以下构造公开DLL的公共API类(或函数):

#ifdef YOUR_DLL_EXPORTS
#define YOUR_API __declspec(dllexport)
#else
#define YOUR_API __declspec(dllimport)
#endif 

class YOUR_API ClassToExpose {};

希望这个帮助