簡體   English   中英

Interop C#/ C問題:AccessViolationException

[英]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。 “

有兩種可能的解決方案:

  1. 將方法的返回類型聲明為IntPtr並使用Marshal.ReadInt32讀取結構的字段,或使用Marshal.PtrToStructure將數據復制到托管結構,或使用不安全的代碼將IntPtr值轉換為Point * C庫需要公開一個釋放內存的destroyPoint(Point *)方法。
  2. 將C方法簽名更改為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.

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