簡體   English   中英

創建C#到C ++的橋梁:為什么在調用DLL時會出現AccessViolationException?

[英]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中,字符串有點困難。 我的一般經驗法則是:

  • 輸入char *參數= c#字符串
  • 返回char * = IntPtr(使用PtrToStringAnsi)
  • 輸出char *參數= c#StringBuilder-並確保在調用該函數之前將其預分配足夠大的空間(即= new StringBuilder( size ))。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM