症狀描述如下:
如果將一個委托作為函數指針從托管代碼封送到非托管代碼,並且在對該委托進行垃圾回收后對該函數指針發出了一個回調,則將激活 callbackOnCollectedDelegate 托管調試助手 (MDA)。
原因描述如下:
從其創建函數指針並將創建的函數指針公開給非托管代碼的委托已被垃圾回收。當非托管組件嘗試對該函數指針發出調用時,會產生訪問沖突。
一旦將委托作為非托管函數指針封送出去,垃圾回收器就無法跟蹤其生存期。這樣,在該非托管函數指針的生存內,您的代碼必須保持一個指向該委托的引用。
解決辦法如下:
一旦將委托作為非托管函數指針封送出去,垃圾回收器就無法跟蹤其生存期。這樣,在該非托管函數指針的生存內,您的代碼必須保持一個指向該委托的引用。但是在此之前,您首先必須確定回收了哪個委托。激活 MDA 之后,MDA 會提供該委托的類型名稱。請使用此名稱在您的代碼中搜索將該委托外傳給非托管代碼的平台調用或 COM 簽名。通過這些調用站點之一將有問題的委托傳遞出去。您還可以啟用 gcUnmanagedToManaged MDA 以強制在每次向運行庫發出回調之前都進行垃圾回收。這樣可以確保在回調之前總是進行垃圾回,從而可以消除由垃圾回收引起的不確定性。一旦您得知回收了哪個委托,請更改您的代碼,以便在封送的非托管函數指針的生存期內在托管端保持對該委托的引用。
這么大一塊文字讓人很暈. 簡單來說: 這是一個由於CLR垃圾回收引起的問題. 由於將委托作為非托管
函數指針封送給非委托代碼. 垃圾回收器就無法跟蹤其生命周期. 解決辦法就是, 將被過早垃圾回收的
委托置於整個對象的生存周期內,就是是把委托賦值給類的成員
例如:
原來的委托:
public delegate int HookProc(int Code, Int32 wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
調用SetWindowsHookEx:
SetWindowsHookEx(13,New HookProc(xxx) , Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
運行幾次后就會出現錯誤,HookProc被回收了,
解決辦法:
public class test
{
..........
private static HookProc hookproc;
..........
hookproc=new HookProc(xxx);
SetWindowsHookEx(13,hookproc , Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]), 0);
}
這樣就不會拋出異常。。。