目的:

实现能自动在窗口中实现鼠标点击与键盘输入的操作(以QQ自动发送某些为例)

语言:

C/C++

实现原理:

一、窗口句柄(HWND)

【1】

每个程序窗口都有一个窗口句柄,在程序的一次运行中句柄是不会发生变化的(每当重新开启程序后句柄会发生改变),句柄的类型为HWND

【2】 我们可以用VS自带的Spy++工具获取窗口句柄,窗口类名,与标题

【2.1】 比如下图我们“记事本”的

窗口句柄是:00030B24

窗口类名是:Notepad

窗口标题是:新建文本文档.txt - 记事本

python 点击窗口获取句柄 python获取窗口句柄自动点击_句柄


【2.2】 在窗口句柄下还有子窗口句柄,比如编辑框的句柄为:00060B0E ,类名为:Edit

python 点击窗口获取句柄 python获取窗口句柄自动点击_类名_02


【3】 ※在我们通过使用Spy++工具后能查到窗口的类名与标题。在代码编写中,通过例如 FindWindow 这样的函数可以查找并返回对应窗口的句柄值,并将其赋予窗口句柄(HWND)类型的变量中,在接下来的代码中引用。

【3.1】使用FindWindow函数查找窗口句柄

函数原型:HWND FindWindow(LPCSTR lpClassName,LPCSTR lpWindowName);
其中:lpClassName参数指向类名,lpWindowName指向窗口名,如果有指定的类名和窗口的名字则表示成功返回一个窗口的句柄。否则返回零。(类名与窗口名至少知道一个就可以进行查找)

例:获得窗口

HWND h = FindWindow(L"Notepad", NULL);HWND h = FindWindow(NULL, L"新建文本文档.txt - 记事本");

在这里通过Spy++查找窗口的类名,知道了记事本窗口的类名Notepad,则可成功查找(或者使用窗口标题)

python 点击窗口获取句柄 python获取窗口句柄自动点击_类名_03


【3.2】代码

#include <iostream>
#include <Windows.h>   //使用HWND的头文件
using namespace std;
int main()
{
	HWND h;
	h = FindWindow(L"Notepad", NULL);
	cout << hex << (int)h;
}

输出结果(可以看出来和Spy++查到的结果一样):

python 点击窗口获取句柄 python获取窗口句柄自动点击_窗口句柄_04


二、鼠标模拟点击(两种方法):

【第一种方法】

原理:1 获取到窗口的句柄。 2 将窗口坐标保存到坐标结构体RECT中。 3 将物理鼠标的指针指向指定的位置。 4 模拟点击

1 GetWindowRect 函数原型 :BOOL GetWindowRect(HWND hWnd,LPRECT lpRect);
作用:第一个参数为窗口的句柄,第二个参数可以为RECT类的变量,用来保存窗口的x坐标和y坐标
2 SetCursorPos 函数原型 :BOOL SetCursorPos(int X,int Y);
作用:使得现在的物理鼠标指向参数数值的位置
3 mouse_event 函数原型 :
VOID mouse_event(
DWORD dwFlags, // motion and click options
DWORD dx, // horizontal position or change
DWORD dy, // vertical position or change DWORD
dwData, // wheel movement
ULONG_PTR dwExtraInfo //application-defined information );

作用: 执行鼠标的动作,例如下面的代码块就是一次鼠标左键点击操作

HWND h;
h = FindWindow(L"Notepad", NULL);
RECT rect;
GetWindowRect(h, &rect);
SetCursorPos(rect.left + mX, rect.top + mY);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

优点:直接根据位置点击,不易失效
缺点:1 如果目标窗口上面有窗口遮挡,则不能点击到目标窗口(因为是完全模拟物理鼠标)
2 在远程服务器端 由于远程服务器的特性(桌面和远程访问端有关的原因),断开连接时,没有桌面窗口与物理鼠标,程序会失效

【第二种方法】
原理:1 获取到窗口的句柄。 2 向窗口中发送鼠标点击的信息 (mX与mY为坐标)

1 FindWindowEx 函数原型 HWND FindWindowEx(HWND hwndParent,HWND
hwndChildAfter,LPCTSTR lpszClass,LPCTSTR lpszWindow);

作用 :
第一个参数为父窗口的句柄;
第二个参数为子窗口句柄,查找从在Z序中的下一个子窗口开始,子窗口必须为hwndParent窗口的直接子窗口而非后代窗口,如果HwndChildAfter为NULL,查找从hwndParent的第一个子窗口开始。
第三个和第四个参数与FindWindow函数相同 2 SendMessage 函数原型 LRESULT
SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam)

作用 :第一个参数为发送信息的目标窗口句柄,第二个参数为指定被发送的消息,第三个与第四个参数为指定附加的消息特定信息

HWND h;
h = FindWindow(L"Notepad", NULL);
SendMessage(h, WM_LBUTTONDOWN, VK_LBUTTON, mX + mY * 65536);
SendMessage(h, WM_LBUTTONUP, 0, mX + mY * 65536);

优点:在窗口最小化时、在远程服务器断开时没有鼠标时,都可以正常点击,并且与物理鼠标操作不冲突
缺点:只能向当前的句柄窗口上发送点击信息。如果点击的位置有子句柄窗口,则必须获取到子窗口的句柄,并向子句柄窗口发送点击信息。(通常我用函数FindWindowEx查找子窗口的句柄,如以下代码段

HWND h,p;
h = FindWindow(L"Notepad", NULL);
p = FindWindowEx(h, NULL, L"Edit", NULL);
SendMessage(p, WM_LBUTTONDOWN, VK_LBUTTON, mX + mY * 65536);
SendMessage(p, WM_LBUTTONUP, 0, mX + mY * 65536);

三、键盘模拟点击(不能具体得到输入框句柄的一种处理方式):
原理:1 激活要输入的程序。 2 确定焦点,并通过鼠标点击激活输入框的光标。 进行键盘模拟

keybd_event 函数原型 VOID keybd_event(BYTE bVk,BYTE bScan,DWORD
dwFlags,DWORD dwExtralnfo)

作用 : 参数:
bVk:定义一个虚拟键码。键码值必须在1~254之间。
bScan:定义该键的硬件扫描码。 dwFlags:定义函数操作的各个方面的一个标志位集。应用程序可使用如下一些预定义常数的组合设置标志位。
KEYEVENTF_EXTENDEDKEY:若指定该值,则扫描码前一个值为OXEO(224)的前缀字节。
KEYEVENTF_KEYUP:若指定该值,该键将被释放;若未指定该值,该键将被按下。
dwExtralnfo:定义与击键相关的附加的32位值。

//此处是模拟一次alt+s的模拟
keybd_event(18, 0, 0, 0);					//按下alt
keybd_event(83, 0, 0, 0);					//按下S
Sleep(500);
keybd_event(83, 0, KEYEVENTF_KEYUP, 0);		//抬起S
keybd_event(18, 0, KEYEVENTF_KEYUP, 0);		//抬起alt
Sleep(500);

四、获取某个窗口的截图并将其放入剪贴板中(这一部分代码忘了的原作者的链接,等找到之后补到后面)

void CaptureScreen(HWND h, int Flag)
{
	//if (Flag == 1)cout << "【现在发送第一张截图" << endl;
	//else if (Flag = 2)cout << "【现在发送第二张截图" << endl;
	//获得屏幕宽度
	int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
	//获得屏幕高度
	int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
	//获得桌面窗口设备环境
	HDC hDC = GetDC(h);
	//产生全屏幕窗口设备描述表的兼容设备环境
	HDC hCaptureDC = CreateCompatibleDC(hDC);
	//产生全屏幕窗口设备描述表的兼容位图
	HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDC, nScreenWidth, nScreenHeight);
	//将兼容位图选入兼容设备环境
	SelectObject(hCaptureDC, hCaptureBitmap);
	//将全屏幕窗口位图的象素数据拷贝到兼容设备描述表
	BitBlt(hCaptureDC, 0, 0, nScreenWidth, nScreenHeight, hDC, 0, 0, SRCCOPY);
	HBITMAP hBmp = (HBITMAP)SelectObject(hCaptureDC, hCaptureBitmap);
	if (OpenClipboard(0))
	{
		EmptyClipboard();
		SetClipboardData(CF_BITMAP, hBmp);
		CloseClipboard();
	}
	//QQsend();
	ReleaseDC(h, hDC);
	DeleteDC(hCaptureDC);
	DeleteObject(hCaptureBitmap);
}

实现代码(不完善的):

这个函数前面是通过句柄获得了某个窗口的截图,并将其放入了剪贴板中。
接下来是要将截图发送到qq的某个人
注:这里面使用的是腾讯的TIM软件,windows系统
注:使用时要保证要发送的那个人是在窗口最前

void QQsend() {
	HWND h = FindWindow(L"TXGuiFoundation", L"鸽");
	RECT rect;
	GetWindowRect(h, &rect);
	ShowWindow(h, SW_RESTORE);					//激活h窗口
	SetFocus(h);								//设置当前窗口为焦点
	SetForegroundWindow(h);						//指定窗口的线程设置到前台,激活该窗口。键盘输入转向该窗口
	cout << "【进行粘贴操作" << endl;

	//模拟发送按键alt+s
	keybd_event(18, 0, 0, 0);
	keybd_event(83, 0, 0, 0);
	Sleep(500);
	keybd_event(83, 0, KEYEVENTF_KEYUP, 0);
	keybd_event(18, 0, KEYEVENTF_KEYUP, 0);
	Sleep(500);

	//模拟粘贴按键ctrl+v
	keybd_event(17, 0, 0, 0);
	keybd_event(86, 0, 0, 0);
	Sleep(500);
	keybd_event(86, 0, KEYEVENTF_KEYUP, 0);
	keybd_event(17, 0, KEYEVENTF_KEYUP, 0);
	Sleep(500);

	//模拟发送按键alt+s
	keybd_event(18, 0, 0, 0);
	keybd_event(83, 0, 0, 0);
	Sleep(500);
	keybd_event(83, 0, KEYEVENTF_KEYUP, 0);
	keybd_event(18, 0, KEYEVENTF_KEYUP, 0);
	Sleep(500);

	cout << "【点击发送" << endl;
	//点击发送
	SendMessage(h, WM_LBUTTONDOWN, 0, (rect.right - 60) + (rect.bottom - 70) * 65536);
	SendMessage(h, WM_LBUTTONUP, 0, (rect.right - 60) + (rect.bottom - 70) * 65536);
}

所说的窗口最前是保证要发送信息的那个人的窗口要这个样子

python 点击窗口获取句柄 python获取窗口句柄自动点击_句柄_05

编后记:在这篇记录中只介绍了相关函数的非常一小部分的定义及使用方法,十分建议查找阅读相关的文档深化理解,感谢阅读,欢迎指正。