1. 程式人生 > >訊息鉤子與定時器(VC_Win32)

訊息鉤子與定時器(VC_Win32)

訊息鉤子

[概述][相關函式][編寫訊息鉤子]

概述

鉤子過程

作業系統在傳遞訊息時,將我們感興趣的訊息先傳遞給HOOK過程,在此函式中進行檢查,然後在決定是否放行該訊息,就好像逃犯在逃亡時可能會經過許多段路段,為了抓住他,警察要在某些地方設定檢查站,以便檢查過往的車輛和行人,我們可以把車輛和行人看做是訊息,檢查站就好像是HOOK過程,如果在摸個檢查站發現了這個逃犯,就會把他抓起來,這樣就相當於阻止了逃犯的逃亡過程,讓他無法在繼續逃亡下去了,這個道理和鉤子過程是一樣的,作業系統將我們感興趣的訊息都交給鉤子過程,後者實際就是一個函式,在此函式中進行判斷,如果是我們希望遮蔽的訊息,那麼就直接處理掉,不然它繼續向下傳遞.如果我們不感興趣的訊息,就直接放棄對它們的處理

,這就好像對於那些不是逃犯的行人和車輛一樣,警察會讓他們繼續前進

鉤子鏈

SetWindowsHookEx函式作用是安裝一個應用程式定義的鉤子過程,並將其放到鉤子鏈中,為了讓讀者更好的理解鉤子的概念,讓我們在看看前面所舉的逃犯的例子.警察在抓捕逃犯時,可以再多個地方設定檢查站,逐一對車輛和行人進行排查,同樣地,應用程式也可以在多個地方安裝鉤子函式,對我們感興趣的多個訊息逐一進行排查,這樣多個鉤子過程就形成了鉤子鏈,要注意的是,最後安裝的鉤子過程總是排列在該鏈的前面

訊息鉤子分類

  • 程序內鉤子  勾取本程序訊息
  • 全域性鉤子     勾取所有訊息

相關函式

設定訊息鉤子 SetWindowsHookEx

函式原型

HHOOK SetWindowsHookEx(
int idHook, // hook type
HOOKPROC lpfn, // hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // thread identifier
);

引數說明

  • idHook

    指定要安裝的鉤子過程的型別

    • WH_CALLWNDPROC    安裝一個鉤子過程,在作業系統將訊息傳送到目標視窗處理過程之前,對該訊息進行監視
    • WH_CALLWNDPROCRET 安裝一個鉤子過程,它對被目標視窗處理過了的訊息進行監視
    • WH_CBT
               安裝一個鉤子過程,接受對CBT應用程式有用的訊息
    • WH_DEBUG         安裝一個鉤子過程,以便對其他鉤子過程除錯
    • WH_FOREGROUNDIDLE  安裝一個鉤子過程,該鉤子過程當應用程式的前提執行緒即將進入空閒狀態時被呼叫,它有助於在空閒時間內執行低優先順序的任務
    • WH_GETMESSAGE     安裝一個鉤子過程對傳送到訊息佇列的訊息進行監視
    • WH_JOURNALPLAYBACK 安裝一個鉤子過程,對此前由WH_JORNALRECORD鉤子過程記錄的訊息進行傳送
    • WH_JOURNALRECORD   安裝一個鉤子過程,對傳送到系統訊息佇列的輸入訊息進行記錄
    • WH_KEYBOARD      安裝一個鉤子過程,對鍵盤按鍵訊息進行監視
    • WH_KEYBOARD_LL      安裝一個鉤子過程,只能在Windows NT中安裝,用來對底層的鍵盤輸入事件進行監視
    • WH_MOUSE        安裝一個鉤子過程,對滑鼠訊息進行監視
    • WH_MOUSE_LL       安裝一個鉤子過程,只能在Windows NT中安裝,用來對底層的滑鼠輸入事件進行監視
    • WH_MSGFILTER      安裝一個鉤子過程,以監視由對話方塊,訊息框,選單條,或滾動條中輸入事件引發的訊息
    • WH_SHELL            安裝一個鉤子過程,以接受外殼應用程式有用的通知
    • WH_SYSMSGFILTER   安裝一個鉤子過程,以監視對話方塊,訊息框,選單條,或滾動條中輸入事件引發的訊息,該鉤子過程對系統中所有應用程式的這裡訊息都進行監視

    對應的回撥函式也就是鉤子函式的處理過程的形式在MSDN查閱

    如果鉤子函式返回非零值,表示已經對當前訊息進行了處理,這樣系統就不會在將這個訊息傳遞給目標視窗過程,因此,如果鉤子過程對於當前訊息進行處理,則返回一個非零值,以避免系統再次將此訊息傳遞給目標視窗過程;否則建議呼叫CallNextHookEx含返回該函式的返回值,以便其他安裝了此訊息型別鉤子過程的應用程式捕獲相應的通知

  • lpfn

    指向相應的鉤子過程,如果dwThreadId引數為0,或者指定了一個其他程序建立的執行緒識別符號,那麼引數lpfn必須指向一個位於動態連結庫中的鉤子過程.否則,引數lpfn可以指向當前程序相關的程式碼中定義的一個鉤子過程

  • hMod

    指定lpfn指向的鉤子過程所在的DLL的控制代碼,如果引數dwThreadId指定的執行緒由當前程序穿件,並且相應的鉤子過程定義與當前程序相關的程式碼中,那麼必須將引數hMod設定為NULL(全域性鉤子,此引數必須指向DLL控制代碼)

  • dwThreadId

    指定與鉤子過程相關的執行緒標識,如果值為0,那麼按照的鉤子過程將與桌面上執行的所有執行緒都有關(安裝的鉤子過程可以與某個特定執行緒相關,也可以和所有執行緒相關,取決於這個引數的取值)

返回值

如果成功返回的值是鉤子過程的控制代碼,如果函式失敗返回的是NULL

鉤子回撥函式原型 CallNextHookEx

函式原型

LRESULT CallNextHookEx(
HHOOK hhk, // handle to current hook
int nCode, // hook code passed to hook procedure
WPARAM wParam, // value passed to hook procedure
LPARAM lParam // value passed to hook procedure
);

引數說明

  • hhk:  指定當前鉤子過程控制代碼
  • nCode,wParam,lParam:  後三個引數是鉤子過程回撥函式的三個引數

返回值

這個值是在鉤子鏈中的下個鉤子函式的返回值

取消訊息鉤子函式 UnhookWindowsHookEx

函式原型

BOOL UnhookWindowsHookEx(
  HHOOK hhk   // handle to hook procedure
);

引數說明

  • hhk:   要被移除的訊息鉤子

編寫訊息鉤子

流程圖:

  • 程序鉤子

  • 全域性鉤子

程式碼樣例:

程序鉤子

程式原始碼:

#include<windows.h>
#include<string.h>

HHOOK g_hKeyboard=NULL;
HHOOK g_hMouse=NULL;

LRESULT CALLBACK MouseProc(
  int nCode,      // hook code
  WPARAM wParam,  // message identifier
  LPARAM lParam   // mouse coordinates
)
{
    return 1;
}

LRESULT CALLBACK KeyboardProc(
  int code,       // hook code
  WPARAM wParam,  // virtual-key code
  LPARAM lParam   // keystroke-message information
)
{
    //當按下回車鍵時候取消訊息鉤子
    if(VK_RETURN==wParam)
    {
        UnhookWindowsHookEx(g_hKeyboard);
        UnhookWindowsHookEx(g_hMouse);
        MessageBox(NULL,"取消訊息鉤子","訊息",MB_OK);
    }
    return 1;
}

LRESULT CALLBACK textprom(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);


int WINAPI WinMain(  HINSTANCE hInstance,  // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,      // pointer to command line
  int nCmdShow          // show state of window
  )
{
    WNDCLASS wndclass;
    MSG msg;

    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra=0;
    wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
    wndclass.hInstance=hInstance;
    wndclass.lpfnWndProc=textprom;
    wndclass.lpszClassName="text";
    wndclass.lpszMenuName=NULL;
    wndclass.style=CS_HREDRAW | CS_VREDRAW;

    if(!RegisterClass(&wndclass))
    {
        MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
    }

    HWND hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT,
        400,250,NULL,NULL,hInstance,NULL);

    ShowWindow(hwnd,nCmdShow);
    UpdateWindow(hwnd);

    //設定滑鼠和鍵盤訊息鉤子
    g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());
    g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId());
    
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK textprom(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch(uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        ;
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

執行結果:

全域性鉤子

動態連結庫原始碼:

// .h 標頭檔案 -----------------------------------------------------
#ifndef DLL_API
#define DLL_API _declspec(dllimport)
#endif
#include"windows.h"

HHOOK g_hMouse=NULL;
HHOOK g_hKeyboard=NULL;

DLL_API void SetHook();
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam);


// .cpp 源程式 ---------------------------------------------------
#define DLL_API _declspec(dllexport)
#include"dllHook.h"

LRESULT CALLBACK MouseProc(
  int nCode,      // hook code
  WPARAM wParam,  // message identifier
  LPARAM lParam   // mouse coordinates
)
{
    return 1;
}

LRESULT CALLBACK KeyboardProc(
  int code,       // hook code
  WPARAM wParam,  // virtual-key code
  LPARAM lParam   // keystroke-message information
)
{
    if(VK_RETURN==wParam)
    {
        UnhookWindowsHookEx(g_hMouse);
        UnhookWindowsHookEx(g_hKeyboard);
        MessageBox(NULL,"取消訊息鉤子","訊息",MB_OK);
    }
    return 1;
}

void SetHook()
{
    g_hMouse=SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("dllHook.dll"),0);
    g_hKeyboard=SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,GetModuleHandle("dllHook.dll"),0);
}

程式原始碼: 

#include<windows.h>
//載入動態連線庫標頭檔案
#include"../dllHook/dllHook.h"
//載入動態連線庫的引入庫(LIB)
#pragma comment(lib, "../debug/dllHook.lib")


LRESULT CALLBACK textprom(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
);


int WINAPI WinMain(  HINSTANCE hInstance,  // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,      // pointer to command line
  int nCmdShow          // show state of window
  )
{
    WNDCLASS wndclass;
    MSG msg;

    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra=0;
    wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndclass.hIcon=LoadIcon(NULL,IDI_ERROR);
    wndclass.hInstance=hInstance;
    wndclass.lpfnWndProc=textprom;
    wndclass.lpszClassName="text";
    wndclass.lpszMenuName=NULL;
    wndclass.style=CS_HREDRAW | CS_VREDRAW;

    if(!RegisterClass(&wndclass))
    {
        MessageBox(NULL,"create windows error!","error",MB_OK | MB_ICONSTOP);
    }

    HWND hwnd=CreateWindow("text","hellow world",WS_DLGFRAME | WS_MINIMIZEBOX | WS_SYSMENU,CW_USEDEFAULT,CW_USEDEFAULT,
        400,250,NULL,NULL,hInstance,NULL);

    ShowWindow(hwnd,nCmdShow);
    UpdateWindow(hwnd);
    
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK textprom(
  HWND hwnd,      // handle to window
  UINT uMsg,      // message identifier
  WPARAM wParam,  // first message parameter
  LPARAM lParam   // second message parameter
)
{
    HDC hdc;
    PAINTSTRUCT ps;
    RECT rect;

    switch(uMsg)
    {
    case WM_LBUTTONDOWN:
        //設定訊息鉤子
        SetHook();
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        ;
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

執行結果(回車錢所有滑鼠事件和鍵盤事件都將被遮蔽)

定時器

[相關函式][編寫定時器]

相關函式

[建立定時器][刪除定時器]

建立新定時器 SetTimer

函式原型:

UINT SetTimer(
HWND hWnd, 
UINT nIDEvent, 
UINT uElapse, 
TIMERPROC lpTimerFunc ); 

引數說明:

  • hWnd:      指定定時器的關聯視窗
  • nIDEvent:   指定了不為零的定時器識別符號.
  • nElapse:     指定了定時值;以毫秒為單位.
  • lpfnTimer:   指定了應用程式提供的TimerProc回撥函式的地址,該函式被用於處理WM_TIMER訊息.如果這個引數為NULL,則WM_TIMER訊息被放入應用程式的訊息佇列並由CWnd物件來處理.

返回值

如果函式成功,則返回新定時器的識別符號.應用程式可以將這個值傳遞給KillTimer成員函式以銷燬定時器.如果成功,則返回非零值;否則返回0

說明:

  • 所屬類    CWnd
  • 相對事件   WM_TIMER
  • 這個函式設定一個系統定時器.指定了一個定時值,每當發生超時,則系統就向設定定時器的應用程式的訊息佇列傳送一個WM_TIMER訊息,或者將訊息傳遞給應用程式定義的TimerProc回撥函式.lpfnTimer回撥函式不需要被命名為TimerProc,但是它必須按照如下方式定義:
    void CALLBACK EXPORT TimerProc(
      HWND hWnd, // 呼叫SetTimer的視窗的控制代碼
      UINT nMsg, // WM_TIMER 
      UINT nIDEvent // 定時器標識
      DWORD dwTime // 系統時間 
    );
    定時器是有限的全域性資源;因此對於應用程式來說,檢查SetTimer返回的值以確定定時器是否可用是很重要的

消除已存在定時器

函式原型

BOOL KillTimer( int nIDEvent );

引數說明

  • nIDEvent:   傳遞給SetTimer的定時器事件值

返回值

指定了函式的結果.如果事件已經被銷燬,則返回值為非零值.如果KillTimer成員函式不能找到指定的定時器事件,則返回0

說明 

  • 所屬類 CWnd
  • 銷燬以前呼叫SetTimer建立的用nIDEvent標識的定時器事件.任何與此定時器有關的未處理的WM_TIMER訊息都從訊息佇列中清除

編寫定時器

流程圖:

程式碼樣例:

程式原始碼:

#include"windows.h"
#include<cstdlib>
#include<iostream>
using namespace std;

VOID CALLBACK TimerProc(HWND hwnd,UINT msg ,UINT_PTR dwEvent,DWORD dwTime);

void main()
{
    MSG msg;

    //建立定時器
    UINT TimerID = SetTimer(NULL,1,1000,(TIMERPROC)TimerProc);

    //訊息迴圈
    while(GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    system("pause");
}

VOID CALLBACK TimerProc(HWND hwnd,UINT msg ,UINT_PTR dwEvent,DWORD dwTime)
{
    cout<<"定時器訊息回撥函式呼叫成功"<<endl;
    
    //關閉定時器
    if(KillTimer(NULL,dwEvent))
        PostQuitMessage(0);
}

執行結果:

相關推薦

訊息鉤子定時(VC_Win32)

訊息鉤子 [概述][相關函式][編寫訊息鉤子] 概述 鉤子過程 作業系統在傳遞訊息時,將我們感興趣的訊息先傳遞給HOOK過程,在此函式中進行檢查,然後在決定是否放行該訊息,就好像逃犯在逃亡時可能會經過許多段路段,為了抓住他,警察要在某些地方設定檢查站,以便檢查過往的車輛和行

MySQL5-函數/存儲過程定時、觸發器

名稱 狀態 訪問 safe 安全問題 ant comm gnu led 目錄 一、函數/存儲過程 二、定時器 三、觸發器 四、函數語句學習 一、函數/存儲過程 1、函數與存儲過程 (1) function與procedure的區別:一個有返回值,一個沒有,僅此而已。

MySQL觸發器定時的介紹和錯誤處理

MySQL觸發器與定時器的介紹和錯誤處理方法 最近在做一個東南亞的海外專案,整個專案的技術架構是由我負責,由於專案比較龐大,涉及三種語言,資料關係比較複雜,用的觸發器、定時器比較多。借這個新型大專案,也重溫了了很久沒有接觸的觸發器(TRIGGER)、定時器(EVENT),本文也是回憶結合

從零開始的全棧工程師——js篇2.14(表單定時

一、表單 Form input select textarea type=”radio/checkbox/password/button/text/submit/reset/”   表單的事件 onchange  當表單內容被修改時觸發的事件 onfocus  獲取

STC51微控制器中斷定時配置參考

*外中斷INT0--------void intsvr0(void) interrupt 0 using 1 *定時/計數器T0-----void timer0(void) interrupt 1 using 1 *外中斷INT1--------void intsvr0(void)

利用隨機數定時做一個簡單的偽隨機抓鬮遊戲

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> *

iOS -- GCD之延遲定時

我們開發常用的定時器有三種:NSTimer,CADisplyLink,CGD 本文詳細說一下CGD的延遲與定時器方法。文章最後,也會說說GCD與前兩個的區別。 直接上程式碼,首先是延遲的程式碼: 其中的delayInSeconds就是延遲的時間,執行之後,輸出臺會在2s之後,列

高效能網路程式設計6--reactor反應堆定時管理

反應堆開發模型被絕大多數高效能伺服器所選擇,上一篇所介紹的IO多路複用是它的實現基礎。定時觸發功能通常是伺服器必備元件,反應堆模型往往還不得不將定時器的管理囊括在內。本篇將介紹反應堆模型的特點和用法。首先我們要談談,網路程式設計界為什麼需要反應堆?有了IO複用,有了epoll

JavaScript時鐘定時

1.時鐘 例子:時鐘 <!doctype html><html><head><meta charset="utf-8"><title>clock</title><script type="text/javascript"&

react生命週期API(3.0)及生命週期定時的用法;

react的定時器的呼叫必須採用元件生命週期函式去呼叫: 有關元件的生命週期,見菜鳥教程: http://www.runoob.com/react/react-component-life-cycle

AJAX教程系列五:非同步資料獲取定時

window.onload = function() { var oBtn = document.getElementById('btn'); oBtn.onclick = function() { /*ajax({ url : 'getN

timer和ScheduledExecutorService延時定時的使用

直接上程式碼: Timer timer = new Timer(); int interval = 10; //時間間隔 ,毫秒 timer.schedule(new TimerTask(){    public void run(){      //邏輯處理程式碼   

mysql下儲存過程定時

一年前,第一次接觸資料庫的時候。發現專案上的一個表很神奇。當用戶提交資料的時候,狀態為0(表示未執行)。過了一陣子狀態為1(表示正在執行),又過一陣子狀態為2(表示執行成功)或者3(表示執行失敗)。 第一次接觸程式設計的小白感覺很神奇。因為神奇,就會以為特別複

Linux訊號實踐(5) --時間定時

三種不同精度的睡眠1.sleep#include <unistd.h> unsigned int sleep(unsigned int seconds);RETURN VALUE   Zer

定時TATE應用

pre reg .cn err 開始 main 定時 .com spa 1 #include <reg51.h> 2 #include <stdio.h> 3 #define uchar unsigned char 4 sbit le

GCD dispatch_source基本使用,創建GCD定時NSTimer的區別

pre 任務調度 cnblogs class -s ping log glob ... 可以使用GCD創建定時器 創建定時器: dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PR

JavaScript定時 setTimeoutsetInterval 淺析

throttle tin 並不是 tint 導致 語句 運行 應用 超過 一、 前情提要   1)JavaScript 是運行在單線程的執行環境中的   2)由瀏覽器安排事件的執行順序 二、setTimeout   使用場景: 設定代碼在未來的某個時間執行,而執行的時機是不

node中定時, process.nextTick(), setImediate()的區別聯系

con ron 十分 入隊 interval 延遲 聯系 一個 實現類 1.定時器   setTimeout()和setInterval()與瀏覽器中的API是一致的,定時器的問題在於,他並非精確的(在容忍範圍內)。盡管事件循環十分快,但是如果某一次循環占用的時間較多,那麽

並發編程 - 線程 - 1.互斥鎖/2.GIL解釋鎖/3.死鎖遞歸鎖/4.信號量/5.Event事件/6.定時

級別 src 總結 alex post strip CQ bsp 回收機制 1.互斥鎖: 原理:將並行變成串行 精髓:局部串行,只針對共享數據修改 保護不同的數據就應該用不用的鎖 1 from threading import Thread

定時在console中打印內容

IT char brush console asd true alert 內容 什麽 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">