• Delphi 关于钩子函数HOOK (二)


    用DELPHI编制钩子函数
    Windows消息管理机构提供了能使应用程序访问控制消息流
    所谓的钩子(HOOK)机制。钩子有多种,分别用于捕获某一特定类型或某一范围的消息。如:键盘消息,鼠标消息等。
    我们这里仅以键盘钩子的使用为例,讨论在DELPHI下怎样编写DLL程序和怎样在自己的程序中安装使用键盘钩子函数,
    并讨论了不同程序使用同一DLL文件时怎样共享数据。
    一、 钩子过滤函数的编写说明
    由于钩子过滤函数必须在独立的模块中,也就是说我们必须首先生成一个DLL框架,然后再在其中加入钩子函数代码
    以及其他相关函数代码。我们这里以键盘钩子过滤函数的编写为例来说明。具体步骤如下:
    1、先生成一个DLL框架
    2、编写自己的键盘钩子过滤函数
    钩子过滤函数必须是回调函数,其函数的声明为:
    function KeyHookProc(
    iCode:Integer;
    wParam:WPARAM;
    lParam:LPARAM ) : LRESULT; stdcall ;export ;
    在生成的DLL框架中加入自己的键盘钩子处理函数处理键盘消息。
    代码如下:…
    if(iCode>=0) then begin
    Result:=0; //初始化返回值
    // 在这里加入自己的代码
    end else
    begin
    Result:=CallNextHook(hOldKeyHook
    iCode
    wParam
    lParam);
    // hOldKeyHook是保存的原键盘过滤函数
    end;
    3、 安装键盘钩子过滤函数
    为安装一个钩子键盘过滤函数应调用SetWindowsHookEx函数(适用于Windows3.0的SetWindowsHook钩子安装函数现在
    已经废弃不用)。该函数的原形如下:
    HHOOK SetWindowsHookEx(
    int idHook
    // 安装的钩子类型
    HOOKPROC lpfn
    // 钩子过滤函数地址
    HINSTANCE hMod
    // 任务句柄
    DWORD dwThreadId // 钩子用于的目的
    );
    需要说明的是:通常应该调用MakeProcInstance函数以获取一个输出函数的前导码的入口地址,再将此地址作为
    SetWindowsHookEx的第二个参数lpfn。但由于Delphi提供了"灵巧调用(smart callback)",使得MakeProcInstance
    可以省去,而直接将钩子过滤函数名用作入口地址。
    这样当应用程序用GetMessage或PeekMessage函数从消息队列中读消息或有按键消息(WM_KEYDOWN或WM_KEYUP)要处理时,
    系统就要调用钩子过滤函数KeyHookProc处理键盘消息。
    4、 卸载钩子过滤函数。
    当钩子函数不再需要时,应调用UnHookWindowsHookProc卸载安装的钩子以释放系统资源。
    完整的程序清单如下
    Library KEYHOOK;
    uses Windows;
    const BUFFER_SIZE=16*1024;
    const HOOK_MEM_FILENAME='SAMPLE KEY_HOOK_MEM_FILE';
    const HOOK_MUTEX_NAME ='SAMPLE KEY_HOOK_MUTEX_NAME';
    type
    TShared=record
    Keys : array[0..BUFFER_SIZE] of Char;
    KeyCount : Integer;
    end;
    PShared=^TShared;
    var
    MemFile
    HookMutex : THandle;
    hOldKeyHook : HHook;
    ProcSaveExit : Pointer;
    Shared : PShared;
    //键盘钩子过滤函数
    function KeyHookProc(iCode: Integer; wParam: WPARAM ; lParam: LPARAM):LRESULT
    ; stdcall; export;
    const KeyPressMask = $80000000;
    begin
    if iCode < 0 then
    Result := CallNextHookEx(hOldKeyHook
    iCode
    wParam
    lParam)
    else begin
    if ((lParam and KeyPressMask)= 0) then // 键按下
    begin
    Shared^.Keys[Shared^.KeyCount]:=Char(wParam and $00ff);
    Inc(Shared^.KeyCount);
    if Shared^.KeyCount>=BUFFER_SIZE-1 then Shared^.KeyCount:=0;
    end;
    iCode:=-1;
    Result := CallNextHookEx(hOldKeyHook
    iCode
    wParam
    lParam);
    end;
    end;
    // 设置钩子过滤函数
    function EnableKeyHook : BOOL ; export;
    begin
    Shared^.KeyCount:=0; //初始化键盘指针
    if hOldKeyHook=0 then begin
    hOldKeyHook := SetWindowsHookEx(WH_KEYBOARD
    KeyHookProc
    HInstance
    0);
    end;
    Result := (hOldKeyHook 0);
    end;
    //撤消钩子过滤函数
    function DisableKeyHook: BOOL ; export;
    begin
    if hOldKeyHook 0 then
    begin
    UnHookWindowsHookEx(hOldKeyHook); // 解除 Keyboard Hook
    hOldKeyHook:= 0;
    Shared^.KeyCount:=0;
    end;
    Result := (hOldKeyHook = 0);
    end;
    //取得键盘缓冲区中击键的个数
    function GetKeyCount :Integer ; export;
    begin
    Result:=Shared^.KeyCount;
    end;
    //取得键盘缓冲区的键
    function GetKey(index:Integer) : Char ; export;
    begin
    Result:=Shared^.Keys[index];
    end;
    //清空键盘缓冲区
    procedure ClearKeyString ; export;
    begin
    Shared^.KeyCount:=0;
    end;
    //DLL的退出处理过程
    procedure KeyHookExit; far;
    begin
    if hOldKeyHook 0 then DisableKeyHook;
    UnMapViewOfFile(Shared); // 释放内存映象文件
    CloseHandle(MemFile); // 关闭映象文件
    ExitProc := ProcSaveExit;
    end;
    exports // 定义输出函数
    EnableKeyHook
    DisableKeyHook
    GetKeyCount
    ClearKeyString
    GetKey;
    begin
    // DLL 初始化部分
    HookMutex:=CreateMutex(nil
    True
    HOOK_MUTEX_NAME);
    // 通过建立内存映象文件以共享内存
    MemFile:=OpenFileMapping(FILE_MAP_WRITE
    False
    HOOK_MEM_FILENAME);
    if MemFile=0 then
    MemFile:=CreateFileMapping($FFFFFFFF
    nil
    PAGE_READWRITE
    0
    SizeOf(TShared)
    HOOK_MEM_FILENAME);
    Shared:=MapViewOfFile(MemFile
    File_MAP_WRITE
    0
    0
    0);
    ReleaseMutex(HookMutex);
    CloseHandle(HookMutex);
    ProcSaveExit := ExitProc; // 保存DLL的ExitProc
    ExitProc := @KeyHookExit; // 设置DLL新的ExitProc
    end.
    // 源代码结束
    二、 在自己的程序中使用编制好的键盘钩子过滤函数。
    钩子函数编制好后,使用起来其实很简单:首先调用SetWindowsHookEx安装自己的钩子过滤函数,
    同时保存原先的钩子过滤函数地址。这时钩子函数就开始起作用了,它将按照你的要求处理键盘消息。
    程序运行完毕或不再需要监视键盘消息时,调用UnHookWindowsHookProc函数卸载所安装的钩子函数,
    同时恢复原来的钩子过滤函数地址。
    下面就是使用在以上编制的钩子函数的例子:
    unit Unit1;
    interface
    uses
    Windows
    Messages
    SysUtils
    Classes
    Graphics
    Controls
    Forms
    Dialogs
    StdCtrls
    ExtCtrls;
    type
    TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    bSetHook: TButton;
    bCancelHook: TButton;
    bReadKeys: TButton;
    bClearKeys: TButton;
    Panel2: TPanel;
    procedure bSetHookClick(Sender: TObject);
    procedure bCancelHookClick(Sender: TObject);
    (36

    procedure bReadKeysClick(Sender: TObject);
    procedure bClearKeysClick(Sender: TObject);
    end;
    var Form1: TForm1;
    implementation
    {$R *.DFM}
    function EnableKeyHook : BOOL ; external 'KEYHOOK.DLL';
    function DisableKeyHook : BOOL ; external 'KEYHOOK.DLL';
    function GetKeyCount : Integer ; external 'KEYHOOK.DLL';
    function GetKey(idx:Integer) : Char ; external 'KEYHOOK.DLL';
    procedure ClearKeyString ; external 'KEYHOOK.DLL';
    procedure TForm1.bSetHookClick(Sender: TObject); // 设置键盘钩子
    begin
    EnableKeyHook;
    bSetHook.Enabled :=False;
    bCancelHook.Enabled:=True;
    bReadKeys.Enabled :=True;
    bClearKeys.Enabled :=True;
    Panel2.Caption:=' 键盘钩子已经设置';
    end;
    procedure TForm1.bCancelHookClick(Sender: TObject); // 卸载键盘钩子
    begin
    DisableKeyHook;
    bSetHook.Enabled :=True;
    bCancelHook.Enabled:=False;
    bReadKeys.Enabled :=False;
    bClearKeys.Enabled :=False;
    Panel2.Caption:=' 键盘钩子没有设置';
    end;
    procedure TForm1.bReadKeysClick(Sender: TObject); // 取得击键的历史记录
    var i:Integer;
    begin
    Memo1.Lines.Clear; // 在Memo1中显示击键历史记录
    for i:=0 to GetKeyCount-1 do
    Memo1.Text:=Memo1.Text+GetKey(i);
    end;
    procedure TForm1.bClearKeysClick(Sender: TObject); // 清除击键历史记录
    begin
    Memo1.Clear;
    ClearKeyString;
    end;
    end.
    // 源代码结束
    三、 Windows95下DLL中实现共享内存
    在上面的钩子函数所在的DLL文件中,需要使用共享内存,即,所有击键的记录存储在同一个数据段中。
    为什么要这样做呢?这是因为Windows95的DLL调用方法与Windows3.X的方法不同。每个进(线)程在登
    录某动态连接库时都会为该动态连接库传入一个新的实例句柄(即DLL数据段的句柄)。这使得DLL各个
    实例之间互不干扰,但是这对那些所有DLL实例共享一组变量带来一些困难。为了解决这个问题,我们
    在这儿通过建立内存映射文件的方法来解决。即使用Windows的OpenFileMapping、CreateFileMapping和
    MapViewOfFile三个函数来实现。使用方法如下:

    MemFile是THandle类型,Shared是指针类型,HOOK_MEM_FILENAME是一常量串

    MemFile:=OpenFileMapping(FILE_MAP_WRITE
    False
    HOOK_MEM_FILENAME); //打开内存映射文件
    if MemFile=0 then //打开失败则创建内存映射文件
    MemFile:=CreateFileMapping($FFFFFFFF
    nil
    PAGE_READWRITE
    0
    SizeOf(TShared)
    HOOK_MEM_FILENAME);
    //映射文件到变量
    Shared:=MapViewOfFile(MemFile
    File_MAP_WRITE
    0
    0
    0);
    到此为止,你已经知道用Delphi编制钩子函数有多么容易。最后不得不提醒大家:钩子函数虽然功能比较强,
    但如果使用不当将会严重影响系统的效率,所以要尽量避免使用系统钩子。非要使用不可时也应该格外小心,
    应使之尽可能小地影响系统的运行。
    Delphi - 关于钩子函数HOOK (5)
    建立键盘鼠标动作记录与回放
    内容:很多的教学软件或系统监视软件可以自动记录回放用户的输入文字或点击按钮等操作操作,
    这个功能的实现是使用了Windows的Hook函数。
    Windows提供API函数SetwindowsHookEx来建立一个Hook,通过这个函数可以将一个程序添加到Hook链中监视Windows消息,函数语法为:
    SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD)
    其中参数idHook指定建立的监视函数类型。通过Windows MSDN帮助可以看到,SetwindowsHookEx函数提供15
    种不同的消息监视类型,在这里我们将使用WH_JOURNALRECORD和WH_JOURNALPLAYBACK来监视键盘和鼠标操作。
    参数lpfn指定消息函数,在相应的消息产生后,系统会调用该函数并将消息值传递给该函数供处理。函数的一般形式为:
    Hookproc (code: Integer; wparam: WPARAM; lparam: LPARAM): LRESULT stdcall;
    其中code为系统指示标记,wParam和lParam为附加参数,根据不同的消息监视类型而不同。只要在程序中建立这样
    一个函数再通过SetwindowsHookEx函数将它加入到消息监视链中就可以处理消息了。
    在不需要监视系统消息时需要调用提供UnHookWindowsHookEx来解除对消息的监视。
    WH_JOURNALRECORD和WH_JOURNALPLAYBACK类型是两种相反的Hook类型,前者获得鼠标、键盘动作消息,后者回放鼠标键盘消息。
    所以在程序中我们需要建立两个消息函数,一个用于纪录鼠标键盘操作并保存到一个数组中,另一个用于将保存的操作返给系统回放。
    下面来建立程序,在Delphi中建立一个工程,在Form1上添加3个按钮用于程序操作。另外再添加一个按钮控件和一个Edit控件用于验证操作。
    下面是Form1的全部代码
    unit Unit1;
    interface
    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls;
    type
    TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit1: TEdit;
    Button4: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;
    var
    Form1: TForm1;
    EventArr:array[0..1000]of EVENTMSG;
    EventLog:Integer;
    PlayLog:Integer;
    hHook,hPlay:Integer;
    recOK:Integer;
    canPlay:Integer;
    bDelay:Bool;
    implementation
    {$R *.DFM}
    Function PlayProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
    begin
    canPlay:=1;
    Result:=0;
    if iCode < 0 then //必须将消息传递到消息链的下一个接受单元
    Result := CallNextHookEx(hPlay,iCode,wParam,lParam)
    else if iCode = HC_SYSMODALON then
    canPlay:=0
    else if iCode = HC_SYSMODALOFF then
    canPlay:=1
    else if ((canPlay =1 )and(iCode=HC_GETNEXT)) then begin
    if bDelay then begin
    bDelay:=False;
    Result:=50;
    end;
    pEventMSG(lParam)^:=EventArr[PlayLog];
    end
    else if ((canPlay = 1)and(iCode = HC_SKIP))then begin
    bDelay := True;
    PlayLog:=PlayLog+1;
    end;
    if PlayLog>=EventLog then begin
    UNHookWindowsHookEx(hPlay);
    end;
    end;
    function HookProc(iCode:Integer;wParam:wParam;lParam:lParam):LRESULT;stdcall;
    begin
    recOK:=1;
    Result:=0;
    if iCode < 0 then
    Result := CallNextHookEx(hHook,iCode,wParam,lParam)
    else if iCode = HC_SYSMODALON then
    recOK:=0
    else if iCode = HC_SYSMODALOFF then
    recOK:=1
    else if ((recOK>0) and (iCode = HC_ACTION)) then begin
    EventArr[EventLog]:=pEventMSG(lParam)^;
    EventLog:=EventLog+1;
    if EventLog>=1000 then begin
    UnHookWindowsHookEx(hHook);
    end;
    end;
    end;
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    Button1.Caption:='纪录';
    Button2.Caption:='停止';
    Button3.Caption:='回放';
    Button4.Caption:='范例';
    Button2.Enabled:=False;
    Button3.Enabled:=False;
    end;
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    EventLog:=0;
    //建立键盘鼠标操作消息纪录链
    hHook:=SetwindowsHookEx(WH_JOURNALRECORD,HookProc,HInstance,0);
    Button2.Enabled:=True;
    Button1.Enabled:=False;
    end;
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    UnHookWindowsHookEx(hHook);
    hHook:=0;
    Button1.Enabled:=True;
    Button2.Enabled:=False;
    Button3.Enabled:=True;
    end;
    procedure TForm1.Button3Click(Sender: TObject);
    begin
    PlayLog:=0;
    //建立键盘鼠标操作消息纪录回放链
    hPlay:=SetwindowsHookEx(WH_JOURNALPLAYBACK,PlayProc,
    HInstance,0);
    Button3.Enabled:=False;
    end;
    end.
    代码添加完毕后,运行程序,点击“纪录”按钮开始纪录操作,这时你可以在文本控件中输入一些文字或者点击
    “范例”按钮,然后点击“停止”按钮停止纪录,再点击“回放”按钮就可以讲先前所做的操作回放。
    在上面的程序中,HookProc是纪录操作的消息函数,每当有鼠标键盘消息发生时,系统都会调用该函数,
    消息信息就保存在地址lParam中,我们可以讲消息保存在一个数组中。PlayProc是消息回放函数,当系统可以
    执行消息回放时调用该函数,程序就将先前纪录的消息值返回到lParam指向的区域中,系统就会执行该消息,
    从而实现了消息回放。
    Delphi - 关于钩子函数HOOK (6)
    以下例程可以实现禁止用户用ALT+TAB或ALT+ESCAPE键切换程序,并且可以屏蔽左右windows键:
    unit Unit1;
    interface
    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls;
    type
    TForm1 = class(TForm)
    btn1: TButton;
    btn2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    private
    { Private declarations }
    public
    { Public declarations }end;
    {按键消息的结构,Delphi中也没有,自己定义吧。这也就我为什么说用C写
    这样的程序更好的原因之一。还必须注意的是这个结构在Windows NT 4 sp3以上系统
    中才能使用}
    tagKBDLLHOOKSTRUCT = packed record
    vkCode: DWORD;//虚拟键值
    scanCode: DWORD;//扫描码值(没有用过,我也不懂^_^)
    {一些扩展标志,这个值比较麻烦,MSDN上说得也不太明白,但是
    根据这个程序,这个标志值的第六位数(二进制)为1时ALT键按下为0相反。}
    flags: DWORD;
    time: DWORD;//消息时间戳
    dwExtraInfo: DWORD;//和消息相关的扩展信息
    end;
    KBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT;
    PKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT;
    //这个是低级键盘钩子的索引值,Delphi中没有,必须自己定义
    const WH_KEYBOARD_LL = 13;
    //定义一个常量好和上面哪个结构中的flags比较而得出ALT键是否按下
    const LLKHF_ALTDOWN = $20;
    var
    Form1: TForm1;
    hhkLowLevelKybd: HHOOK;
    implementation
    {$R *.dfm}
    {
    功能:低级键盘钩子的回调函数,在里面过滤消息
    参数:nCode 是Hook的标志
    WParam 表示消息的类型
    LParam 是一个指向我们在上面定义的哪个结构KBDLLHOOKSTRUCT的指针
    返回值:如果不是0的话windows就把这个消息丢掉,程序就不会再收到这个消息了。
    }
    function LowLevelKeyboardProc(nCode: Integer;
    WParam: WPARAM;LParam: LPARAM):LRESULT; stdcall;
    var
    fEatKeystroke: BOOL;
    p: PKBDLLHOOKSTRUCT;
    begin
    Result := 0;
    fEatKeystroke := FALSE;
    p := PKBDLLHOOKSTRUCT (lParam);
    //nCode值为HC_ACTION时表示WParam和LParam参数包涵了按键消息
    if (nCode = HC_ACTION) then
    begin
    //拦截按键消息并测试是否是左windows、右windows、Ctrl+Esc、Alt+Tab、和Alt+Esc功能键。
    case wParam of
    WM_KEYDOWN,
    WM_SYSKEYDOWN,
    WM_KEYUP,
    WM_SYSKEYUP:
    fEatKeystroke :=
    (p.vkCode = VK_rwin) or (p.vkCode = VK_lwin) or
    ((p.vkCode = VK_TAB) and ((p.flags and LLKHF_ALTDOWN) 0)) or
    ((p.vkCode = VK_ESCAPE) and ((p.flags and LLKHF_ALTDOWN) 0)) or
    ((p.vkCode = VK_ESCAPE) and ((GetKeyState(VK_CONTROL) and $8000) 0));
    end;
    end;
    if fEatKeystroke = True then
    Result := 1;
    if nCode 0 then
    Result := CallNextHookEx(0, nCode, wParam, lParam);
    end;
    procedure TForm1.Button1Click(Sender: TObject);
    begin
    //设置低级键盘钩子
    if hhkLowLevelKybd = 0 then
    begin
    hhkLowLevelKybd := SetWindowsHookExW(WH_KEYBOARD_LL,
    LowLevelKeyboardProc, Hinstance, 0);
    if hhkLowLevelKybd 0 then
    MessageBox(Handle, '低级键盘钩子设置成功!', '提示', MB_OK)
    else
    MessageBox(Handle, '低级键盘钩子设置失败!', '提示', MB_OK);
    end
    else
    MessageBox(Handle, '低级键盘钩子已设置!', '提示', MB_OK);
    end;
    procedure TForm1.Button2Click(Sender: TObject);
    begin
    //卸载低级键盘钩子
    if hhkLowLevelKybd 0 then
    if UnhookWindowsHookEx(hhkLowLevelKybd) False then
    begin
    MessageBox(Handle, '低级键盘钩子卸载成功!', '提示', MB_OK);
    hhkLowLevelKybd := 0;
    end
    else
    MessageBox(Handle, '低级键盘钩子卸载失败!', '提示', MB_OK)
    else
    MessageBox(Handle, '没有发现低级键盘钩子!', '提示', MB_OK);
    end;
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
    //在Form关闭的时候检测,如果没有卸载钩子就卸载之
    if hhkLowLevelKybd 0 then
    UnhookWindowsHookEx(hhkLowLevelKybd);
    end;
    上面例程在WINXP和DELPHI 7.0中使用通过。
  • 相关阅读:
    要做好性能测试,该掌握些什么?
    <form> 标签
    javascript DOM编程艺术 第10章问题记录
    简单的图片显示
    fieldset 不常用的HTML标签
    XMLHttpRequest
    单选复选框的制作
    ASP.NET MVC 中使用“RadioButtonList”和“CheckBoxList”
    Web Service 系列 → 第一个 Hello Word
    .NET平台开发实例药店系统分布式部署
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/9594836.html
Copyright © 2020-2023  润新知