0

I want my delphi application to be able to know which control currently has keyboard focus, system wide (in different processes). To achieve this, I use a global CBT hook to get all of the setfocus messages. Every focus change triggers my application to check the type of the focused control by using UIAutomation framework. This works e.g. for my own delphi applications or in the delphi IDE, but if I focus edit boxes in chrome browser, I will only get the window and not the focused control. Changing focus inside of chrome window has no effect. There's no setfocus message sent anymore. Furthermore, focusing e.g. the edit box in the start menu of windows or elements in the explorer won't return anything. As far as I know, those actions should also cause a setfocus message, so I think that maybe not every process is hooked properly. Below is the code of my hook DLL, I don't know what I am doing wrong...

const
  WM_MYFOCUSCHANGED = WM_USER + 1;

type
  PHookRec = ^THookRec;
  THookRec = packed Record
    HookHandle: hhook;
    WindowHandle: hwnd;
  End;

var
  MapHandle: THandle;     // file mapping object
  ipHookRec: PHookRec;    // Pointer to hook record

{$R *.res}

procedure MapFileMemory(dwAllocSize: DWORD);
begin
  {Create a process wide memory mapped variable}
  MapHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0,     dwAllocSize, 'HookRecMemBlock');
  if (MapHandle = 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}
  ipHookRec := MapViewOfFile(MapHandle, FILE_MAP_WRITE, 0, 0, dwAllocSize);
  if (ipHookRec = nil) then
  begin
    CloseHandle(MapHandle);
    MessageBox(0, 'Hook DLL', 'Could not map file', MB_OK);
    exit;
  end;
end;

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

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

function FocusHookProc(code: integer; wParam: wParam; lParam: lParam):     LResult; stdcall;
begin
  if (code < 0) then
  begin
    result := CallNextHookEx(ipHookRec^.HookHandle, code, wParam, lParam);
    exit;
  end;

  result := 0;

  if (code = WM_SETFOCUS) then
  begin
    if (ipHookRec^.WindowHandle <> INVALID_HANDLE_VALUE) then
      PostMessage(ipHookRec^.WindowHandle, WM_MYFOCUSCHANGED, wParam,     lParam);
    // wParam: Handle to the window gaining keyboard focus

  end;
end;

procedure InstallHook(Hwnd: Cardinal); stdcall;
begin

  if ((ipHookRec <> nil) and (ipHookRec^.HookHandle = 0) and     (ipHookRec^.WindowHandle = 0)) then
  begin
    ipHookRec^.WindowHandle := Hwnd;   // handle to the application window
    ipHookRec^.HookHandle := SetWindowsHookEx(WH_CBT, @FocusHookProc, Hinstance, 0);
  end;
end;

procedure UninstallHook; stdcall;
begin
   if ((ipHookRec <> nil) and (ipHookRec^.HookHandle <> 0)) then
  begin
    {Remove our hook and clear our hook handle}
    if (UnHookWindowsHookEx(ipHookRec^.HookHandle) <> FALSE) then
    begin
      ipHookRec^.HookHandle := 0;
      ipHookRec^.WindowHandle := 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}
        MapHandle := 0;
        ipHookRec := nil;
        MapFileMemory(sizeof(ipHookRec^));
      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
  InstallHook name 'INSTALLHOOK',
  UninstallHook name 'UNINSTALLHOOK',
  GetHookRecPointer name 'GETHOOKRECPOINTER';

begin
  DLLProc := @DllEntryPoint;         // set DLL main entry point
  DllEntryPoint(Dll_Process_Attach); // call DLL main entry point
end.
Airogat
  • 199
  • 1
  • 1
  • 10
  • If you want to detect when specific edit or text box is selected in any web browser I'm afraid that you would have to write a specific extension for it. You see edit or text boxes that are shown in a browser window are not standard system edit or text controlls. Infact they don't even have thir own window as all other standard windows controlls do have. They are just rendered in the browser itself so that they look similar. So the browser might just generate one SetFocus message when you select any edit or text box inside it so that OS could enable Accesibility Accesories like online keyboard – SilverWarior Mar 23 '15 at 16:21
  • But I hardy doubt web browser would be generating seperate SetFocus message every time you change different edit or text box i the web page. And eve if it would the focused window would be the browser window. – SilverWarior Mar 23 '15 at 16:23
  • What is your ultimate goal? – David Heffernan Mar 24 '15 at 07:33
  • My ultimate goal is to notice if an edit box was clicked in the whole system. I could live with it not working in browsers, but in the operating system and applications on it, it should work. – Airogat Mar 24 '15 at 07:43
  • @Air - You can't hook 64 bit processes with a 32 bit hook, that may be your problem with explorer. – Sertac Akyuz Mar 24 '15 at 15:37
  • i think you're absolutely right. the application works just fine fo 32 bit processes, but when I start a 64 bit process, the "could not create file map object" message appears and the application won't react anymore. I would be confident with it working only with 32 bit processes, so is there a way to simply ignore all 64 bit processes, which would cause a crash? – Airogat Apr 14 '15 at 09:40

0 Answers0