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

3

我正在尝试从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个回答

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——默认情况下,尝试将指向数组的指针封送为数组是行不通的,因为数组比指针更大,这会导致封送程序尝试封送比可用内存更多的内存。

此外,我注意到您的P/Invoke方法声明是错误的。DGNOpen函数返回结构本身(而不是指针),因此应该像以下示例那样。
public static extern DGNElemCore DGNOpen(string fileName, int bUpdate);
DGNReadElement函数接受一个结构体(而不是指针),并返回该结构体的指针(而不是结构体本身),因此应该像这样:
public static extern IntPtr DGNReadElement(DGNHandle handle);

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


太好了!非常感谢Justin。在更改类中字段的顺序并使用“IntPtr”而不是“byte []”之后,没有错误了。 - taibc

2
问题在于 #include 头文件可能包含 C++/CLI 编译器会误解的声明,比如 C 函数声明。最好的方法是明确地告诉编译器。
#pragma managed(push, off)
#include "c_include.h"
#pragma managed(pop)

您可以像在C++应用程序中一样在C++/CLI应用程序中使用C++库。我总是尝试将第三方库包装在代理或外观设计模式后面,以便客户始终使用托管类。如果您的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 {};

希望这能帮到你。

谢谢。我需要在我的dll项目中的所有.h文件中添加#pragma managed(push, off) #include "c_include.h" #pragma managed(pop)吗? - taibc
@taibc:是的,你应该对所有本地 C/C++ 头文件执行此操作。 - nogard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接