//使用鼠标钩子<br>//Hookdll.dpr(钩子DLL的工程文件)<br>//========================================================================<br>{ }<br>{ This is the core of the system and task hook. Some notes: }<br>{ }<br>{ 1) You will definitely want to give the file a more descriptive name }<br>{ to avoid possible collisions with other DLL names. }<br>{ 2) Edit the MouseHookCallBack function to do what you need when a }<br>{ mouse message is received. If you are hooking something other }<br>{ mouse messages, see the SetWindowsHookEx topic in the help for the }<br>{ proper WH_xxxx constant, and any notes about the particular type }<br>{ of hook. }<br>{ 3) If an application that uses the DLL crashes while the hook is }<br>{ installed, all manner of wierd things can happen, depending on the }<br>{ sort of thing you are doing in the callback. The best suggestion }<br>{ is to use a utility that displays loaded DLLs and forcibly unload }<br>{ the DLL. You could also write a simple app that checks to see if }<br>{ the DLL is loaded, and if so, call FreeModule until it returns 0. }<br>{ 4) If you make changes to the DLL but the changes don't seem to be }<br>{ working, you may have the DLL already loaded in memory. Remember, }<br>{ loading a DLL that is already in memory just increments a usage }<br>{ count in Windows and uses the already loaded copy. }<br>{ 5) Remember when you are hooking in at the *system* level, your }<br>{ callback function is being called for everything in the OS. Try }<br>{ to keep the processing in the callback as tight and fast as you }<br>{ possibly can. }<br>{ 6) Be careful of the uses clause. If you include stuff like Dialogs, }<br>{ you will end up linking in a lot of the VCL, and have a DLL that }<br>{ comes out compiled to around 250k. You would probably be better }<br>{ served using WM_USER messages to communicate with the application. }<br>{ 7) I have successfully hooked mouse messages without the use of a }<br>{ DLL, but many of the hooks say they require the callback to be in }<br>{ a DLL, so I am hesitant to include this method. It certainly }<br>{ makes the build/test cycle *much* easier, but since it is not }<br>{ "sanctioned" by MS, I would stay away from it and discourage it. }<br>{ 8) Remember that 32-bit processes can only load 32-bit DLLs, and }<br>{ 16-bit processes can only load 16-bit DLLs. That means that if }<br>{ you set a system hook and your DLL is compiled under Delphi 1, }<br>{ only 16-bit processes are going to fire the system hook. So, if }<br>{ it were running on a Windows 98 machine, not much of anything }<br>{ is going to fire your "system" hook. }<br>{ }<br><br>library HookDLL;<br><br>uses WinTypes, WinProcs, Messages;<br><br><br>{ Global variables }<br>{$IFDEF WIN32}<br><br>{ For a system-wide hook, global variables must be Memory Mapped to be<br> accessible by all processes because the data segment of 32-bit DLL is private<br> to each process using it. }<br>type<br> PSharedData = ^TSharedData;<br> TSharedData = record<br> { In this record, mirror the global variables of the 16-bit version }<br> HookCount: integer;<br> HookHandle: HHook;<br> MonitorWnd: HWND;<br> WM_MONITORMOUSEMOVE: UINT;<br> end;<br><br>var<br> { global data for the DLL for a single process }<br> hMapObject: THandle;<br> SharedData: PSharedData;<br><br>{$ELSE}<br><br>{ 16 bit Windows }<br>type<br> UINT = Longint;<br><br>var<br> HookCount: integer;<br> HookHandle: HHook;<br> MonitorWnd: HWND;<br> WM_MONITORMOUSEMOVE: UINT;<br>{$ENDIF}<br><br>const<br> MESSAGE_MONITOR_MOUSE_MOVE = 'DFSHookDLLMonitorMouseMoveMessage';<br><br><br>function GetMonitorMouseMoveMsg: UINT; export;<br>begin<br> {$IFDEF WIN32}<br> if SharedData = NIL then<br> Result := 0<br> else<br> Result := SharedData^.WM_MONITORMOUSEMOVE;<br> {$ELSE}<br> Result := WM_MONITORMOUSEMOVE;<br> {$ENDIF}<br>end;<br><br>{ This is where you do your special processing. }<br>{$IFDEF WIN32}<br>function MouseHookCallBack(Code: integer; Msg: WPARAM; MouseHook: LPARAM): LRESULT; stdcall;<br>{$ELSE}<br>function MouseHookCallBack(Code: integer; Msg: word; MouseHook: longint): longint; export;<br>{$ENDIF}<br>var<br> {$IFDEF WIN32}<br> HookHandle: HHOOK;<br> MonitorWnd: HWND;<br> WM_MONITORMOUSEMOVE: UINT;<br> {$ENDIF}<br> MouseHookStruct: PMouseHookStruct absolute MouseHook;<br>begin<br> {$IFDEF WIN32}<br> { Access the shared data. Do check if SharedData is assigned, because under<br> some circumstances the hook filter can be called after all processes have<br> detached }<br> if SharedData <> NIL then<br> begin<br> MonitorWnd := SharedData^.MonitorWnd;<br> HookHandle := SharedData^.HookHandle;<br> WM_MONITORMOUSEMOVE := SharedData^.WM_MONITORMOUSEMOVE;<br> end<br> else<br> begin<br> WM_MONITORMOUSEMOVE := 0;<br> MonitorWnd := 0;<br> HookHandle := 0; { It seems that this handle is not used in the CallNextHookEx<br> function anyway. Several sources on the microsoft web site<br> indicate this. }<br> end;<br> {$ENDIF}<br><br> { If the value of Code is less than 0, we are not allowed to do anything }<br> { except pass it on to the next hook procedure immediately. }<br> if (Code >= 0) and (MonitorWnd <> 0) then<br> begin<br> { This example sends the coordinates of all mouse move messages to the<br> monitoring app (the one that installed the hook). }<br> if (Msg = WM_MOUSEMOVE) and (MouseHookStruct <> NIL) then<br> PostMessage(MonitorWnd, WM_MONITORMOUSEMOVE, MouseHookStruct^.pt.x,<br> MouseHookStruct^.pt.y);<br><br> { You could do any number of things here based on the different mouse<br> messages...<br> case Msg of:<br> WM_LBUTTONDOWN:<br> begin<br> end;<br> WM_LBUTTONUP:<br> begin<br> end;<br> WM_LBUTTONDBLCLK:<br> begin<br> end;<br> WM_RBUTTONDOWN:<br> begin<br> end;<br> WM_RBUTTONUP:<br> begin<br> end;<br> WM_RBUTTONDBLCLK:<br> begin<br> end;<br> WM_MBUTTONDOWN:<br> begin<br> end;<br> WM_MBUTTONUP:<br> begin<br> end;<br> WM_MBUTTONDBLCLK:<br> begin<br> end;<br> WM_MOUSEMOVE:<br> begin<br> end;<br> end;}<br><br> { If you handled the situation, and don't want Windows to process the }<br> { message, do *NOT* execute the next line. Be very sure this is what }<br> { want, though. If you don't pass on stuff like WM_MOUSEMOVE, you }<br> { will NOT like the results you get. }<br> Result := CallNextHookEx(HookHandle, Code, Msg, MouseHook);<br> end<br> else<br> Result := CallNextHookEx(HookHandle, Code, Msg, MouseHook);<br>end;<br><br>{ Call InstallHook to set the hook. }<br>function InstallHook(SystemHook: boolean; TaskHandle: THandle;<br> AMonitorWnd: HWND) : boolean; export;<br> { This is really silly, but that's the way it goes. The only way to get the }<br> { module handle, *not* instance, is from the filename. The Microsoft example }<br> { just hard-codes the DLL filename. I think this is a little bit better. }<br> function GetModuleHandleFromInstance: THandle;<br> var<br> s: array[0..512] of char;<br> begin<br> { Find the DLL filename from the instance value. }<br> GetModuleFileName(hInstance, s, sizeof(s)-1);<br> { Find the handle from the filename. }<br> Result := GetModuleHandle(s);<br> end;<br>begin<br> { Technically, this procedure could do nothing but call SetWindowsHookEx(), }<br> { but it is probably better to be sure about things, and not set the hook }<br> { more than once. You definitely don't want your callback being called more }<br> { than once per message, do you? }<br> {$IFDEF WIN32}<br> Result := FALSE;<br> if SharedData = NIL then<br> exit<br> else<br> with SharedData^ do<br> begin<br> {$ENDIF}<br> Result := TRUE;<br> if HookCount = 0 then<br> begin<br> MonitorWnd := AMonitorWnd;<br> if SystemHook then<br> HookHandle := SetWindowsHookEx(WH_MOUSE, MouseHookCallBack, HInstance, 0)<br> else<br> { See the Microsoft KnowledgeBase, PSS ID Number: Q92659, for a<br> discussion of the Windows bug that requires GetModuleHandle() to be<br> used. }<br> HookHandle := SetWindowsHookEx(WH_MOUSE, MouseHookCallBack,<br> GetModuleHandleFromInstance, TaskHandle);<br> if HookHandle <> 0 then<br> inc(HookCount)<br> else<br> Result := FALSE;<br> end<br> else<br> inc(HookCount);<br> {$IFDEF WIN32}<br> end;<br> {$ENDIF}<br>end;<br><br>{ Call RemoveHook to remove the system hook. }<br>function RemoveHook: boolean; export;<br>begin<br> { See if our reference count is down to 0, and if so then unhook. }<br> Result := FALSE;<br> {$IFDEF WIN32}<br> if SharedData = NIL then<br> exit<br> else<br> with SharedData^ do<br> begin<br> {$ENDIF}<br> if HookCount < 1 then exit;<br> Result := TRUE;<br> dec(HookCount);<br> if HookCount = 0 then<br> Result := UnhookWindowsHookEx(HookHandle);<br> {$IFDEF WIN32}<br> end;<br> {$ENDIF}<br>end;<br><br>{ Have we hooked into the system? }<br>function IsHookSet: boolean; export;<br>begin<br> {$IFDEF WIN32}<br> if SharedData = NIL then<br> Result := FALSE<br> else<br> with SharedData^ do<br> {$ENDIF}<br> Result := (HookCount > 0) and (HookHandle <> 0);<br>end;<br><br>{$IFDEF WIN32}<br>{ Shared data management }<br>procedure AllocSharedData;<br>var<br> Init: boolean;<br>begin<br> if hMapObject = 0 then<br> begin<br> // Create a named file mapping object.<br> hMapObject := CreateFileMapping(<br> THandle($FFFFFFFF), // use paging file<br> NIL, // no security attributes<br> PAGE_READWRITE, // read/write access<br> 0, // size: high 32-bits<br> SizeOf(TSharedData), // size: low 32-bits<br> 'DFSHookDLLSharedDataBlock'); // name of map object, THIS MUST BE UNIQUE!<br> // The first process to attach initializes memory.<br> Init := GetLastError <> ERROR_ALREADY_EXISTS;<br> if hMapObject = 0 then exit;<br> // Get a pointer to the file-mapped shared memory.<br> SharedData := MapViewOfFile(<br> hMapObject, // object to map view of<br> FILE_MAP_WRITE, // read/write access<br> 0, // high offset: map from<br> 0, // low offset: beginning<br> 0); // default: map entire file<br> if SharedData = NIL then exit;<br> // Initialize memory if this is the first process.<br> if Init then<br> FillChar(SharedData^, SizeOf(TSharedData), 0);<br> SharedData^.WM_MONITORMOUSEMOVE := RegisterWindowMessage(<br> MESSAGE_MONITOR_MOUSE_MOVE);<br> end;<br>end;<br><br>procedure FreeSharedData;<br>begin<br> if hMapObject <> 0 then<br> try<br> try<br> UnMapViewOfFile(SharedData);<br> CloseHandle(hMapObject);<br> except<br> end;<br> finally<br> SharedData := NIL;<br> hMapObject := 0;<br> end;<br>end;<br><br>procedure EntryPointProc(Reason: Integer);<br>begin<br> case Reason of<br> DLL_PROCESS_DETACH: FreeSharedData;<br> DLL_PROCESS_ATTACH: AllocSharedData;<br> DLL_THREAD_ATTACH: {} ;<br> DLL_THREAD_DETACH: {} ;<br> end;<br>end;<br>{$ENDIF}<br><br><br>exports<br> GetMonitorMouseMoveMsg,<br> InstallHook,<br> RemoveHook,<br> IsHookSet,<br> MouseHookCallBack;<br><br><br>{ Initialize DLL data. }<br>begin<br> {$IFDEF WIN32}<br> SharedData := NIL;<br> hMapObject := 0;<br> DllProc := @EntryPointProc;<br> EntryPointProc(DLL_PROCESS_ATTACH); // Must call manually in the Delphi world<br> {$ELSE}<br> HookCount := 0;<br> HookHandle := 0;<br> WM_MONITORMOUSEMOVE := RegisterWindowMessage(MESSAGE_MONITOR_MOUSE_MOVE);<br> {$ENDIF}<br>end.<br><br>//========================================================================<br><br>//Hookunit.pas(钩子DLL的单元文件)<br>//========================================================================<br>{ This is a simple DLL import unit to give us access to the functions in }<br>{ the HOOKDLL.PAS file. This is the unit your project will use. }<br>unit Hookunit;<br><br>interface<br><br>uses WinTypes;<br><br>{$IFNDEF WIN32}<br>type<br> UINT = Longint;<br>{$ENDIF}<br><br>function GetMonitorMouseMoveMsg: UINT;<br>function InstallSystemHook(AMonitorWnd: HWND): boolean;<br>function InstallTaskHook(AMonitorWnd: HWND): boolean;<br>function RemoveHook: boolean;<br>function IsHookSet: boolean;<br>{ Do not use InstallHook directly. Use InstallSystemHook or InstallTaskHook. }<br>function InstallHook(SystemHook: boolean; TaskHandle: THandle;<br> AMonitorWnd: HWND): boolean;<br><br>implementation<br><br>uses WinProcs;<br><br>const<br> {$IFDEF WIN32}<br> HOOK_DLL = 'HOOKDLL.DLL';<br> {$ELSE}<br> HOOK_DLL = 'HOOKDLL';<br> {$ENDIF}<br><br>function GetMonitorMouseMoveMsg: UINT; external HOOK_DLL;<br>function InstallHook(SystemHook: boolean; TaskHandle: THandle;<br> AMonitorWnd: HWND): boolean; external HOOK_DLL;<br>function RemoveHook: boolean; external HOOK_DLL;<br>function IsHookSet: boolean; external HOOK_DLL;<br><br>function InstallSystemHook(AMonitorWnd: HWND): boolean;<br>begin<br> Result := InstallHook(TRUE, 0, AMonitorWnd);<br>end;<br><br>function InstallTaskHook(AMonitorWnd: HWND): boolean;<br>begin<br> Result := InstallHook(FALSE,<br> {$IFDEF WIN32}<br> GetCurrentThreadID,<br> {$ELSE}<br> GetCurrentTask,<br> {$ENDIF}<br> AMonitorWnd<br> );<br>end;<br><br>end.<br><br>//========================================================================<br><br>//调用钩子的主程序代码<br>//========================================================================<br>unit Main;<br><br>interface<br><br>uses<br> SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,<br> Forms, Dialogs, StdCtrls, HookUnit;<br><br>type<br> THookForm = class(TForm)<br> SysHookBtn: TButton;<br> RemoveHookBtn: TButton;<br> TaskHookBtn: TButton;<br> lblMousePos: TLabel;<br> procedure FormCreate(Sender: TObject);<br> procedure SysHookBtnClick(Sender: TObject);<br> procedure RemoveHookBtnClick(Sender: TObject);<br> procedure TaskHookBtnClick(Sender: TObject);<br> procedure FormDestroy(Sender: TObject);<br> protected<br> procedure WndProc(var Message: TMessage); override;<br> procedure UpdateButtons;<br> procedure UpdateMousePosInfo(X, Y: integer);<br> end;<br><br>var<br> HookForm: THookForm;<br><br>implementation<br><br>{$R *.DFM}<br><br>procedure THookForm.FormCreate(Sender: TObject);<br>begin<br> UpdateButtons;<br>end;<br><br>procedure THookForm.FormDestroy(Sender: TObject);<br>begin<br> while IsHookSet do<br> RemoveHook; { Make sure we unhook ourselves. }<br>end;<br><br>procedure THookForm.SysHookBtnClick(Sender: TObject);<br>begin<br> if not InstallSystemHook(Handle) then<br> ShowMessage('Could not install mouse hook. SetWindowsHookEx() failed.');<br> UpdateButtons;<br>end;<br><br>procedure THookForm.TaskHookBtnClick(Sender: TObject);<br>begin<br> if not InstallTaskHook(Handle) then<br> ShowMessage('Could not install task hook. SetWindowsHookEx() failed.');<br> UpdateButtons;<br>end;<br><br>procedure THookForm.RemoveHookBtnClick(Sender: TObject);<br>begin<br> if IsHookSet then<br> RemoveHook;<br> UpdateButtons;<br>end;<br><br>procedure THookForm.UpdateButtons;<br>begin<br> SysHookBtn.Enabled := not IsHookSet;<br> TaskHookBtn.Enabled := not IsHookSet;<br> RemoveHookBtn.Enabled := IsHookSet;<br> if not IsHookSet then<br> lblMousePos.Caption := 'Hook not set';<br>end;<br><br>procedure THookForm.WndProc(var Message: TMessage);<br>begin<br> if Message.Msg = GetMonitorMouseMoveMsg then<br> UpdateMousePosInfo(Message.WParam, Message.LParam)<br> else<br> inherited WndProc(Message);<br>end;<br><br>procedure THookForm.UpdateMousePosInfo(X, Y: integer);<br>begin<br> lblMousePos.Caption := Format('X = %d, Y = %d', [X, Y]);<br>end;<br><br>end.<br><br>//========================================================================<br> <br><br><br>来自:stuwei, 时间:2003-4-3 22:29:00, ID:1736906<br>截取消息wm_mousemove<br> <br><br><br>来自:明月春秋, 时间:2003-4-4 17:03:00, ID:1738351<br>当我打开子窗体时,鼠标在子窗体上移动,些时,鼠标钩子,还能截取到消息吗,<br>而且,我要的鼠标的位置 ,是相对于主窗体 <br> <br><br><br>来自:小笨苯, 时间:2003-4-4 18:00:00, ID:1738621<br>上面的代码是全局钩子,不管当前是那个窗体处于激活状态,也不管是不是你的程序的窗体处于激活状态,<br>都能取到鼠标的位置。<br>>>我要的鼠标的位置 ,是相对于主窗体 <br>这个可以自己转换一下嘛。ClientToScreen , ScreenToClient