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.