GetFocus()为MS Word返回null

时间:2016-07-13 06:23:12

标签: delphi winapi window-functions window-handles

我正在编写一个键盘应用程序,它挂钩键盘并重新映射键。为此,我创建了两个项目,一个是.exe和.dll。在.dll项目中,我检测到用户通过GetFocus()键入的窗口句柄。但是,它在记事本中工作正常,但在MS Word中没有,因为我无法使用GetFocus()

获取MS Word的Window句柄

我理解,那是因为它可能在不同的线程下运行,因此,我需要通过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();

这给了我lpHookRec ^ .AppWinHandle中NotePad窗口的句柄。但是,如果我使用MS Word而不是NotePad,上面的代码会给我null(零)。所以需要编写返回正确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 个答案:

答案 0 :(得分:0)

根据GetFocus()文档:

  

检索具有键盘焦点的窗口的句柄,如果窗口附加到调用线程的消息队列

     

...

     

GetFocus返回当前线程消息队列的键盘焦点窗口。如果GetFocus返回NULL,则另一个线程的队列可能会附加到具有键盘焦点的窗口

     

使用GetForegroundWindow功能检索用户当前正在使用的窗口的句柄。您可以使用AttachThreadInput函数将线程的消息队列与另一个线程拥有的窗口相关联。

您正在尝试执行此部分,但您没有正确执行此操作,并且您不会在此过程中检查错误。您也错误地将HWND数据类型用于线程ID,但它们不是HWND,而是DWORD

尝试更像这样的事情:

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;