前段时间因为参加个比赛要用atl做个三维控件,但是用atl写好的控件在c#里调用不能捕捉左边键盘的事件。修改了控件的很多属性,弄了很久还是解决不了,后来在网上偶遇键盘钩子的方法,然后抱着试一试的态度,竟然成功了,兴奋不已!现在没事弄,故记之。
1.目的:
在用ATL写一个影像展现控件时,有一个需求,希望如windows使用习惯一样,用户可以按住Ctrl键选中多个 图片,松开Ctrl,鼠标再次点击时取消所选的图片.
键盘钩子函数的作用是获得按键状态(按了什么键、重复多少次、Shift/Ctrl/Alt是什么状态,是Pressed 还是Released等).这里关心Ctrl是Pressed 还是Released,用一个全局bKeyBPress记录.
2.钩子函数
控件的cpp文件里:
extern HINSTANCE ghInstance;
bool bKeyBPress=false;
HHOOK gkeyHook2=NULL;
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wparam, LPARAM lparam)
{
if (nCode>= 0)
{
if(wparam==VK_CONTROL){
if((DWORD)lparam&0x80000000){
bKeyBPress=false;
}
else{
bKeyBPress=true;
}
}
}
return CallNextHookEx(gkeyHook2, nCode, wparam, lparam);
}
3.如何获得dll的ghInstance,如何注册钩子函数
ghInstance实际定义在dllmain.cpp中,在DllMain中给它赋值.如下:
HINSTANCE ghInstance;
// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
hInstance;
ghInstance=hInstance;
return _AtlModule.DllMain(dwReason, lpReserved);
}
控件的.h文件里:
extern HHOOK gkeyHook2;
extern LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wparam, LPARAM lparam);
在控件的OnCreate里注册:
gkeyHook2=::SetWindowsHookEx(WH_KEYBOARD,KeyBoardProc,ghInstance,0);
在FinalConstruct()中注销:
::UnhookWindowsHookEx(gkeyHook2);
4.剩下的事
ctrl的按键状态已经在钩子函数里获得了,剩下的事就是在控件的OnLButtonDown事件里使用这个按键状态了.
5.在做这个功能时先用的方法是在控件的OnKeyDown和OnKeyUp事件里更新状态:
LRESULT CNKIViewCtrl::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// TODO: Add your message handler code here and/or call default
CComQIPtr<IWebBrowser2, &IID_IWebBrowser2> m_pWebBrowser2;
HRESULT hrrs = CoCreateInstance(CLSID_WebBrowser, NULL,CLSCTX_INPROC,IID_IWebBrowser2,(void**)&m_pWebBrowser2);
CComQIPtr<IOleInPlaceActiveObject,
&IID_IOleInPlaceActiveObject> pIOIPAO(m_pWebBrowser2);
HRESULT hr = S_FALSE;
if (pIOIPAO)
{
MSG msg;
msg.message = uMsg;
msg.wParam = wParam;
msg.lParam = lParam;
this->TranslateAccelerator(&msg);
if (wParam==17)
{
//do something
}
bHandled=FALSE;
}
return hr;
}
起初的问题是控件根本没有接收KeyDown和KeyUp事件,百度后加了下面一段代码,
MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
LRESULT OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
return DLGC_WANTARROWS;
}
但也不是所有的键盘Down和up都能接收,所以最后用得键盘钩子的办法,效果终于对了.