C# 调用C代码时pinvoke给出AccessViolationException
我的一位同事用C语言制作了一个图书馆,我想从C#开始称之为图书馆。我认为这几乎是对的,但是当我调用第二个方法/函数时,它会得到一个AccessViolationException。我试过几种不同的方法:C# 调用C代码时pinvoke给出AccessViolationException,c#,c,pinvoke,C#,C,Pinvoke,我的一位同事用C语言制作了一个图书馆,我想从C#开始称之为图书馆。我认为这几乎是对的,但是当我调用第二个方法/函数时,它会得到一个AccessViolationException。我试过几种不同的方法: 类而不是结构 将[Marshallas(UnmanagedType.FunctionPtr)]添加到回调/委托(这实际上会在第一次调用而不是第二次调用时引发异常) 正在删除struct中字符串的[Marshallas(UnmanagedType.LPStr)](这将在右大括号上的第二个方法之后引
在C dll中,您需要使用
dllexport
而不是dllimport
#define HELPERDLL_API __declspec(dllexport)
在C#代码导入函数中
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
在C dll中,您需要使用
dllexport
而不是dllimport
#define HELPERDLL_API __declspec(dllexport)
在C#代码导入函数中
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
这些函数在非托管端都是
void
,在托管端用int
返回类型声明。这种不匹配必须纠正
回调函数指针使用cdecl
调用约定。从托管代码传递的代理使用stdcall
调用约定
通过如下方式标记学员来处理此问题:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SaveCallBack();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ExitCallBack();
初始化
可以更简单地声明为:
public static extern void Initialise(ref HelperAttributes attributes);
除此之外,我们必须猜测我们看不到的代码。例如,
初始化
复制传递的结构,还是记住结构的地址
如果是后者,那么这就是一个致命的问题。解决方案是修复非托管代码,使其不记住地址,而是复制内容。即使它复制了结构,它是做深度复制,还是复制了指向字符数组的指针?要弄清这一点,需要看到问题中不存在的代码
而且,正如Hans所解释的,即使函数正确地复制了结构,您也需要保持委托的活动性 函数在非托管端都是
void
,在托管端用int
返回类型声明。这种不匹配必须纠正
HelperAttributes attributes = new HelperAttributes();
回调函数指针使用cdecl
调用约定。从托管代码传递的代理使用stdcall
调用约定
通过如下方式标记学员来处理此问题:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SaveCallBack();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ExitCallBack();
初始化
可以更简单地声明为:
public static extern void Initialise(ref HelperAttributes attributes);
除此之外,我们必须猜测我们看不到的代码。例如,
初始化
复制传递的结构,还是记住结构的地址
如果是后者,那么这就是一个致命的问题。解决方案是修复非托管代码,使其不记住地址,而是复制内容。即使它复制了结构,它是做深度复制,还是复制了指向字符数组的指针?要弄清这一点,需要看到问题中不存在的代码
而且,正如Hans所解释的,即使函数正确地复制了结构,您也需要保持委托的活动性
HelperAttributes attributes = new HelperAttributes();
这非常非常麻烦。您在这段代码中有明确的内存管理问题。这里分配的结构的生命周期非常有限。它仅在Click事件处理程序方法的持续时间内有效,最多为纳秒。这不仅仅是一种方式:
- C代码不能存储传递的指针,它必须复制结构。它可能不会这样做。现在您有了一个“悬空指针”,一个非常臭名昭著的C bug。随后,取消对指针的引用会产生任意垃圾
- 代码创建并分配给结构的saveCallback和exitCallback成员的委托对象无法生存。垃圾收集器无法发现C代码要求它们保持活动状态。因此,下一次垃圾收集会销毁对象,当C代码进行回调时,会发出很大的声音。还请注意,如果它们实际使用参数,则必须使用[UnmanagedFunctionPointer]声明它们以使其成为Cdecl
- C代码不能存储传递的指针,它必须复制结构。它可能不会这样做。现在您有了一个“悬空指针”,一个非常臭名昭著的C bug。随后,取消对指针的引用会产生任意垃圾
- 代码创建并分配给结构的saveCallback和exitCallback成员的委托对象无法生存。垃圾收集器无法发现C代码要求它们保持活动状态。因此,下一次垃圾收集会销毁对象,当C代码进行回调时,会发出很大的声音。还请注意,如果它们实际使用参数,则必须使用[UnmanagedFunctionPointer]声明它们以使其成为Cdecl