2017年9月7日 星期四

[轉貼]如何正確捕捉滑鼠及其原理(附範例)

在Win95, WinNT 的環境下, Microsoft為了避免行程間互相干擾,
對於使用者輸入的部份(如鍵盤, 滑鼠), 是採用一種叫"Local
Input State Processing"的方式, 這種技術簡單來說, 就是每個
行程, 執行緒, 認為自已是唯一取得使用者輸入的, 彼此之間並
不互相干擾(其實真正取得使用者輸入的只有 Active的行程或執行緒).

而我們最常用來抓取螢幕座標的 SetCapture , 是以System-Wide 的
型式來實作的, 所以就算滑鼠不在你的程式視窗範圍內, 也可以抓到
座標, 但是當你把滑鼠按鍵放開, 系統會改變滑鼠捕捉權, 使其為
Thread-Local-Wide, 此時就算你沒有使用 ReleaseCapture 來釋放
滑鼠捕捉權, 也無法在視窗範圍外捕捉滑鼠.這是因為 Local Input
State Processing 的關係. 若要解決這個問題, 最好的方法就是暫
時把 Local Input State Processing 的功能關閉, 而方式就是掛上
一個 Journal Record Hook, 在Win95, NT 下因為Local Input State
Processing 和 Journal Record Hook 會互相干擾, 而Microsoft為了
向下相容性所以當Journal Record Hook 被掛起來時, 就會把 Local
Input State Processin 給關閉.

範例貼在下一篇, 此測試程式會把攔截到的滑鼠座標以 Label1 秀出.
   
測試方法, 首先按下"SetCapture"按鈕, 在程式視窗範圍內, 按下滑鼠
左鍵不放, 將滑鼠移出程式視窗, 此時可以發現, 即使在程式視窗範圍
外還是可以捕捉到滑鼠. 之後把滑鼠左鍵放開, 在移動滑鼠, 可以發現,
在放開滑鼠左鍵後,即使我們沒有呼叫ReleaseCapture, 也無法捕捉到滑
鼠.

接下來我們按下"SetCapture with Journal Record Hook"按鈕, 按下後
首先程式會先呼叫SetCapture, 之後在掛上 Journal Record Hook, 而此
Hook 沒做什麼事, 只是把值傳給下一個Hook(CallNextHookEx), 在我們
按下這按鈕後, 無論怎麼移動滑鼠, 無論有沒有按下滑鼠左鍵, 都可以收
到滑鼠座標.


範例:

unit Unit1;

interface

uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
    StdCtrls;

type
    TForm1 = class(TForm)
    Label1: TLabel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    private
    { Private declarations }

    Procedure HandleMouseMsg(var msg: TMessage); Message WM_MOUSEMOVE;

    public
    { Public declarations }
    end;

var
    Form1: TForm1;
    hJourHook: HHOOK;
   
implementation

{$R *.DFM}

Procedure TForm1.HandleMouseMsg(var msg: TMessage);
begin
    Label1.Caption := Format( 'x=%d, y=%d',
        [LOWORD(msg.Lparam), HIWORD(msg.Lparam)]);
    Application.ProcessMessages;
end;

Function JournalRecordProc( code: Integer;
        WParamInfo: WPARAM;
        LParamInfo: LPARAM): Integer; stdcall;
begin
    Result := CallNextHookEx(hJourHook, code, WParamInfo, LParamInfo);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
    SetCapture(Handle);
    SetWindowsHookEx(WH_JOURNALRECORD, @JournalRecordProc, HInstance, 0);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    ReleaseCapture();
    UnhookWindowsHookEx(hJourHook);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
    SetCapture(Handle);
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
    ReleaseCapture();
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    Button1.Caption := 'SetCapture with Journal Record Hook';
    Button2.Caption := 'ReleaseCapture with Journal Record Hook';
    Button3.Caption := 'SetCapture';
    Button4.Caption := 'ReleaseCapture';
end;

end.

轉貼至:http://delphi.ktop.com.tw/board.php?cid=30&fid=72&tid=58460

沒有留言:

張貼留言