我正在探索在第三方应用程序的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;
}
通过一些测试,一旦我调用第三方功能,似乎就会发生错误。我该如何解决这个问题?
答案 0 :(得分:3)
此功能在C程序中也很难使用。从函数返回字符串是一种支持不足的方案。存在内存管理问题,目前尚不清楚谁拥有该字符串。在大多数情况下,调用者应该获得字符串的所有权并在使用后释放它。这对你的函数来说效果不好,程序会崩溃,因为你返回了一个字符串文字。
.NET pinvoke marshaller也需要解决这个问题。由于额外的问题,它无法使用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();
答案 1 :(得分:2)
从您的问题看来,情况就是这样:
ProcessA(第三方应用) - >加载X.DLL - >初始化插件 - >做其他事情。
ProcessB(您的C#应用程序) - >加载X.DLL - >调用GetData();
ProcessA中加载的X.DLL是否有任何与ProcessB中加载的X.DLL对话的机制?
如果没有,那么这种方法是有缺陷的。您的代码可能会崩溃,因为“3rdPartyLibrary”类尚未在您的C#应用程序中初始化,因为它是完全不同的DLL副本。为了提取这些数据,您需要一个X.DLL定义的查询接口,它可以跨进程交换,也可以是套接字?
然后ProcessB与此接口对话并提取数据。如果使用套接字,则X.DLL将实现服务器和客户端代码,其中GetData()将使用此机制(可能是套接字)并查询数据并将其返回。
所以:ProcessA中的X.DLL应该像服务器一样。 并且:ProcessB中的X.DLL(或写一个Y.DLL)应该像客户端一样从ProcessA获取此信息。
顺便说一句,如果查询只需要完成一次,只需要在X.DLL中硬编码并转储到磁盘,然后以你的方便探索: - )答案 2 :(得分:1)
通常,返回的char *需要作为IntPtr返回:
[DllImport(@"MyDll.dll")]
private static IntPtr HelloFromDll();
然后,您需要将该IntPtr转换为字符串:
string retVal=Marshal.PtrToStringAnsi(HelloFromDll());
P / Invoke中的字符串有点困难。我的一般经验法则是: