触摸屏应用相关技术之二——鼠标键盘hook
上文提及,在系统启动过程中,到访问内容完全占据桌面的间隙,桌面直接暴露给访客,这是危险的间隙,需要想办法解决。
我们的应对措施是:在这段间隙中,对鼠标键盘进行完全锁定,直至访问内容全屏打开。开放鼠标左键,允许访客交互内容。
利用hook,我们可以在客户端统计点击数,及长时间无人点击时自动切回内容首页。
为此需要利用hook技术,并在Winlogon中载入。
在注册表中登记项如下:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\CET]
"Asynchronous"=dword:00000000
"Dllname"="SetHook.dll"
"Impersonate"=dword:00000000
"Logoff"="StopProcessAtWinLogoff"
"Logon"="StartProcessAtWinLogon"
SetHook.dll中的关键代码如下:
…
//全局变量
HHOOK hKeyBoardHook=NULL; //keyboard hook
HHOOK hMouseHook=NULL; //mouse hook
HWND hOutPutWnd=NULL; //Display Pass Wnd
…
//Winlogon加载函数
VOID APIENTRY StartProcessAtWinLogon (PWLX_NOTIFICATION_INFO pInfo)
{
//start hook
hThread= CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
//get jk_path
getJkPath();
}
…
//Winlogon卸载函数
VOID APIENTRY StopProcessAtWinLogoff (PWLX_NOTIFICATION_INFO pInfo)
{
//MessageBox(NULL,"系统正在注销!","Winlogon Notification Package",MB_OK);
}
…
//鼠标键盘hook
DWORD WINAPI ThreadFunc()
{
HDESK hDesk;
//_H同一桌面上进程之间只能发送窗口消息。无法跨进程与其他桌面发送它们。
//_H同样,Windows消息是限制应用程序定义挂钩。
//_H特定桌面中运行的进程挂钩过程将〈〈只获得针对同一桌面上创建窗口消息。〉〉
//_H详见http://support.microsoft.com/kb/171890/zh-cn
//_H所以,这里必须设置钩子所在线程的桌面为Default桌面
//_H才能使得钩子所在线程能接收到Default桌面的消息
hDesk = OpenDesktop("Default",0,FALSE,MAXIMUM_ALLOWED);
SetThreadDesktop(hDesk);
CloseHandle(hDesk);
//_H设置低级键盘钩子,屏蔽非SAS window的热键
//_H需要#define _WIN32_WINNT 0x0500
hMouseHook=SetWindowsHookEx(WH_MOUSE_LL,MouseHookProc,glhInstance,0);
hKeyBoardHook=SetWindowsHookEx(WH_KEYBOARD_LL,KeyBoardProc,glhInstance,0);
if (hMouseHook == NULL)
{
OutputDebugString("Set hook failed..");
//__leave;
return 1;
}
OutputDebugString("钩子成功设置");
// ::ShowWindow(::FindWindow("ProgMan",NULL),SW_HIDE);
// ::ShowWindow(::FindWindow("Shell_TrayWnd",NULL),SW_HIDE);
//_H在非GUI线程中使用消息钩子必须主动接收并分发收到的消息
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
…
//取消hook
void CloseMe(){
if(hOutPutWnd!=NULL)
::SendMessage(hOutPutWnd,WM_IDLE,2,0);
BOOL mHook=UnhookWindowsHookEx(hMouseHook);
BOOL kHook=UnhookWindowsHookEx(hKeyBoardHook);
TerminateThread(hThread,1);
CloseHandle(hThread);
hThread=NULL;
}
…
//向桌面程序发送鼠标消息,在此基础上可以统计点击数,及长时间无人点击自动切回内容首页
void OnEvent(){
::SendMessage(hOutPutWnd,WM_IDLE,1,0);
}
…
//如果检查到桌面程序被强制关闭了,再启动它!
void ShellJK(){
PROCESS_INFORMATION pi;
STARTUPINFO sti;
ZeroMemory(&sti,sizeof(sti));
sti.cb=sizeof(sti);
sti.lpDesktop="winsta0\\default";
CreateProcess(jk_file,NULL,NULL,NULL,FALSE,0,NULL,jk_path, &sti, &pi);
}
//键盘hook,按Ctrl+ESC或者Ctrl+Space退出hook
LRESULT WINAPI KeyBoardProc(int nCode,WPARAM wParam,LPARAM lParam)
{ //keyboard hook proc
BOOL bUnlock1=FALSE;
BOOL bUnlock2=FALSE;
OnEvent();
if (nCode == HC_ACTION){
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT) lParam;
bUnlock1=(p->vkCode == VK_ESCAPE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0);//Ctrl+Esc
bUnlock2=(p->vkCode == VK_SPACE) && ((GetKeyState(VK_CONTROL) & 0x8000) != 0);//Ctrl+Space
break;
}
}
if(bUnlock1 || bUnlock2){
CloseMe();
}
return CallNextHookEx(hKeyBoardHook,nCode,wParam,lParam);
}
…
//鼠标hook,在hOutPutWnd为NULL前屏蔽所有事件,之后允许左键
LRESULT WINAPI MouseHookProc(int nCode,WPARAM wParam ,LPARAM lParam)
{
//LPMOUSEHOOKSTRUCT lpMouse=(MOUSEHOOKSTRUCT FAR*)lParam;
if(wParam==WM_RBUTTONDOWN | wParam==WM_RBUTTONUP
| wParam==WM_LBUTTONDBLCLK){
return TRUE;
}
if(hOutPutWnd!=NULL){
if(wParam==WM_LBUTTONDOWN && nCode>=0){
if(!IsWindow(hOutPutWnd)){
ShellJK();
return TRUE;
}
OnEvent();
}
return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
}
else
return TRUE;
}
…
//桌面程序全屏之后通知hook程序其窗口句柄
BOOL CHook::StartHook(HWND hwnd, int span, int dev)
{
if(hThread==NULL)
hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,0,&dwThreadId);
hOutPutWnd=hwnd;
return TRUE;
}