迁移代码至C#/Win32引发执行引擎异常求助
WinUI3迁移C#/Win32包后窗口子类化执行引擎异常排查与解决
问题背景
WinUI3项目原通过旧P/Invoke包调用底层窗口修改方法,因该包已被弃用,迁移至源代码生成的C#/Win32包后,Win32窗口子类化功能出现执行引擎异常。调试发现NewWindowProc能被触发,但后续执行报错。
原工作代码
private delegate IntPtr WinProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam); private IntPtr _oldWndProc = IntPtr.Zero; [DllImport("user32.dll", EntryPoint = "SetWindowLong")] private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc); [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr")] private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc); // This static method is required because Win32 does not support GetWindowLongPtr directly private static IntPtr SetWindowLongPtr(IntPtr hWnd, PInvoke.User32.WindowLongIndexFlags nIndex, WinProc newProc) { if (IntPtr.Size == 8) return SetWindowLongPtr64(hWnd, nIndex, newProc); else return SetWindowLongPtr32(hWnd, nIndex, newProc); } [DllImport("user32.dll")] static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam); private void SubClassingWin32() { _hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this); var newWndProc = new WinProc(NewWindowProc); _oldWndProc = SetWindowLongPtr(_hwnd, PInvoke.User32.WindowLongIndexFlags.GWL_WNDPROC, newWndProc); } private IntPtr NewWindowProc(IntPtr hWnd, PInvoke.User32.WindowMessage Msg, IntPtr wParam, IntPtr lParam) { switch (Msg) { [...] } return CallWindowProc(_oldWndProc, hWnd, Msg, wParam, lParam); }
重写后问题代码
private delegate LRESULT WNDPROCDelegate(HWND hWnd, uint Msg, WPARAM wParam, LPARAM lParam); // This static method is required because Win32 does not support GetWindowLongPtr directly private static IntPtr SetWindowLongPtr(HWND hWnd, WindowLongIndexFlags nIndex, IntPtr dwNewLong) => #if x64 PInvoke.SetWindowLongPtr(hWnd, nIndex, dwNewLong); #else PInvoke.SetWindowLong(hWnd, nIndex, dwNewLong.ToInt32()); #endif private void SubClassingWin32() { _hwnd = (HWND)WinRT.Interop.WindowNative.GetWindowHandle(this); WNDPROCDelegate del = NewWindowProc; var newWndProc = Marshal.GetFunctionPointerForDelegate(del); var oldWndProc = SetWindowLongPtr(_hwnd, WindowLongIndexFlags.GWL_WNDPROC, newWndProc); _oldWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(oldWndProc); } private LRESULT NewWindowProc(HWND hWnd, uint Msg, WPARAM wParam, LPARAM lParam) { switch (Msg) { [...] } return PInvoke.CallWindowProc(_oldWndProc, hWnd, Msg, wParam, lParam); }
怀疑的问题点
_oldWndProc = Marshal.GetDelegateForFunctionPointer<WNDPROC>(oldWndProc);return PInvoke.CallWindowProc(_oldWndProc, hWnd, Msg, wParam, lParam);
排查思路与解决方法
1. 委托被GC回收(核心问题)
重写代码中WNDPROCDelegate del是局部变量,SubClassingWin32方法执行完毕后,该委托会被GC标记为可回收。当Win32回调NewWindowProc时,委托已被销毁,直接触发执行引擎异常。
解决:
将新窗口过程委托声明为类成员变量,保留强引用避免GC回收:
// 类成员变量,保留委托引用 private WNDPROC _newWndProc; private IntPtr _oldWndProc = IntPtr.Zero; private HWND _hwnd; private void SubClassingWin32() { _hwnd = (HWND)WinRT.Interop.WindowNative.GetWindowHandle(this); // 赋值给成员变量,避免GC回收 _newWndProc = NewWindowProc; var newWndProcPtr = Marshal.GetFunctionPointerForDelegate(_newWndProc); // 直接调用C#/Win32的SetWindowLongPtr,无需自行封装 _oldWndProc = PInvoke.SetWindowLongPtr(_hwnd, WindowLongIndexFlags.GWL_WNDPROC, newWndProcPtr); }
2. 委托类型不匹配
自行定义的WNDPROCDelegate与C#/Win32生成的WNDPROC委托可能存在隐式不兼容,且转换旧窗口过程指针为委托时类型不匹配,导致调用CallWindowProc时出错。
解决:
直接使用C#/Win32生成的WNDPROC委托,无需自行定义;同时保留旧窗口过程的IntPtr指针,避免委托转换:
private LRESULT NewWindowProc(HWND hWnd, uint Msg, WPARAM wParam, LPARAM lParam) { switch (Msg) { // 你的消息处理逻辑 } // 直接传入旧窗口过程指针调用CallWindowProc return PInvoke.CallWindowProc(_oldWndProc, hWnd, Msg, wParam, lParam); }
3. 无需自行封装SetWindowLongPtr
C#/Win32已自动处理32/64位兼容性,无需手动编写条件编译的封装方法,直接调用PInvoke.SetWindowLongPtr即可。
调试建议
- 启用非托管代码调试:在项目属性的“调试”选项卡中勾选“启用非托管代码调试”,可跟踪Win32层面的错误调用栈。
- 验证GC回收问题:在
SubClassingWin32方法末尾添加GC.KeepAlive(del),若异常消失,则确认是委托被GC回收导致的问题。 - 分步断点调试:在
NewWindowProc开头添加Debugger.Break(),逐步执行到CallWindowProc,定位具体报错的代码行。 - 检查参数类型匹配:对比C#/Win32生成的
WNDPROC定义,确保NewWindowProc的参数类型、返回值完全一致。
内容的提问来源于stack exchange,提问作者JHBonarius
相关产品推荐
相关产品推荐

