MFC DLL PreTranslateMessage 导致的快捷键不响应的问题?

时间:2021-06-26 16:53:56

解决办法:

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为什么要线程独立,模块独立的原因了。一旦多线程搞进来,很多东西就不能掌控了。