一旦窗口接收到消息,P/Invoke PostMessage就崩溃

本文关键字:Invoke PostMessage 崩溃 消息 窗口 | 更新日期: 2023-09-27 18:15:39

我在。net核心的c#控制台应用程序中通过p/Invoke Win32 API创建了一个窗口。以下是核心代码:

class WindowContext
{
    public IWindow MainLoop(Action guiMethod)// this is called somewhere else
    {
        MSG msg = new MSG();
        while (msg.message != 0x12/*WM_QUIT*/)
        {
            if (PeekMessage(ref msg, IntPtr.Zero, 0, 0, 0x0001/*PM_REMOVE*/))
            {
                TranslateMessage(ref msg);
                DispatchMessage(ref msg);
            }
        }
    }
    private IntPtr WindowProc(IntPtr hWnd, uint msg, UIntPtr wParam, IntPtr lParam)
    {
        //....
    }
    public IWindow CreateWindow(Point position, Size size)// this is called to create a window
    {
        IntPtr hInstance = processHandle.DangerousGetHandle();
        string szAppName = "ImGuiApplication~";
        WNDCLASS wndclass;
        wndclass.style = 0x0002 /*CS_HREDRAW*/ | 0x0001/*CS_VREDRAW*/;
        wndclass.lpfnWndProc = WindowProc;
        // RegisterClass(ref wndclass);
        // CreateWindowEx(...)
        // ...
    }
}

但是当我把鼠标移到窗口上时,程序总是崩溃。

程序'[18996]dotnet.exe'已退出,代码为-1073740771 (0xc000041d).

最后我发现崩溃发生在PeekMessage被调用的时候。但我不知道为什么。

一旦窗口接收到消息,P/Invoke PostMessage就崩溃

经过3个小时的搜索和调试,终于找到了原因。

WinProc委托实例被垃圾收集。那么本机代码将访问一个无效的函数指针。

我指的是这个wndclass.lpfnWndProc = WindowProc;。windclass是一个临时对象——确切地说是一个结构体实例——当程序从CreateWindow返回时,它将不存在于堆栈上。之后,由CLR决定是否GC wndclass.lpfnWndProc

因此,解决方案是使wndclass不是一个临时对象。例如,

class WindowContext
{
    WNDCLASS wndclass;
    public IWindow CreateWindow(Point position, Size size)// this is called to create a window
    {
        IntPtr hInstance = processHandle.DangerousGetHandle();
        string szAppName = "ImGuiApplication~";
        wndclass.style = 0x0002 /*CS_HREDRAW*/ | 0x0001/*CS_VREDRAW*/;
        wndclass.lpfnWndProc = WindowProc;
    }
}

现在,windclass与WindowContext实例存在的时间一样长。问题解决了。

SO:

上的一些类似问题https://stackoverflow.com/a/5007211/3427520

https://stackoverflow.com/a/1616718/3427520