![](/img/trans.png)
[英]GraphViz C# interop resulting in AccessViolationException occasionally
[英]Problem with Interop C#/C: AccessViolationException
並感謝任何幫助的建議。
我在C中有這個簡單的功能:
__declspec(dllexport) Point* createPoint (int x, int y) {
Point *p;
p = (Point*) malloc(sizeof(Point));
p->x = x;
p->y=y;
return p;
}
Point是一個非常簡單的結構,有兩個int字段,x和y。
我想從C#調用這個函數。
我用這個代碼:
[DllImport("simpleC.dll", EntryPoint = "createPoint", CallingConvention = CallingConvention.Cdecl, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.LPStruct)]
public static extern Point createPoint(int x, int y);
Point p = Wrapper.createPoint(1, 2);
但是在運行時我有一個AccessViolationException
。 詳細觀察異常,我發現從Marshal.CoTaskMemFree(IntPtr)
方法拋出異常。
看來這個方法無法釋放C malloc分配的內存。
我究竟做錯了什么?
真的感謝。
CoTaskMemFree
不能用於釋放malloc
分配的malloc
(因為它們使用不同的分配器)。 根據MSDN ,“運行時始終使用CoTaskMemFree方法釋放內存。如果您使用的內存未使用CoTaskMemAlloc方法分配,則必須使用IntPtr並使用適當的方法手動釋放內存。”
此外, Adam Nathan指出 “UnmanagedType.LPStruct僅支持一種特定情況:將System.Guid值類型視為具有額外級別間接的非托管GUID。...您應該遠離UnmanagedType.LPStruct。 “
有兩種可能的解決方案:
IntPtr
並使用Marshal.ReadInt32讀取結構的字段,或使用Marshal.PtrToStructure將數據復制到托管結構,或使用不安全的代碼將IntPtr
值轉換為Point *
。 C庫需要公開一個釋放內存的destroyPoint(Point *)
方法。 void getPoint(int x, int y, Point *)
。 這允許C#分配結構,而C方法只是填充數據值。 (大多數Win32 API都是以這種方式定義的)。 最后一點:除非您的方法使用SetLastError Win32 API,否則您不需要在P / Invoke屬性上指定SetLastError = true
。
由於您沒有釋放“p”的代碼,因此很難說。 但是,malloc()和free()一起工作的方式可能與C#管理內存的方式完全不同。 由於C#有垃圾收集(我相信),它很可能使用完全不同的內存管理系統。
在任何情況下,正確的解決方案是,如果您使用庫創建對象,您還應該使用它來銷毀它。 實現一個“destroyPoint”函數,該函數釋放C庫中的內存,將其導入C#代碼,並從那里調用它來銷毀C庫創建的對象。
作為一般設計/編碼規則,每個“創建”函數都應具有匹配的“自由/銷毀/刪除”功能。 除此之外,它可以輕松確保所有創建的項目得到正確銷毀。
如何在C#端定義Point類型?
它必須是不安全的,或者你需要返回一個void指針(IntPtr)。 GC無法計算來自外部的引用(這里是已分配的內存),因此您的代碼無法通過GC管理外部分配的內存。
一種替代方法是保留靜態引用以避免垃圾收集,如果您需要在應用程序的運行時期間持久保留對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.