各位DX,TX,我想做一个健康键盘输入的程序,想只用钩子。我在创建窗口时候装了一个钩子,代码如下:
KBHook:=SetWindowsHookEx(WH_KEYBOARD,
           {callback —>} @KeyboardHookProc,
                          HInstance,
                          0) ;回调函数我是这样写的:
function KeyboardHookProc(Code: Integer; WordParam: Word; LongParam: LongInt) : LongInt;
var
  i:Integer;
begin
if(GetKeyState(WordParam) and $8000) = $8000 then
begin
case WordParam of
  vk_Space: Form1.Label1.Caption:=Form1.Label1.Caption+IntToStr(0);
  vk_Right: Form1.Label1.Left:=Form1.Label1.Left + 5;
  vk_Left: Form1.Label1.Left:=Form1.Label1.Left - 5;
  vk_Up: Form1.Label1.Top:=form1.Label1.Top - 5;
  vk_Down: Form1.Label1.Top:=form1.Label1.Top + 5;
end; {case}
end;    目前的情况是只要焦点在本程序,label1就可以随着键盘的输入移动。但只要焦点切换到其他程序,再按方向键,就会把当前焦点的程序或者窗口结束掉。我把SetWindowsHookEx的HInstance改为0后,不会影响到其他程序,但也捕获不到键盘输入了。
    还有,我注入钩子时候,杀毒软件会报。

解决方案 »

  1.   

    hook 没有用过, 这种跨进程的操作杀毒软件一般是会报病毒的, 可以把程序放到信任的列表中.
      

  2.   

    兄弟,你安装键盘钩子的方法不对,按照下面安装:
    KBHook:=SetWindowsHookEx(WH_KEYBOARD,
      {callback —>} @KeyboardHookProc,
      0,
      GetCurrentThreadId);这样其他窗口就不会关闭了。
    此外你没有再钩子会掉函数中加入:
    Result := CallNextHookEx(KBHook, Code, WordParam, LongParam);
    改一下你的代码:
      if code < 0 then
      begin
        Result := CallNextHookEx(KBHook, Code, WordParam, LongParam);
        Exit;
      end;
      if(GetKeyState(WordParam) and $8000) = $8000 then
      begin
        case WordParam of
          vk_Space: Form1.Label1.Caption:=Form1.Label1.Caption+IntToStr(0);
          vk_Right: Form1.Label1.Left:=Form1.Label1.Left + 5;
          vk_Left: Form1.Label1.Left:=Form1.Label1.Left - 5;
          vk_Up: Form1.Label1.Top:=form1.Label1.Top - 5;
          vk_Down: Form1.Label1.Top:=form1.Label1.Top + 5;
        end; {case}
      end;
      Result := CallNextHookEx(KBHook, Code, WordParam, LongParam);但是当这个窗体失去焦点后,钩子就不起作用了,如果你想这个窗体失去焦点的时候也是生效,要使用全局钩子,必须把这个钩子放到dll中。
     
      

  3.   

    谢谢AnSunny,请问怎么把钩子放到DLL中?
    还有我实际上是想监控F12是否按下,是否还有其他不会被杀毒软件查杀的办法?用消息,directx怎样?
      

  4.   

    还有一个方法就是注册热键,也是最简单的一个方法,比钩子好用。呵呵。下面教你一招:不过这个在你检测F12的时候必须同时按下:ctrl或者shift或者alt也就是说:按住ctrl + f12或者alt+f12或者shift+f12才可以:
    在你的form类里面添加一个消息处理函数:
    procedure hotkey(var msg:tmessage);message wm_hotkey;实现:
    procedure TForm1.hotkey(var msg:tmessage);
    begin
      if msg.LParamHi = VK_F12 then
      begin
        //你的处理代码
      end;
    end;
    声明一个全局变量:
    atom: TAtom;(要把windows.pas use进来)
    然后在你的create函数里面加上:
    atom := GlobalAddAtom('My_HOT_KEY');
    RegisterHotKey(Handle, atom, MOD_CONTROL, VK_F12);//(MOD_CONTROL的意思就是按下ctrl键)
    最后再你的close事件里面加上
    GlobalDeleteAtom(atom);就ok了,呵呵呵
      

  5.   

    再次感谢AnSunny我的程序是一个业务程序的外挂,主要是检测到有提交信息后(F12)进行一些处理。所以可能不方便注册热键。
      

  6.   

    新建一个unitunit HookType;interface
      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;
        ReadPos : Integer;
      end;
      PShared=^TShared;
    implementationend.
    //下面是动态库源码,你可以根据你自己的情况修改
    library HookDll;{ Important note about DLL memory management: ShareMem must be the
      first unit in your library's USES clause AND your project's (select
      Project-View Source) USES clause if your DLL exports any procedures or
      functions that pass strings as parameters or function results. This
      applies to all strings passed to and from your DLL--even those that
      are nested in records and classes. ShareMem is the interface unit to
      the BORLNDMM.DLL shared memory manager, which must be deployed along
      with your DLL. To avoid using BORLNDMM.DLL, pass string information
      using PChar or ShortString parameters. }uses
      SysUtils,
      Classes,
      Windows,
      Generics.Collections,
      HookType in 'HookType.pas';{$R *.res}const
      KeyPressMask = $80000000;
    var
      MemFile,HookMutex : THandle;
      hOldKeyHook : Cardinal;
      ProcSaveExit : Pointer;
      Shared : PShared;
      FormHandle: THandle;//键盘钩子过滤函数
    function KeyHookProc(iCode: Integer; wParam: WPARAM ; lParam: LPARAM):LRESULT ; stdcall; export;
    begin
      if iCode < 0 then
        Result := CallNextHookEx(hOldKeyHook, iCode, wParam, lParam)
      else
      begin
        if (lparam and $80000000) = 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 ; stdcall; export;
    begin
      Shared^.KeyCount := 0;
      Shared^.ReadPos := 0;
      if hOldKeyHook=0 then
      begin
        hOldKeyHook := SetWindowsHookEx(WH_KEYBOARD,
        KeyHookProc,
        HInstance,
        0);
      end;
      Result := (hOldKeyHook <> 0);
    end;//撤消钩子过滤函数
    function DisableKeyHook: BOOL ; stdcall; export;
    begin
      if hOldKeyHook<> 0 then
      begin
        UnHookWindowsHookEx(hOldKeyHook); // 解除 Keyboard Hook
        hOldKeyHook:= 0;
        Shared^.KeyCount := 0;
        Shared^.ReadPos := 0;
      end;
      Result := (hOldKeyHook = 0);
    end;//取得键盘缓冲区中击键的个数
    function GetKeyCount :Integer ; stdcall; export;
    begin
      Result:= Shared^.KeyCount;
    end;//取得键盘缓冲区的键
    function GetKey() : char ; stdcall; export;
    begin
      if ((Shared^.KeyCount > 0) and (Shared^.ReadPos < Shared^.KeyCount)) or
         (Shared^.ReadPos > Shared^.KeyCount) then
      begin
        Result:=shared^.Keys[Shared^.ReadPos];
        Inc(Shared^.ReadPos);
        if Shared^.ReadPos >= BUFFER_SIZE then
          Shared^.ReadPos := 0;
      end
      else
      begin
        Result := #0;
      end;end;//清空键盘缓冲区
    procedure ClearKeyString ; stdcall; export;
    begin
      shared^.KeyCount := 0;
      Shared^.ReadPos := 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.
    使用方法:
    先EnableKeyHook();
    然后:
    你做一个定时器,时间设成10毫秒或者50毫秒的,短点儿,在定时器事件中
    用c := GetKey();取得Char,然后做你要做的事情就行了。退出的时候用DisableKeyHook;
      

  7.   

    十分感谢AnSunny,我已经知道改怎么做了。
    结贴了。