P/Invoke PostMessage 在 window 收到消息后崩溃
P/Invoke PostMessage crashes once the window received message
我正在 .NET 核心上的 C# 控制台应用程序中通过 P/Invoke Win32 API 创建一个 window。以下是核心代码。
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(...)
// ...
}
}
但是当我将鼠标移到 window 上时,程序一直崩溃。
The program '[18996] dotnet.exe' has exited with code -1073740771 (0xc000041d).
最后我发现崩溃发生在调用PeekMessage 时。但是我说不出为什么。
经过3个小时的查找调试,终于找到原因了。
WinProc 委托实例被垃圾收集。然后本机代码将访问无效的函数指针。
我是说这个 wndclass.lpfnWndProc = WindowProc;
。 wndclass 是一个临时对象——确切地说是一个结构实例——当程序从 CreateWindow
开始 returns 时,它不会存在于堆栈中。之后由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 上的一些类似问题:
我正在 .NET 核心上的 C# 控制台应用程序中通过 P/Invoke Win32 API 创建一个 window。以下是核心代码。
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(...)
// ...
}
}
但是当我将鼠标移到 window 上时,程序一直崩溃。
The program '[18996] dotnet.exe' has exited with code -1073740771 (0xc000041d).
最后我发现崩溃发生在调用PeekMessage 时。但是我说不出为什么。
经过3个小时的查找调试,终于找到原因了。
WinProc 委托实例被垃圾收集。然后本机代码将访问无效的函数指针。
我是说这个 wndclass.lpfnWndProc = WindowProc;
。 wndclass 是一个临时对象——确切地说是一个结构实例——当程序从 CreateWindow
开始 returns 时,它不会存在于堆栈中。之后由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 上的一些类似问题: