在日常的一些操作中,会遇到重复的鼠标动作,类似按键精灵的软件就会成为比较好的助手。这里借助网上查找的资料自己实现了一个简单的鼠标动作录制软件。
完成界面如图:
录制
录制鼠标动作首先需要截获。钩子函数是一种对Windows系统进程进行监听的函数,可以用来截获鼠标、键盘的消息。钩子函数的实现方法很多,这里借鉴的是一种比较简单的方法,但是注意要在MFC中使用,我曾在控制台程序下使用,会导致程序卡死,原因尚未知。
(参考:http://www.cnblogs.com/gongxijun/p/5043825.html
http://www.cnblogs.com/gongxijun/p/5043825.html)
关键回调函数如下:
LRESULT CALLBACK LowLevelMouseProc(INT nCode, WPARAM wParam, LPARAM lParam){
CPoint _mousepoint;
MSLLHOOKSTRUCT *pkbhs = (MSLLHOOKSTRUCT *)lParam;
if(nCode == HC_ACTION && start_log){//鼠标左击
if (wParam == WM_LBUTTONDOWN){/* || wParam == WM_LBUTTONUP*/
FILE*fp;
if (!first_click){//第一次没点过
HWND h = ::GetForegroundWindow();
hWnd = h;
fopen_s(&fp, filename, "w");
first_click = true;
WCHAR name[100];
::GetWindowText(h, name, 100);
fprintf(fp, "%s\n", UnicodeToAnsi(name));
fclose(fp);
}
fopen_s(&fp, filename, "a");
if (is_time_count){
int end = GetTickCount();
fprintf(fp, "%d\n", end - time_count);
time_count = end;
}
else{
is_time_count = true;
time_count = GetTickCount();
}
CPoint point;
RECT rc;
GetCursorPos(&point);
GetWindowRect(hWnd, &rc);
int x = point.x - rc.left;
int y = point.y - rc.top;
fprintf(fp, "%d %d\n", x, y);
fclose(fp);
}
else if (first_click && (wParam == WM_RBUTTONDOWN || wParam == WM_RBUTTONUP)){//结束
start_log = false;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
实现的功能是,从输入框读取要保存的文件名,然后在第一次点击后开始录制,把操作的窗口名保存到文件,然后保存坐标。在之后每一次点击,计算与之前的时间差(参考:http://blog.csdn.net/coder_xia/article/details/6566708)
运行
运行录制文件中的数据,只需要一些Windows的API即可实现鼠标点击:
char* A_p = UnicodeToAnsi(m_infile.GetBuffer());
ifstream fin(A_p);
if (fin.fail()){
MessageBox(L"打开文件失败!", NULL, MB_OK);
return;
}
int x, y;
char fname[100];
fin >> fname;
HWND hWnd;
hWnd = FindWindowA(NULL, fname);
if (!hWnd){
MessageBox(L"窗口不存在!", NULL, MB_OK);
return;
}
while (fin >> x >> y){
LPARAM lParam = MAKELPARAM(x, y);
::SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, lParam);
Sleep(250);
::SendMessage(hWnd, WM_LBUTTONUP, MK_LBUTTON, lParam);
Sleep(250);
int wait;
if (fin >> wait){
Sleep(wait);
}
else{
break;
}
}
fin.close();
但在对话框的函数中使用上述方法,会导致运行后程序卡死,因此需要多线程编程,使二者运行分离;同时,在将读入的数据用一个数据结构描述后再进行运行会使得运行变得可控。
class mouse_act{
HWND hWnd;
std::vector<int>x;
std::vector<int>y;
std::vector<int>wait;
int count = 0;
public:
mouse_act(HWND wd) :hWnd(wd){ ; }
mouse_act(){ hWnd = NULL; }
void add(int _x, int _y, int _wait = -1){
x.push_back(_x);
y.push_back(_y);
wait.push_back(_wait);
}
void play(){
if (!hWnd)return;
LPARAM lParam = MAKELPARAM(x[count], y[count]);
::SendMessage(this->hWnd, WM_LBUTTONDOWN, MK_LBUTTON, lParam);
Sleep(250);
::SendMessage(this->hWnd, WM_LBUTTONUP, MK_LBUTTON, lParam);
Sleep(250);
if (wait[count] > 0){
Sleep(wait[count]);
count++;
}
else{
count = 0;
}
}
void clear(){
hWnd = NULL;
}
bool isNULL(){
return hWnd == NULL;
}
};
mouse_act act_player;
使用多线程的方法(参考:http://www.cnblogs.com/codingmengmeng/p/5913068.html),使其在全局变量控制下逐步运行:
bool play_flag = false;
DWORD WINAPI PlayMouseAction(LPVOID lpParamter){
while(play_flag){
act_player.play();
}
return 0;
}