准备:
模块状态:Module State, MFC对应类型AFX_MODULE_STATE
线程状态:Thread State, MFC对应类型AFX_THREAD_STATE
模块线程状态:Module Thread State, MFC对应类型AFX_MODULE_THREAD_STATE
模块概念:
MFC Regular DLL -> Module
MFC EXE -> Module
MFC Extension DLL -> None (所属的Module为CDynamicLibrary所挂载的Module,可以是Regular Dll或MFC EXE)
一个Module State含有多个Thread State。每个Thread State以TLS(Thread Local Storage)的方式存储
MFC对象如CWnd, CGdiObject等绑定到了一个特定Module下的特定Thread.
因此不同Module或不同Thread的情况下,MFC对象不能互访.
Tooltip注意1:
CWnd::EnableTooltip方法依赖于Thread State存储的内部的CTooltipCtrl控件来实现。MFC在内部实现时,首先从消息队列取出消息,再调用AfxPreTranslateMessage函数,AfxPreTranslateMessage最后会把该消息传递给从子控件到父控件的所有CWnd类的PreTranslateMessage方法中。在这个树级调用的过程中,是直接从当前Thread State中来查找的CWnd对象。在这种情况下,如果带有Tooltip的CWnd并没有绑定到这个Thread State时,会出现问题。比如下面这个例子:
CStatic开启了Tooltip,但是CStatic在一个Regular Dll中实现。另一个MFC EXE主程序调用了这个CStatic,此时这个CStatic并未在EXE程序的Thread State中,最终会导致Tooltip功能不起作用。
解决方法: 在Regular DLL中导出一个函数,函数实现时先切换到当前模块,再调用AfxPreTranslateMessage函数手工分发消息。而在MFC EXE中的CWnd或者CWinThread类的重载函数PreTranslateMessage中需先调用Regular DLL的这个导出函数。
Tooltip注意2:
CWnd::EnableTooltip方法依赖于Thread State存储的内部的CTooltipCtrl控件来实现,当Module卸载时,会依次删除所有存在的Thread State(保存在内部的一个链表),而在删除Thread State时,会调用CTooltipCtrl::DestroyTooltipCtrl方法来清除CTooltipCtrl控件。这个流程会在一定情况下出错:启用了Tooltip的控件所在的线程与执行Module卸载的线程不相同。这是因为,DestroyTooltipCtrl会最终调用DestroyWindow来从Thread State中清除CWnd。如果他们的线程不同,那么DestroyWindow就会找不到这个CWnd,最终引发出错。
解决方法:避免在其它线程中启用Tooltip.