![](/img/trans.png)
[英]AccessViolationException when calling C function from dll on C#
[英]Creating C# to C++ bridge: Why do I get a AccessViolationException when calling DLL?
我正在探索在第3方应用程序的DLL插件和C#应用程序之间建立桥梁的想法。 我正在编写插件DLL和C#应用程序。 该插件将被加载到第三方应用程序中,然后我想使用从C#调用该插件来间接地从第三方应用程序获取数据。
我能够从C#的DLL成功调用导出的函数。 例如:
C ++ DLL:
extern "C" __declspec(dllexport) char * HelloFromDll()
{
char *result;
result = "Hello from my DLL";
return result;
}
C#:
using System.Runtime.InteropServices;
[DllImport(@"MyDll.dll")]
private static extern string HelloFromDll();
然后,我可以从C#调用此DLL函数,并在UI中显示字符串。 但是,一旦我创建了一个从我的第三方应用程序调用一个函数的导出函数,我就会得到一个AccessViolationException。 例如,
extern "C" __declspec(dllexport) char * GetData()
{
char *result;
result = 3rdPartyLibrary::SomeFunction();
return result;
}
通过一些测试,该错误似乎在我调用第3方函数后立即发生。 我怎样才能解决这个问题?
同样,在C程序中也很难使用此功能。 从函数返回字符串的情况很少得到支持。 存在内存管理问题,尚不清楚谁拥有该字符串。 在大多数情况下,要求调用者获得字符串的所有权并在使用后释放它。 这对于您的函数来说效果不佳,因为返回了字符串文字,程序将崩溃。
.NET pinvoke编组器也需要解决此问题。 额外的问题是它不能使用C代码使用的分配器。 它要调用CoTaskMemFree(COM分配器)。 这将导致XP上无法诊断的内存泄漏,Vista和Win7崩溃。
只是不要写这样的C代码。 始终让调用者传递字符串的缓冲区。 现在,您无法猜测谁拥有内存。 像这样:
extern "C" __declspec(dllexport) void HelloFromDll(char* buffer, int bufferSize)
{
strcpy_s(result, bufferSize, "Hello from my DLL");
}
使用这样的C#代码:
[DllImport("foo.dll", CharSet = CharSet.Ansi)]
private static extern void HelloFromDll(StringBuilder buffer, int bufferSize);
...
var sb = new StringBuilder(666);
HelloFromDll(sb, sb.Capacity);
string result = sb.ToString();
从您的问题看来,这是场景:
ProcessA(第三方应用程序)->加载X.DLL->初始化插件->做其他事情。
ProcessB(您的C#应用)->加载X.DLL->调用GetData();
ProcessA中加载的X.DLL是否具有与ProcessB中加载的X.DLL进行通信的机制?
如果没有,那么这种方法是有缺陷的。 您的代码很可能崩溃,因为尚未在您的C#应用程序中初始化“ 3rdPartyLibrary”类,因为它是DLL的完全不同的副本。
为了提取这些数据,您需要一个由X.DLL定义的查询接口,该接口可以跨进程(可能是套接字)进行通信。
然后,ProcessB与该接口进行对话并提取数据。 如果使用套接字,则您的X.DLL将同时实现服务器代码和客户端代码,而您的GetData()将使用此机制(可能是套接字)并查询数据并返回它。
因此:ProcessA中的X.DLL应该像服务器一样。 并且:ProcessB中的X.DLL(或编写Y.DLL)应像客户端一样,并从ProcessA获取此信息。
顺便说一句,如果只需要执行一次查询,只需将硬代码存储在X.DLL中并转储到磁盘,然后方便地进行探索:-)
通常,返回的char *需要作为IntPtr返回:
[DllImport(@"MyDll.dll")]
private static IntPtr HelloFromDll();
然后,您需要将该IntPtr转换为字符串:
string retVal=Marshal.PtrToStringAnsi(HelloFromDll());
在P / Invoke中,字符串有点困难。 我的一般经验法则是:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.