用鉤子函數實現鼠標動作錄製

在日常的一些操作中,會遇到重複的鼠標動作,類似按鍵精靈的軟件就會成爲比較好的助手。這裏藉助網上查找的資料自己實現了一個簡單的鼠標動作錄製軟件。
完成界面如圖:
這裏寫圖片描述

錄製

錄製鼠標動作首先需要截獲。鉤子函數是一種對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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章