令人头痛的WH_CBT钩子,使窗口前置——泪水+汗水的赞歌

一、工作中遇到一个问题:

1、需要让某个特定的窗口不被最小化、不被阻挡、不失去焦点(因为有输入);

2、由某个服务或进程来自动筛选和控制。

本来觉得用几个windows API就OK了,谁知道弄了好长好长的时间,崩溃了N次,直到现在..............依旧不敢100%确定,唉!

二、思路

1、FindWindow而后前置SetForegroundWindow

——总感觉似乎不妥,野蛮暴力,还得检索window句柄列表

2、插个全局钩子,自行判断是否前置

——感觉好一点,没那么暴力了,不过问题来了:用什么钩子?

三、实践

1、找啊找,找遍了整个屋子,终于找着了这本《阿里波特》——HCBT_CREATEWND,可以监视窗口创建——心花怒放!

——于是开始了一周的调试——注定要烧脑到死——跑偏了。

2、失败!失败!失败.........!

3、终于在崩溃前夕,不经意间发现了有个叫HCBT_ACTIVATE的家伙——她居然在灯火阑珊处好久了!小试一下,居然比那个桀骜不驯的HCBT_CREATEWND乖巧多了!不会分辨不清窗口和菜单、不会去分辨对话框窗口........,爱死你了!

好吧,就你了!

4、天真地认为花两小时调试一下就OK了..............图样图森破

5、要么找不到句柄1428,要么126、193.............

6、又一次在崩溃的边缘发现她还有一闺蜜——LoadLibraryEx,那个LoadLibrary已经被Win7封印了!(开发环境win10却好好的)好吧,有新欢就新欢吧,听你的!

噢,差点忘了#pragma data_seg,也如影随形,照顾不周,她就会捣乱,好吧,咱俩不熟,先学习研究下.......

7、啊?程序不能移植?这没有道理啊?肯定是目标机缺少运行库环境!我装——

8、VC++运行库合集——利刃呐!

——什么!还不行?!

9、...................................我还是程序员吗?

10、再去崩溃的边缘溜达溜达吧..............3、4天了,怎么跟人交差啊!

11、传说中有个叫“Release版本”的朋友,可我一直没有联系过!不经意间,他似乎.............

12、试试看吧,也许呢?...............朋友就是朋友啊,够意思!

13、哈哈,钩子跑起来了,貌似成功了!

14、..................怎么?钩子使性子?一会管用一会不管用?

15、家法伺候中.....................

16、我知道了不是你的错,你是32位的,还得有64位的,还得有32和64位的注入程序——好吧,给你俩克隆个双胞胎兄弟!——等等,我先研究下clone技术.................

17、什么?克隆了还不老实?...............................

——一会让我的VS窗口前置,一会让我的浏览器前置,当我的strstr函数不存在啊?

18、这下百度帮不了了,怎么回事?宣告是系统bug吗?

19、找bug,找bug,找bug,找bug,找bug,找bug,找bug,找bug,找bug,找......................

20、天道酬勤啊,原来是你——局部变量const,真不明白strstr函数就那么矫情,非要const参数干吗,可传个非const似乎也行........

21、于是经过很久很久的多次崩溃,钩子就成了下面这个样子——

#include 
#include 
#include 
#include 
//#define _WIN32_WINNT 0x500

using namespace std;
#pragma data_seg("SharedDataName")
HHOOK hooker=NULL;
HWND ghwnd = NULL;
DWORD pid = -1;
//string TargetName = "AppFiles";
const char* target = "AppFiles";//TargetName.c_str();
#pragma data_seg()
#pragma comment(linker,"/section:SharedDataName,rws")

HWND privateHwnd;

extern "C" __declspec(dllexport) LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
extern "C" __declspec(dllexport) HHOOK setHookDll();
extern "C" __declspec(dllexport) BOOL unHookDll();
extern "C" __declspec(dllexport) void getPid(DWORD id);
extern "C" __declspec(dllexport) void getPName(string name);


BOOL APIENTRY DllMain(HMODULE hModule/* hModule */, DWORD ul_reason_for_call, LPVOID /* lpReserved */lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		privateHwnd = NULL;
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

void   CALLBACK   TimerProc(HWND   hWnd, UINT   nMsg, UINT   nTimerid, DWORD   dwTime);

extern "C" __declspec(dllexport) LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) {
	if (nCode<0)
		return CallNextHookEx(hooker, nCode, wParam, lParam);
	tagMSG* msg;
	msg = (tagMSG*)lParam;
	switch (nCode)
	{
		case HC_ACTION:
			break;
		case HCBT_CREATEWND:
			break;
		case HCBT_ACTIVATE:
		{
			HWND currentHwnd = (HWND)wParam;

			DWORD processID = NULL;
			GetWindowThreadProcessId(currentHwnd, &processID);
			HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);

			//LPSTR procName;
			TCHAR procName[MAX_PATH] = { 0 };
			GetProcessImageFileName(hProcess, procName, MAX_PATH);
			char* source = procName;
			
			if (strstr(source, target) != NULL)
			{
				ghwnd = currentHwnd;
				privateHwnd = currentHwnd;
				KillTimer(currentHwnd, 1);
				//MessageBox(currentHwnd, source, target, MB_OK);
				SetTimer(currentHwnd, 1, 1000, (TIMERPROC)TimerProc);
			}
			break;
		}
		default:
			break;
	}

	return CallNextHookEx(hooker, nCode, wParam, lParam);
}

extern "C" __declspec(dllexport) HHOOK setHookDll()
{
	hooker = SetWindowsHookEx(WH_CBT, HookProc, (HINSTANCE)GetModuleHandle("winhook.dll"), 0);//64位 用winhook64.dll
	return hooker;
}

extern "C" __declspec(dllexport) BOOL unHookDll()
{
	KillTimer(privateHwnd, 1);
	bool unHk = UnhookWindowsHookEx(hooker);
	return unHk;
}

extern "C" __declspec(dllexport) void getPid(DWORD id)
{
	pid = id;
}

extern "C" __declspec(dllexport) void getPName(string name)
{

	//TargetName = name;
}

void _stdcall  CALLBACK   TimerProc(HWND   hWnd, UINT   nMsg, UINT   nTimerid, DWORD   dwTime)
{
	SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOMOVE);
	//BringWindowToTop(currentHwnd);
	//SetFocus(currentHwnd);
	//SetActiveWindow(currentHwnd);
	PostMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE,  NULL);

	HWND hForeWnd = ::GetForegroundWindow();
	DWORD dwForeID = ::GetWindowThreadProcessId(hForeWnd, NULL);
	DWORD dwCurID = ::GetCurrentThreadId();
	::AttachThreadInput(dwCurID, dwForeID, TRUE);
	SetForegroundWindow(hWnd);
	PostMessage(hWnd, WM_SETFOCUS, NULL, NULL);
	return;
}

22、不过在前置窗口并获取焦点的问题上倒数第三行的postmessage,以及前面的SetFocus似乎不怎么管用,在实际体验中感觉还是有小问题,我是没着了。

四、小记

1、微软前置个窗口怎么就那么累呢?

2、指定窗口获取个焦点怎么就那么费劲呢?死活就是不认。

3、非共享代码段似乎也会出问题,但不确认。

你可能感兴趣的:(C++)