2016-07-13 36 views
-2

我正在編寫一個鍵盤應用程序,它鉤住鍵盤並重新映射鍵。爲此,我創建了兩個項目,一個是.exe和.dll。在.dll項目中,我檢測到用戶通過GetFocus()鍵入的窗口句柄。然而,它在記事本中工作正常,但不是在MS Word中,因爲我無法獲取MS Word的窗口句柄,使用GetFocus()GetFocus()爲MS Word返回空值

我明白,這是因爲它可能運行在不同的線程和因此,我需要通過GetForegroundWindow()獲取父窗口句柄,並遍歷其子窗口,並以某種方式獲得正確的句柄。 在互聯網上搜索,我發現下面的代碼(http://www.codeproject.com/Articles/34752/Control-in-Focus-in-Other-Processes

activeWindowHandle:= GetForegroundWindow(); 
activeWindowThread:= GetWindowThreadProcessId(activeWindowHandle, 0); 
thisWindowThread:= GetWindowThreadProcessId(lpHookRec^.TheHookHandle, 0); 
AttachThreadInput(activeWindowThread, thisWindowThread, true); 
lpHookRec^.TheAppWinHandle:= GetFocus(); 
AttachThreadInput(activeWindowThread, thisWindowThread, false); 

但是,它不是爲我工作:(

在我的代碼我已經寫

lpHookRec^.TheAppWinHandle := GetFocus(); 

也練就了我如果我使用MS Word而不是NotePad,上面的代碼給了我空(零),所以需要編寫函數來返回正確的Handle,而不管它正在運行的線程下,像

function GetAppliWinHandle: Hwnd; 
    var 
    activeWindowHandle,activeWindowThread,thisWindowThread,focusedControlHandle: Hwnd; 
    begin { GetAppliWinHandle } 
    focusedControlHandle := GetFocus(); 
    if focusedControlHandle = 0 then 
     begin 
     activeWindowHandle := GetForegroundWindow(); 
     activeWindowThread := GetWindowThreadProcessId(activeWindowHandle, 0); 
     thisWindowThread := GetWindowThreadProcessId(lpHookRec^.TheHookHandle, 0); 
     AttachThreadInput(activeWindowThread, thisWindowThread, true); 
     focusedControlHandle := GetFocus(); 
     AttachThreadInput(activeWindowThread, thisWindowThread, false); 
     end; 
    Result:=focusedControlHandle 
    end; { GetAppliWinHandle } 

,這裏是DLL的完整代碼

library TheHook; 

uses 
    Windows, Messages, SysUtils; 

{Define a record for recording and passing information process wide} 
type 
    PHookRec = ^THookRec; 
    THookRec = packed record 
    TheHookHandle: HHOOK; 
    TheAppWinHandle: HWND; 
    TheCtrlWinHandle: HWND; 
    TheKeyCount: DWORD; 
    end; 

var 
    hObjHandle: THandle; {Variable for the file mapping object} 
    lpHookRec: PHookRec; {Pointer to our hook record} 

procedure MapFileMemory(dwAllocSize: DWORD); 
begin 
    {Create a process wide memory mapped variable} 
    hObjHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, dwAllocSize, 
    'HookRecMemBlock'); 
    if (hObjHandle = 0) then 
    begin 
    MessageBox(0, 'Hook DLL', 'Could not create file map object', MB_OK); 
    exit; 
    end; 
    {Get a pointer to our process wide memory mapped variable} 
    lpHookRec := MapViewOfFile(hObjHandle, file_MAP_write, 0, 0, dwAllocSize); 
    if (lpHookRec = nil) then 
    begin 
    CloseHandle(hObjHandle); 
    MessageBox(0, 'Hook DLL', 'Could not map file', MB_OK); 
    exit; 
    end; 
end; 

procedure UnMapFileMemory; 
begin 
    {Delete our process wide memory mapped variable} 
    if (lpHookRec <> nil) then 
    begin 
    UnMapViewOfFile(lpHookRec); 
    lpHookRec := nil; 
    end; 
    if (hObjHandle > 0) then 
    begin 
    CloseHandle(hObjHandle); 
    hObjHandle := 0; 
    end; 
end; 

function GetHookRecPointer: pointer stdcall; 
begin 
    {Return a pointer to our process wide memory mapped variable} 
    result := lpHookRec; 
end; 

{The function that actually processes the keystrokes for our hook} 

function KeyBoardProc(Code: integer; wParam: integer; lParam: integer): integer; 
    stdcall; 
    function GetAppliWinHandle: Hwnd; 
    var 
    activeWindowHandle,activeWindowThread,thisWindowThread,focusedControlHandle: Hwnd; 
    begin { GetAppliWinHandle } 
    focusedControlHandle := GetFocus(); 
    if focusedControlHandle = 0 then 
     begin 
     activeWindowHandle := GetForegroundWindow(); 
     activeWindowThread := GetWindowThreadProcessId(activeWindowHandle, 0); 
     thisWindowThread := GetWindowThreadProcessId(lpHookRec^.TheHookHandle, 0); 
     AttachThreadInput(activeWindowThread, thisWindowThread, true); 
     focusedControlHandle := GetFocus(); 
     AttachThreadInput(activeWindowThread, thisWindowThread, false); 
     end; 
    Result:=focusedControlHandle 
    end; { GetAppliWinHandle } 

var 
    KeyUp: bool; 
    {Remove comments for additional functionability ... : 

    IsAltPressed: bool; 
    IsCtrlPressed: bool; 
    IsShiftPressed: bool; 
    } 
begin 
    result := 0; 
    case Code of 
    HC_ACTION: 
     begin 
     {We trap the keystrokes here} 
     {is this a key up message?} 
     KeyUp := ((lParam and (1 shl 31)) <> 0); 

     {Remove comments for additional functionability ... : 

     {is the Alt key pressed} 
     if ((lParam and (1 shl 29)) <> 0) then 
     begin 
      IsAltPressed := TRUE; 
     end 
     else 
     begin 
      IsAltPressed := FALSE; 
     end; 
     {is the Control key pressed} 
     if ((GetKeyState(VK_CONTROL) and (1 shl 15)) <> 0) then 
     begin 
      IsCtrlPressed := TRUE; 
     end 
     else 
     begin 
      IsCtrlPressed := FALSE; 
     end; 
     {if the Shift key pressed} 
     if ((GetKeyState(VK_SHIFT) and (1 shl 15)) <> 0) then 
     begin 
      IsShiftPressed := TRUE; 
     end 
     else 
     begin 
      IsShiftPressed := FALSE; 
     end; 
     } 

     {if KeyUp then increment the key count} 
     if (KeyUp <> FALSE) then 
     begin 
      Inc(lpHookRec^.TheKeyCount); 
     end; 
     case wParam of 
      {Was the enter key pressed?} 
      VK_RETURN: 
      begin 
       {if KeyUp} 
       if (KeyUp <> FALSE) then 
       begin 
       {Post a bogus message to the window control in our app} 
       PostMessage(lpHookRec^.TheCtrlWinHandle, WM_KEYDOWN, 0, 0); 
       PostMessage(lpHookRec^.TheCtrlWinHandle, WM_KEYUP, 0, 0); 
       end; 
       {if you wanted to swallow the keystroke then return -1, else if you 
want 
          to allow the keystroke then return 0} 
       result := 0; 
       exit; 
      end; {VK_RETURN} 
      {if the left arrow key is pressed then lets play a joke!} 
      VK_LEFT: 
      begin 
       {Get the Handle of the Application Window in lpHookRec^.TheAppWinHandle} 
      lpHookRec^.TheAppWinHandle:=GetAppliWinHandle; 

       {if KeyUp} 
       if (KeyUp <> FALSE) then 
       begin 
       {Create a UpArrow keyboard event} 
       keybd_event(VK_RIGHT, 0, 0, 0); 
       keybd_event(VK_RIGHT, 0, KEYEVENTF_KEYUP, 0); 
       end; 
       {Swallow the keystroke} 
       result := -1; 
       exit; 
      end; {VK_LEFT} 
     end; {case wParam} 
     {Allow the keystroke} 
     result := 0; 
     end; {HC_ACTION} 
    HC_NOREMOVE: 
     begin 
     {This is a keystroke message, but the keystroke message has not been removed 
      from the message queue, since an application has called PeekMessage() 
      specifying PM_NOREMOVE} 
     result := 0; 
     exit; 
     end; 
    end; {case code} 
    if (Code < 0) then 
    {Call the next hook in the hook chain} 
    result := CallNextHookEx(lpHookRec^.TheHookHandle, Code, wParam, lParam); 
end; 

procedure StartKeyBoardHook stdcall; 
begin 
    {if we have a process wide memory variable and the hook has not already been 
set...} 
    if ((lpHookRec <> nil) and (lpHookRec^.TheHookHandle = 0)) then 
    begin 
    {set the hook and remember our hook handle} 
    lpHookRec^.TheHookHandle := SetWindowsHookEx(WH_KEYBOARD, @KeyBoardProc, 
     hInstance, 0); 
    end; 
end; 

procedure StopKeyBoardHook stdcall; 
begin 
    {if we have a process wide memory variable and the hook has already been set...} 
    if ((lpHookRec <> nil) and (lpHookRec^.TheHookHandle <> 0)) then 
    begin 
    {Remove our hook and clear our hook handle} 
    if (UnHookWindowsHookEx(lpHookRec^.TheHookHandle) <> FALSE) then 
    begin 
     lpHookRec^.TheHookHandle := 0; 
    end; 
    end; 
end; 

procedure DllEntryPoint(dwReason: DWORD); 
begin 
    case dwReason of 
    Dll_Process_Attach: 
     begin 
     {if we are getting mapped into a process, then get a pointer to our 
       process wide memory mapped variable} 
     hObjHandle := 0; 
     lpHookRec := nil; 
     MapFileMemory(sizeof(lpHookRec^)); 
     end; 
    Dll_Process_Detach: 
     begin 
     {if we are getting unmapped from a process then, remove the pointer to 
       our process wide memory mapped variable} 
     UnMapFileMemory; 
     end; 
    end; 
end; 

exports 
    KeyBoardProc name 'KEYBOARDPROC', 
    GetHookRecPointer name 'GETHOOKRECPOINTER', 
    StartKeyBoardHook name 'STARTKEYBOARDHOOK', 
    StopKeyBoardHook name 'STOPKEYBOARDHOOK'; 

begin 
    {set our Dll's main entry point} 
    DLLProc := @DllEntryPoint; 
    {Call our Dll's main entry point} 
    DllEntryPoint(Dll_Process_Attach); 

end. 
+1

您將有更多的信息,如果您檢查API的結果。它可能與所謂的* virtualization *有關,如果它是一個點擊運行安裝。 –

+0

@SertacAkyuz當應用程序是MS Word(我猜這是因爲MDI),但在記事本中一切正常時,我從GetFocus()中獲得空值我想要一個可靠的方法/函數,它總是能夠給我當前應用程序的句柄 – bhattji

+0

GetFocus返回null,我們理解。你知道線是否連接?如果不是,GetFocus當然會返回null。這就是您檢查API結果的原因。 ...順便說一句,這是不明顯的問題你在問什麼.. –

回答

0

GetFocus()文檔:

檢索句柄具有鍵盤焦點,如果窗口該窗口被附加到調用線程的消息隊列

...

GetFocus返回與鍵盤焦點爲當前線程的消息隊列窗口。如果GetFocus返回NULL,則另一個線程的隊列可能會附加到具有鍵盤焦點的窗口

使用GetForegroundWindow函數可檢索用戶當前正在使用的窗口的句柄。您可以使用AttachThreadInput函數將線程的消息隊列與另一個線程擁有的窗口相關聯。

您正在嘗試執行此操作,但是您沒有正確執行此操作,並且您並未檢查一路上的錯誤。您也錯誤地使用線程ID的HWND數據類型,但它們不是HWND s,而是DWORD s。

嘗試更多的東西是這樣的:

function GetAppliWinHandle: HWND; 
var 
    activeWindowHandle: HWND; 
    activeWindowThread, thisThread: DWORD; 
begin 
    Result := GetFocus(); 
    if Result = 0 then 
    begin 
    activeWindowHandle := GetForegroundWindow(); 
    if activeWindowHandle <> 0 then 
    begin 
     activeWindowThread := GetWindowThreadProcessId(activeWindowHandle, 0); 
     thisThread := GetCurrentThreadId(); 
     if AttachThreadInput(activeWindowThread, thisThread, TRUE) then 
     begin 
     Result := GetFocus(); 
     AttachThreadInput(activeWindowThread, thisThread, FALSE); 
     end; 
    end; 
    end; 
end; 

然而,同樣的文檔說:

要用鍵盤焦點前景隊列或另一隊列得到窗口線程,使用GetGUIThreadInfo函數。

例如:

function GetAppliWinHandle: HWND; 
var 
    activeWindowHandle: HWND; 
    activeWindowThread: DWORD; 
    gui: TGUIThreadinfo; 
begin 
    Result := GetFocus(); 
    if Result = 0 then 
    begin 
    activeWindowHandle := GetForegroundWindow(); 
    if activeWindowHandle <> 0 then 
    begin 
     activeWindowThread := GetWindowThreadProcessId(activeWindowHandle, 0); 
     gui.cbSize := sizeof(gui); 
     if GetGUIThreadInfo(activeWindowThread, gui) then 
     Result := gui.hwndFocus; 
    end; 
    end; 
end; 

或者簡單:

function GetAppliWinHandle: HWND; 
var 
    gui: TGUIThreadinfo; 
begin 
    gui.cbSize := sizeof(gui); 
    if GetGUIThreadInfo(0, gui) then 
    Result := gui.hwndFocus 
    else 
    Result := 0; 
end; 
+0

謝謝謝謝謝謝謝謝謝謝謝謝。我嘗試了第一個,這工作:)我會嘗試其他兩個,但「GUITHREADINFO」不被識別,我使用哪個單位? 雷米你搖滾:) – bhattji

+0

德爾福認爲'GUITHREADINFO'爲'TGUIThreadinfo'代替。我更新了我的例子。 –

+0

另外兩個不在編譯:( (1)我將cbize替換爲cbSize (2)它給出錯誤「[dcc64錯誤] TheHookDll.dpr(136):E2033實際和正式var參數的類型必須相同」如果GetGUIThreadInfo(0,gui)然後 – bhattji