我读到过在WinAPI中只有UI线程才应该允许操作UI元素。但我认为,不是UI线程的线程根本无法操作UI元素。
我认为这是因为如果一个非UI线程的线程调用SendMessage()函数来操作某个UI元素,那么会向UI线程发送一条消息,然后是UI线程来操作UI元素而不是其他线程。
我的理解正确吗?
我读到过在WinAPI中只有UI线程才应该允许操作UI元素。但我认为,不是UI线程的线程根本无法操作UI元素。
我认为这是因为如果一个非UI线程的线程调用SendMessage()函数来操作某个UI元素,那么会向UI线程发送一条消息,然后是UI线程来操作UI元素而不是其他线程。
我的理解正确吗?
AttachThreadInput
指定不同的线程处理输入。SendMessage()
会切换到所属线程。但这并不意味着从多个线程调用SendMessage()
是安全或推荐的方法。相反,这样做充满了危险,必须小心地同步线程。WndProc()
看起来像这样:LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
switch (message)
{
case WM_MYMSG1:
...
SendMessage(hWnd, WM_MYMSG2, wParam, lParam);
...
break;
...
}
...
}
WndProc()
,使其可以从多个线程访问,您必须确保使用可重入锁,而不是信号量。
其次,如果您使用可重入锁,必须确保仅在WndProc()
内部使用它,甚至将其限定于特定消息。否则很容易陷入死锁:
//Worker thread:
void foo ()
{
EnterCriticalSection(&g_cs);
SendMessage(hWnd, WM_MYMSG1, NULL, NULL);
LeaveCriticalSection(&g_cs);
}
//Owner thread:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_MYMSG1:
{
EnterCriticalSection(&g_cs); //Deadlock!
...
LeaveCriticalSection(&g_cs);
}
break;
}
}
WndProc()
内不调用任何控制器函数;其中包括但不限于:DialogBox()
、MessageBox()
和GetMessage()
。否则会导致死锁。LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
switch (message)
{
case WM_MYMSG1:
...
SendMessage(hWnd2, WM_MYMSG1, wParam, lParam); //Deadlock!
...
break;
...
}
...
}
WndProc()
可能会导致可怕的竞态条件错误。//in UI thread:
wpOld = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
//in another thread:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)otherWndProc);
//back in UI thread:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)newWndProc);
//still in UI thread:
LRESULT CALLBACK newWndProc(...)
{
CallWindowProc(wpOld, ...); //Wrong wpOld!
}
HWND
的线程才能接收和分派该HWND
的消息,因此所有在HWND
上执行的实际工作都由拥有它的线程完成。但是其他线程肯定可以向HWND
发送消息,并且它们将由拥有它的线程分派和处理。这在SendMessage()
文档中已经明确说明:... - Remy LebeauSendMessage()
函数来实现,但是SendMessage()
函数并不是用来操作UI控件的(即它不会在内存中操作表示UI控件的数据结构),而是向UI线程发送消息... - Paul Morris