问题描述
我正在通过非托管 CreateWindowEx
创建一个窗口,使用 PInvoke 作为服务器工作,以便从不同的进程分派 SendMessage
调用。这应该包含在一个同步函数(类注册+窗口创建)中,如下所示:
public bool Start()
{
if (!Running)
{
var processHandle = Process.GetCurrentProcess().Handle;
var windowClass = new WndClassEx
{
lpszMenuName = null,hInstance = processHandle,cbSize = WndClassEx.Size,lpfnWndProc = WndProc,lpszClassName = Guid.NewGuid().ToString()
};
// Register the dummy window class
var classAtom = RegisterClassEx(ref windowClass);
// Check whether the class was registered successfully
if (classAtom != 0u)
{
// Create the dummy window
Handle = CreateWindowEx(0x08000000,classAtom,"",-1,IntPtr.Zero,processHandle,IntPtr.Zero);
Running = Handle != IntPtr.Zero;
// If window has been created
if (Running)
{
// Launch the message loop thread
taskFactory.StartNew(() =>
{
Message message;
while (GetMessage(out message,0) != 0)
{
TranslateMessage(ref message);
dispatchMessage(ref message);
}
});
}
}
}
return Running;
}
但是,MSDN 声明 GetMessage
retrieves a message from the calling thread's message queue,因此这是不可能的,因为它包含在不同的线程/任务中。我不能简单地将 CreateWindowEx
函数调用移到 taskFactory.StartNew()
范围内。
关于如何实现这一目标的任何想法?也许从 GetMessage
更改为 PeekMessage
(不过,第二个可能会使用大量 cpu)?
要求:
解决方法
我不能简单地将 CreateWindowEx 函数调用移动到 taskFactory.StartNew() 范围内。
抱歉,您将不得不这样做。尽管您可以将消息发送/发布到驻留在另一个线程中的窗口,但检索和分派消息不能跨线程边界工作。创建一个窗口,销毁那个窗口,并为那个窗口运行一个消息循环,都必须在同一个线程上下文中完成。
就您而言,这意味着所有逻辑都必须在您传递给 taskFactory.StartNew()
的回调中。
关于如何实现这一目标的任何想法?也许从 GetMessage 更改为 PeekMessage(不过,第二个可能会使用大量 CPU)?
那不能解决您的问题。 GetMessage()
和 PeekMessage()
都仅从调用线程的消息队列中拉取消息,并且该拉取只能返回调用线程拥有的窗口的窗口消息。这在他们的文档中明确说明:
hWnd
类型:HWND
要检索其消息的窗口的句柄。 窗口必须属于当前线程。
如果 hWnd
为 NULL,GetMessage 检索属于当前线程的任何窗口的消息,以及当前线程的消息队列中 hwnd
值为 NULL 的任何消息(参见 MSG
结构)。因此,如果 hWnd
为 NULL,则处理窗口消息和线程消息。
如果 hWnd 为 -1,GetMessage 仅检索当前线程的消息队列中 hwnd
值为 NULL 的消息,即 PostMessage
发布的线程消息(当 hWnd
参数为 NULL) 或 PostThreadMessage
。
hWnd
类型:HWND
要检索其消息的窗口的句柄。 窗口必须属于当前线程。
如果 hWnd
为 NULL,PeekMessage 检索属于当前线程的任何窗口的消息,以及当前线程的消息队列中 hwnd
值为 NULL 的任何消息(参见 MSG
结构)。因此,如果 hWnd
为 NULL,则处理窗口消息和线程消息。
如果 hWnd
为 -1,PeekMessage 仅检索当前线程消息队列中 hwnd
值为 NULL 的消息,即 PostMessage
发布的线程消息(当 { {1}} 参数为 NULL) 或 hWnd
。
PostThreadMessage
和 GetMessage()
之间的唯一区别在于:
-
PeekMessage()
在队列为空时等待消息,而GetMessage()
不会。 -
PeekMessage()
可以返回消息而不将其从队列中移除,而PeekMessage()
不能。
现在,话虽如此,请尝试以下操作。它将保持与原始代码相同的语义,即在退出之前等待创建新窗口。窗口创建只是在任务线程中执行,而不是在调用线程中执行:
GetMessage()