我已经读过,应该只允许UI线程在WinAPI中操作UI元素。但我认为,一个不是UI线程的线程甚至不可能操纵UI元素。
我认为,因为当一个线程(不是UI线程)调用SendMessage()函数来操作某个UI元素时,会向UI线程发送一条消息,然后是UI线程操作UI元素,而不是另一个线程。
我说的对吗?
发布于 2018-07-08 03:29:54
首先,假设性地说,试图满足OP的好奇心:
AttachThreadInput输入。但是,考虑到我们坚持使用标准Windows创建和管理UI这一问题的前提,可以肯定地说,只有在创建窗口的线程中才能实现对窗口的访问,因为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;
...
}
...
}您还必须非常小心地使用Windows来隐式地管理操作系统的进程特定锁和保存和维护正确的锁层次结构。。相当多的User32函数和许多阻塞的COM调用都属于这一类。
其中一些问题可以通过使用InSendMessage()和ReplyMessage() (当使用SendMessage()时)或PostMessage()及其兄弟姐妹来缓解。但是,您会遇到各种控制流问题,因为您可能想知道消息是在继续当前线程或处理下一条消息之前处理的。因此,您最终不得不实现某种同步机制,但这将变得越来越困难,而且还需要避免许多陷阱。
问题还不止于在线程之间发送消息。将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!
}此外,不正确地使用来自多个线程的DC可以导致细微的错误。
这些原因以及其他原因(包括性能)可能导致MFC和WinForms等标准API包装器的设计者简单地假设他们的API将在单线程上下文中使用。它们不提供任何线程安全保护,这取决于用户实现这样的机制,然而更高层次的抽象使其成为更容易到忽视根本问题。当出现这样的问题时,通常的答案是:不要使用所有者线程之外的控件。
https://stackoverflow.com/questions/51092924
复制相似问题