![](/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.