问题起因
今天在做需求的时候,创建了一个全新的MFC非模态对话框,对话框中用到了edit控件,结果发现,在这个edit控件中输入任何的内容,都显示到了excel中选中的那个单元格中去了,而edit内容还是为空。因为edit也是我们重写的,只是给它加了个皮肤,功能是没有影响的,所以首先在OnChar那里打了个断点,发现并没有进来(edit控件中是有用鼠标点击获取焦点过的)。这个非模态对话框的父类就是ExcelApplication,因此分析,在excel上创建的任何非模态对话框,键盘的输入都被excel给截取了,所以导致我们的edit没有响应OnChar。
解决方案
首先想到的解决方法就是使用全局钩子,注册当前线程,当前实例的WH_GETMESSAGE消息钩子,当捕捉到消息后,再进行一个过滤验证,最后发送WM_GETCHAR消息到指定对话框中,整个设计就是一个简单的观察者模式。
附上主要代码:
钩子注册:
HHOOK m_hHook = NULL;
std::set<HWND> m_aWindows;
HRESULT InstallHook(HWND hWnd, HINSTANCE hInstance)
{
if( NULL == m_hHook )
{
m_hHook = ::SetWindowsHookEx(WH_GETMESSAGE,
GetMessageProc,
hInstance,
GetCurrentThreadId());
}
if(m_aWindows.find(hWnd) == m_aWindows.end())
{
m_aWindows.insert(hWnd);
}
return S_OK;
}
钩子回调响应:
static LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LPMSG lpMsg = (LPMSG)lParam;
if(nCode >= 0 && PM_REMOVE == wParam &&
(lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST))
{
auto it = m_aWindows.begin();
while(it != m_aWindows.end())
{
HWND hWnd = *it;
if(::IsWindow(hWnd) && ::IsDialogMessage(hWnd, lpMsg)
{
//过滤自己需要的消息来处理
if(WM_CHAR == lpMsg->message)
{
::SendMessage(hWnd, WM_GETCHAR, 0, lParam);
}
lpMsg->hwnd = NULL;
lpMsg->message = WM_NULL
lpMsg->lParam = 0;
lpMsg->wParam = 0;
}
++it;
}
}
return ::CallNextHookEx(m_hHook, nCode, wParam, lParam);
}