目的:
实现能自动在窗口中实现鼠标点击与键盘输入的操作(以QQ自动发送某些为例)
语言:
C/C++
实现原理:
一、窗口句柄(HWND):
【1】
每个程序窗口都有一个窗口句柄,在程序的一次运行中句柄是不会发生变化的(每当重新开启程序后句柄会发生改变),句柄的类型为HWND
【2】 我们可以用VS自带的Spy++工具获取窗口句柄,窗口类名,与标题
【2.1】 比如下图我们“记事本”的
窗口句柄是:00030B24
窗口类名是:Notepad
窗口标题是:新建文本文档.txt - 记事本
【2.2】 在窗口句柄下还有子窗口句柄,比如编辑框的句柄为:00060B0E ,类名为:Edit
【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,则可成功查找(或者使用窗口标题)
【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++查到的结果一样):
二、鼠标模拟点击(两种方法):
【第一种方法】
原理: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);
}
所说的窗口最前是保证要发送信息的那个人的窗口要这个样子
编后记:在这篇记录中只介绍了相关函数的非常一小部分的定义及使用方法,十分建议查找阅读相关的文档深化理解,感谢阅读,欢迎指正。