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

时间:2016-10-29 16:58:56

标签: c# pinvoke

我正在.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时发生了崩溃。但我不明白为什么。

1 个答案:

答案 0 :(得分:1)

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

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

我的意思是wndclass.lpfnWndProc = WindowProc;。 wndclass是一个临时对象 - 一个完整的结构实例 - 当程序从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;
    }
}

现在,wndclass与WindowContext实例的长度相同。问题解决了。

SO上的一些类似问题:

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

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