多桌面热键消息挂钩

日前在开发一个多桌面系统,即利用CreateDesktop创建多个桌面,并自由切换,类似Sysinternal上的Desktops。开发过程中遇到一个问题,当在新创建的桌面上按下某个Win组合快捷键后,当前桌面并不会响应这个消息,而是默认桌面响应了这个消息。为了能在新创建的桌面上响应热键消息,同事反编译了Desktops软件,发现它使用了低级键盘钩子(WH_KEYBOARD_LL),于是仿照这个方法,我们也挂上了低级键盘钩子,当发现按键组合属于热键映射表中的某一项时,向当前桌面的Shell_TrayWnd窗口发送WM_HOTKEY消息并且不在传递此HOOK消息。这个方法用起来还行,但是由于自身写的代码处理不够好,或者说逆向的不够,功能上有个bug。当用户按下热键后,例如Win+R,输入(按下)t 时会继续触发Win+T热键消息。代码显示按下t是win键也处于按下状态,我分析了代码也没找到原因,想彻底的反汇编Desktops的钩子函数又能力不够,只能另想其它方法解决了。

既然默认桌面的Shell_TrayWnd窗口处理了热键消息,那么我是不是可以利用消息钩子截获它呢?于是我利用WH_GETMESSAGE钩子截获了发送到这个窗口线程的消息

	HWND hWnd = ::FindWindow(_T("Shell_TrayWnd"),0);
	DWORD ProcessID;
	DWORD tid=GetWindowThreadProcessId(hWnd,&ProcessID);
	fnInstallHooklib(tid,hWnd,m_hWnd);


为新桌面创建的线程上等待消息,转发热键消息

DWORD WINAPI CHookDemoDlg::ThreadFun(LPVOID lp)
{
	CHookDemoDlg* pThis = (CHookDemoDlg*)lp;
	HDESK hDesk = OpenDesktop(_T("Topdesk"),0,FALSE,GENERIC_ALL);
	if (hDesk!=NULL)
	{
		if(!SetThreadDesktop(hDesk))
		{
			/*The SetThreadDesktop function will fail if the calling thread has any windows 
			or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop).*/
			/*so SetThreadDesktop failed.*/
			pThis->PopErrorMessage(_T("SetThreadDesktop"));
			return 0;
		}
	}
	else
	{
		pThis->PopErrorMessage(_T("OpenDesktop"));
		return 0;
	}

	pThis->ms_newTrayWnd = ::FindWindow(_T("Shell_TrayWnd"), NULL);

	MSG msg;
	CString strMsg;
	while(GetMessage(&msg,NULL,0,0))
	{
		::SetForegroundWindow(pThis->ms_newTrayWnd);
		::OutputDebugString(_T("Receive Thread Message."));
		if(::PostMessage(pThis->ms_newTrayWnd,WM_HOTKEY,msg.wParam,msg.lParam)==FALSE)
		{
			strMsg.Format(_T("PostMessage failed. code = %d"), GetLastError());
			::OutputDebugString(strMsg);
		}
	}

	::OutputDebugString(_T("Assist Thread Exit"));
	return 0;
}


在钩子函数(DLL模块中)处理热键消息

#pragma data_seg("SharedDataName")
HHOOK ms_hHook=NULL;
HWND ms_hSourceWnd=NULL;
HWND ms_hDirectWnd=NULL;
#pragma data_seg()

extern HMODULE ms_hModule;

#pragma comment(linker,"/section:SharedDataName,rws")

LRESULT CALLBACK GetMsgProc(
	_In_  int code,
	_In_  WPARAM wParam,
	_In_  LPARAM lParam
	)
{
	MSG* pMsg = (MSG*)lParam;

	if (pMsg->message==WM_HOTKEY)
	{
		CString strMsg;
		strMsg.Format(_T("%d %d %d %d"), ms_hHook, ms_hSourceWnd, ms_hDirectWnd, pMsg->hwnd);
		::OutputDebugString(strMsg);

		if (pMsg->hwnd == ms_hSourceWnd)
		{
			::OutputDebugString(_T("Receive WM_HOTKEY message."));
			if(!PostMessage(ms_hDirectWnd,WM_USER + 1000,pMsg->wParam,pMsg->lParam))
			{
				strMsg.Format(_T("PostMessage failed. code = %d"), GetLastError());
				::OutputDebugString(strMsg);
			}
			pMsg->message = WM_NULL;
			return 1;
		}
	}
	return CallNextHookEx(ms_hHook,code,wParam,lParam);
}


本来我是打算利用PostThreadMessage向新桌面关联的线程发送消息,但是发送失败,因为钩子函数运行在默认桌面下,而新桌面关联的线程关联的新桌面,一个线程不能向一个属于另外桌面的线程发送线程消息(如果这两个线程属于同一个进程则例外,下面会有这个情形用法),于是我只好向主窗口发送消息。

主窗口线程收到消息后,它可以向新桌面关联的线程发送消息,如下

if (message == (WM_USER + 1000))
	{
		::OutputDebugString(_T("Receive Message."));
		if(!PostThreadMessage(ms_tid,WM_HOTKEY,wParam,lParam))
		{
			CString strMsg;
			strMsg.Format(_T("PostThreadMessage failed. code=%d"),GetLastError());
			::OutputDebugString((LPCTSTR)strMsg);
		}
		return 0;
	}


这样子新创建的桌面就可以截获热键消息了。

 

注意:

1. SetWindowsHookEx只能截获当前桌面的消息,msdn有说明

The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.

 

2. FindWindow函数会在调用线程关联的桌面上查找窗口

3. 若程序以管理员身份启动,为了让钩子函数(在explorer进程中)向程序发送消息,越过UIPI,需要调用函数

ChangeWindowMessageFilter(WM_USER+1000,MSGFLT_ADD);


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值