如何判斷滑鼠離開Form |
答題得分者是:TWY
|
老大仔
尊榮會員 發表:78 回覆:837 積分:1088 註冊:2006-07-06 發送簡訊給我 |
我想要判斷滑鼠是否有移開form或有在form之中
有找了一些文章 而我在以下的文章中找到了答案 http://delphi.ktop.com.tw/board.php?cid=168&fid=914&tid=31817 但就猶如文章所提到的 當滑鼠移動太快時 這個效果就會失效 所以想請問站上的大大們 是否有解決的方法呢? PS : Delphi7 win2000 |
TWY
高階會員 發表:2 回覆:133 積分:152 註冊:2009-09-02 發送簡訊給我 |
我剛試了兩三種過去看到的不同寫法,但都脫離不了偵測Message的方式,一樣速度太快就失效,似乎VCL Control對於滑鼠太快移動的Message會忽略...!?
放個 ApplicationEvents.OnMessage 來抓出所有 Message 觀察,發現其實處理 Message 的頻率並不是很"高速"的,MouseEnter MouseLeave Message 太快速移動根本不會出現,VCL 當然抓不到...(我在想是否因為 OS 效能考量而作此設計與限制呢,因為所有 Message 都要 eat 的話應該會很忙吧) 抱歉我無法提供你想要的答案,只能分享小弟測試結果與看法大家討論討論。 若Message方式無法突破,可能要考慮是否可以接受其他(可能較笨)的方式了... (例如 Timer 偵測滑鼠位置是否在 Form 範圍內...<<有點笨又不夠技巧的方式 不過正確性應沒問題>>)
編輯記錄
TWY 重新編輯於 2009-09-11 09:58:55, 註解 無‧
|
TWY
高階會員 發表:2 回覆:133 積分:152 註冊:2009-09-02 發送簡訊給我 |
有啦,剛突然想到 Hook 可以攔截到更底層的 Message,剛實際試了一下滑鼠再快速移動都可以抓到!
(約四年多前某外國網站看到的,該網站已關閉...) ↓先將這個 HookMouse.dpr 編譯成 HookMouse.DLL [code delphi] library HookMouse; {Demo de Hook de Rat鏮 a nivel de sistema, Radikal.} uses Windows, Messages; const CM_MANDA_DATOS = WM_USER $1000; type TCompartido = record Receptor, wHitTestCode, x,y, Ventana : hwnd; end; PCompartido =^TCompartido; var HookDeMouse : HHook; FicheroM : THandle; Compartido : PCompartido; function CallBackDelHook( Code : Integer; wParam : WPARAM; lParam : LPARAM ) : LRESULT; stdcall; var DatosMouse : PMouseHookStruct; Intentos : integer; {Esta es la funcion CallBack a la cual llamar?el hook.} {This is the CallBack function called by he Hook} begin {Si hay un nuevo evento de raton...} {if there is a new mouse event...} if code=HC_ACTION then begin {Miramos si existe el fichero} {if the mapfile exists} FicheroM:=OpenFileMapping(FILE_MAP_WRITE,False,'ElReceptor'); {Si no existe, no enviamos nada a la aplicacion receptora} {If dont, send nothing to receiver application} if FicheroM<>0 then begin Compartido:=MapViewOfFile(FicheroM,FILE_MAP_WRITE,0,0,0); {Apuntamos hacia los datos del evento del raton} DatosMouse:=Pointer(lparam); {Los guardamos en el fichero de memoria} Compartido^.Ventana:=DatosMouse^.hwnd; Compartido^.x:=DatosMouse^.pt.x; Compartido^.y:=DatosMouse^.pt.y; {Avisamos al receptor para que atienda el nuevo evento} {Say to receiver that there is a new event} PostMessage(Compartido^.Receptor,CM_MANDA_DATOS,wParam,lParam); UnmapViewOfFile(Compartido); CloseHandle(FicheroM); end; end; {Llamamos al siguiente hook de la cadena} {call to next hook of the chain} Result := CallNextHookEx(HookDeMouse, Code, wParam, lParam) end; procedure HookOn; stdcall; {Procedure que instala el hook} {procedure for install the hook} begin HookDeMouse:=SetWindowsHookEx(WH_MOUSE, @CallBackDelHook, HInstance , 0); end; // stops this type of watch procedure HookOff; stdcall; begin {procedure para desinstalar el hook} {procedure to uninstall the hook} UnhookWindowsHookEx(HookDeMouse); end; exports {Exportamos las procedures...} {Export the procedures} HookOn, HookOff; begin end. [/code] ↓這是引用該 DLL 的測試 Form (放了三個元件 Memo1 , Label1 , Label2 ) [code delphi] unit unHook_CaptureAllMouseMsg; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const NombreDLL = 'HookMouse.dll'; CM_MANDA_DATOS = WM_USER $1000; type TCompartido = record Receptor, wHitTestCode, x,y, Ventana : hwnd; end; PCompartido =^TCompartido; THookMouse=procedure; stdcall; type TfrmHook_CaptureAllMouseMsg = class(TForm) Memo1: TMemo; Label1: TLabel; Label2: TLabel; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FicheroM : THandle; Compartido : PCompartido; HandleDLL : THandle; HookOn, HookOff : THookMouse; procedure LlegaDelHook(var message: TMessage); message CM_MANDA_DATOS; public { Public declarations } end; var frmHook_CaptureAllMouseMsg: TfrmHook_CaptureAllMouseMsg; implementation {$R *.dfm} { TfrmHook_CaptureAllMouseMsg } procedure TfrmHook_CaptureAllMouseMsg.LlegaDelHook(var message: TMessage); var DatosMouse : PMouseHookStruct; NombreVentana : array [0..200] of char; Accion : string; begin with Compartido^ do begin {Coordenadas del raton} {Mouse coordinates} Label1.caption:='[' IntToStr(x) ':' IntToStr(y) ']'; end; {Nombre de la ventana donde esta el raton} {Window Name} GetWindowText(Compartido^.Ventana,@NombreVentana,200); Label2.Caption:=NombreVentana; case Message.wParam of WM_LBUTTONDBLCLK : Accion:='WM_LBUTTONDBLCLK '; WM_LBUTTONDOWN : Accion:='WM_LBUTTONDOWN '; WM_LBUTTONUP : Accion:='WM_LBUTTONUP '; WM_MBUTTONDBLCLK : Accion:='WM_MBUTTONDBLCLK '; WM_MBUTTONDOWN : Accion:='WM_MBUTTONDOWN '; WM_MBUTTONUP : Accion:='WM_MBUTTONUP '; WM_MOUSEMOVE : Accion:='WM_MOUSEMOVE '; WM_NCHITTEST : Accion:='WM_NCHITTEST '; WM_NCLBUTTONDBLCLK : Accion:='WM_NCLBUTTONDBLCLK'; WM_NCLBUTTONDOWN : Accion:='WM_NCLBUTTONDOWN '; WM_NCLBUTTONUP : Accion:='WM_NCLBUTTONUP '; WM_NCMBUTTONDBLCLK : Accion:='WM_NCMBUTTONDBLCLK'; WM_NCMBUTTONDOWN : Accion:='WM_NCMBUTTONDOWN '; WM_NCMBUTTONUP : Accion:='WM_NCMBUTTONUP '; WM_NCMOUSEMOVE : Accion:='WM_NCMOUSEMOVE '; WM_NCRBUTTONDBLCLK : Accion:='WM_NCRBUTTONDBLCLK'; WM_NCRBUTTONDOWN : Accion:='WM_NCRBUTTONDOWN '; WM_NCRBUTTONUP : Accion:='WM_NCRBUTTONUP '; WM_RBUTTONDBLCLK : Accion:='WM_RBUTTONDBLCLK '; WM_RBUTTONDOWN : Accion:='WM_RBUTTONDOWN '; WM_RBUTTONUP : Accion:='WM_RBUTTONUP '; end; Memo1.Lines.Append(Accion); end; procedure TfrmHook_CaptureAllMouseMsg.FormCreate(Sender: TObject); begin HandleDLL:=LoadLibrary( PChar(ExtractFilePath(Application.Exename) NombreDLL ) ); if HandleDLL = 0 then raise Exception.Create('No se pudo cargar la DLL'); @HookOn :=GetProcAddress(HandleDLL, 'HookOn'); @HookOff:=GetProcAddress(HandleDLL, 'HookOff'); if not assigned(HookOn) or not assigned(HookOff) then raise Exception.Create('No se encontraron las funciones en la DLL' #13 'Cannot find the required DLL functions'); {Creamos el fichero de memoria} FicheroM:=CreateFileMapping( $FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(Compartido), 'ElReceptor'); {Si no se cre?el fichero, error} if FicheroM=0 then raise Exception.Create( 'Error al crear el fichero' '/Error while create file'); {Direccionamos nuestra estructura al fichero de memoria} Compartido:=MapViewOfFile(FicheroM,FILE_MAP_WRITE,0,0,0); {Escribimos datos en el fichero de memoria} Compartido^.Receptor:=Handle; HookOn; end; procedure TfrmHook_CaptureAllMouseMsg.FormDestroy(Sender: TObject); begin {Desactivamos el Hook} {Uninstall the Hook} if Assigned(HookOff) then HookOff; {Liberamos la DLL} {Free the DLL} if HandleDLL<>0 then FreeLibrary(HandleDLL); {Cerramos la vista del fichero y el fichero} {Close the memfile and the View} if FicheroM<>0 then begin UnmapViewOfFile(Compartido); CloseHandle(FicheroM); end; end; end. [/code] |
老大仔
尊榮會員 發表:78 回覆:837 積分:1088 註冊:2006-07-06 發送簡訊給我 |
|
TWY
高階會員 發表:2 回覆:133 積分:152 註冊:2009-09-02 發送簡訊給我 |
我原本的意思是假設 Form 最左上角座標(X=10 Y=10),右上(X=20 Y=10),左下(X=10 Y=20),右下(X=20 Y=20)
放一顆 Timer 元件每 0.1 秒去抓取游標目前 X/Y 座標值,若不在 Form 的範圍內表示游標離開 Form 了... 其實這個方法並不是很理想,Timer設頻率太高性能不好,太低判讀精準度不好,所以只是個粗略想法參考罷了。 ===================引 用 老大仔 文 章=================== 不過~至於您文章提到的 用Timer去偵測位置 我可以請問一下要怎麼去偵測嗎? 想說多學一招也好 |
老大仔
尊榮會員 發表:78 回覆:837 積分:1088 註冊:2006-07-06 發送簡訊給我 |
|
RootKit
資深會員 發表:16 回覆:358 積分:419 註冊:2008-01-02 發送簡訊給我 |
實在不懂為什麼有那麼多人喜歡用 HOOK 方式解決問題。
不會感覺大材小用嗎?越瞭解 HOOK 的人就越不敢亂用是非常謹慎的。 [code delphi] type TForm1 = class(TForm) procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); private procedure WMMOUSELEAVE(var Msg: TMessage);message WM_MOUSELEAVE; public end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer); var EventTrack : TTrackMouseEvent; begin With EventTrack do begin cbSize := SizeOf(EventTrack); hwndTrack := Handle; dwFlags := TME_LEAVE; dwHoverTime := 0; end; TrackMouseEvent(EventTrack); end; procedure TForm1.WMMOUSELEAVE(var Msg :TMessage); begin Msg.Result := 0; end; [/code] TrackMouseEvent API 可查閱 MSDN。 參考 ===================引 用 老大仔 文 章=================== 謝謝TWY大大的解說~ 其實...我在稍早時也自行去try看看 雖然timer也是可以弄 但就猶如TWY大大所講的 效果也有差 所以最後還是用HookMouse的方法來實作 而且準度還好到沒話說呢~ 最後還是謝謝TWY大大提供的方法~ |
TWY
高階會員 發表:2 回覆:133 積分:152 註冊:2009-09-02 發送簡訊給我 |
謝謝 Rootkit 大大的忠告,的確 HOOK 控制到 System Level 很接近底層核心了,權限越高危險越大...
有時候用 HOOK 是因為找不到其他更好解決方式的不得已,但最好還是要對欲使用的部份做更進一步了解才好。 我之前貼的範例是某已關閉的國外網站看到的,功能為跨 Process 攔截出所有 Mouse Message(正因功能強大所以吸引我保存下來),我自己沒實際用到所以也沒有深入去研究過,用在 老大仔 的問題上還真是殺雞用了牛刀了。 話說回來,TrackMouseEvent API 的確小巧且可符合這個問題需求,我又多學到一招了,謝謝 Rootkit 大大的分享。 |
老大仔
尊榮會員 發表:78 回覆:837 積分:1088 註冊:2006-07-06 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |