解决办法:
http://blog.sina.com.cn/s/blog_53d9f7e901000aef.html
http://zhidao.baidu.com/link?url=wl3LlUGz_oCQplgHV6vyf-c0dOsBW4xNa68dQJJL_KH1WcjaETEdTvPRlf3ZgdXQ3vKpKRKmHYYOL02mW2vDGtbVrc-4pJuvPXDB_tnJSKu
上面贴出了方法,自己也验证了。
但这篇文章说不解决的问题的方法,而是说说MFC 为什么要用PreTranslateMessage原因!
故事开始:最近在视频播放器的界面,界面用DUILIB开发,界面全部封装在DLL里面,发现不能过滤快捷键,当时也没有想为什么?,那问题来了,窗口消息只会发送对应的窗口处理函数里面去,那我怎么在一个窗口统一处理呢?这个时候我想到用键盘钩子去处理。直接用HOOK 键盘钩子获取按键消息,比喻回车全屏,空格暂定。后面想想其实想想 PreTranslateMessage其实也是做同样事情,你是否有感觉了,你仔细想你就会发现为什么MFC 里面有一个afxMapHWND 这个东西了,因为保存整个 window hwnd的列表。
因为HWND 只是窗口句柄,但并没用,我们处理所有事情都是在窗口类中处理,所以我们通过afxMapHWND 获取对应的实体类。
说了这个么多,我直接用代码说事情,
我们window核心 消息循环来说:
BOOL AFXAPI AfxInternalPumpMessage() { _AFX_THREAD_STATE *pState = AfxGetThreadState(); if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)) { #ifdef _DEBUG TRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT.\n"); pState->m_nDisablePumpCount++; // application must die #endif // Note: prevents calling message loop things in 'ExitInstance' // will never be decremented return FALSE; } #ifdef _DEBUG if (pState->m_nDisablePumpCount != 0) { TRACE(traceAppMsg, 0, "Error: CWinThread::PumpMessage called when not permitted.\n"); ASSERT(FALSE); } #endif #ifdef _DEBUG _AfxTraceMsg(_T("PumpMessage"), &(pState->m_msgCur)); #endif // process this message if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))) { ::TranslateMessage(&(pState->m_msgCur)); ::DispatchMessage(&(pState->m_msgCur)); } return TRUE; }
这个代码和我们常写的win32代码和类似吧。
AfxPreTranslateMessage(&(pState->m_msgCur)
BOOL AfxInternalPreTranslateMessage(MSG* pMsg) { // ASSERT_VALID(this); CWinThread *pThread = AfxGetThread(); if( pThread ) { // if this is a thread-message, short-circuit this function if (pMsg->hwnd == NULL && pThread->DispatchThreadMessageEx(pMsg)) return TRUE; } // walk from target to main window CWnd* pMainWnd = AfxGetMainWnd(); if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg)) return TRUE; // in case of modeless dialogs, last chance route through main // window's accelerator table if (pMainWnd != NULL) { CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd); if (pWnd->GetTopLevelParent() != pMainWnd) return pMainWnd->PreTranslateMessage(pMsg); } return FALSE; // no special processing }
BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg) { ASSERT(hWndStop == NULL || ::IsWindow(hWndStop)); ASSERT(pMsg != NULL); // walk from the target window up to the hWndStop window checking // if any window wants to translate this message for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) { CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); if (pWnd != NULL) { // target window is a C++ window if (pWnd->PreTranslateMessage(pMsg)) return TRUE; // trapped by target window (eg: accelerators) } // got to hWndStop window without interest if (hWnd == hWndStop) break; } return FALSE; // no special processing }
CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg)从 pMsg->hwnd
处理这个消息,如果自己不处理,就要爸爸窗口处理,爸爸窗口不处理,就要爷爷窗口处理,如果都没有返回FALSE,另外的代码块处理。
如果窗口类实在exe里面这里就会正常处理了。但在DLL 里面
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); 代码就会问题了,因为MFC是模块分开的不同的。比喻DLL1 是模块1 DLL2模块2 exe也是另一个模块。每个模块都有自己的窗口类列表。。。所以我在exe不能遍历到你的dll里面的窗口的。所以才不能运行。
</pre><p></p><p></p><p>上面的<pre name="code" class="cpp">AfxInternalPreTranslateMessage
// in case of modeless dialogs, last chance route through main // window's accelerator table if (pMainWnd != NULL) { CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd); if (pWnd->GetTopLevelParent() != pMainWnd) return pMainWnd->PreTranslateMessage(pMsg); }如果上面还是没有处理,检测这个窗口不是WS_CHILD窗口的话就找他的归属的窗口去处理。
如果还没有归属者就找到最顶层窗口处理,反正就MFC就尽量找一个合适窗口来处理,总会找到一个合适来处理。
看代码还是看框架代码,一些代码细节代码看的真累,我现在看chromuni代码真新感觉有点累。。还是找自己感兴趣的模块看吧。
其实上面书了那么多,总结是 MFC 每一个模块保存窗口列表模块,每个模块独立。
______________________________________________________________________________________________
如果是我纯win32 去写这样的PreTranslateMessage 方式函数会怎么做呢? 我不用列表,在HWND 创建绑定一个数据结构,如果是用c++ 绑定 一个窗口c++ 类,如果c就绑定一个c的指针,这样就不会出现上面的模块的问题,但要考虑线程竞争的问题。所以MFC为什么要线程独立,模块独立的原因了。一旦多线程搞进来,很多东西就不能掌控了。