我正在编写一个键盘应用程序,它挂钩键盘并重新映射键。为此,我创建了两个项目,一个是.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.
答案 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;