MsgSimulator.pas
上传用户:llfxmlw
上传日期:2009-09-14
资源大小:335k
文件大小:18k
- unit MsgSimulator;
- {
- June 23, 1998 by Ben Ziegler
- 6/30/98 - Added a Record Macro function
- }
- interface
- uses
- Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
- type
- TWMMessage = (mmMouseDown, mmMouseUp, mmMouseMove, mmKeyDown, mmKeyUp);
- TMessageItem = class(TCollectionItem)
- protected
- em : TEventMsg; // Structure required by JournalPlayback Proc
- FMsg : TWMMessage;
- FDelay : DWORD; // Delay in msec before next message is played
- FX : integer; // This means nothing for keystrokes
- FY : integer; // This means nothing for keystrokes
- FKey : integer; // This means nothing for mouse clicks
- FHWND : integer; // Window Handle (not used for keystrokes)
- FButton : TMouseButton; // This means nothing for keystrokes
- procedure Fill_EM_From_Props;
- procedure Fill_Props_From_EM;
- public
- constructor Create(Collection: TCollection); override;
- property HWND : integer read FHWND write FHWND; // No need to save it - it will be different after each run
- published
- property Msg : TWMMessage read FMsg write FMsg;
- property PosX : integer read FX write FX;
- property PosY : integer read FY write FY;
- property VkKey : integer read FKey write FKey;
- property Delay : DWORD read FDelay write FDelay;
- property Button : TMouseButton read FButton write FButton;
- end;
- TMsgSimulator = class;
- TMessageCollection = class(TCollection)
- private
- FOwner : TMsgSimulator;
- function GetItem(Index: Integer): TMessageItem;
- procedure SetItem(Index: Integer; Value: TMessageItem);
- protected
- function GetOwner: TPersistent; override;
- procedure Update(Item: TCollectionItem); override;
- public
- constructor Create(AOwner: TMsgSimulator);
- function Add: TMessageItem;
- property Owner: TMsgSimulator read FOwner;
- property Items[Index: Integer]: TMessageItem read GetItem write SetItem; default;
- end;
- TMsgSimulator = class(TComponent)
- protected
- FRunning : boolean; // Simulation is currently running
- play_hk : THandle; // JournalPlayback Hook handle
- rec_hk : THandle; // RecordPlayback Hook handle
- PlayDone : boolean; // Flag to signal that all messages have been simulated
- AbortSim : boolean; // Flag to signal aborting the playback of messages
- StartTime : DWORD; // Time simulation started (msec)
- StopTime : DWORD; // Time simulation stoped (msec)
- FDelay : integer; // Default delay between messages
- FMsgList : TMessageCollection; // Messages to playback
- FTopWin : string;
- FindText : string;
- FindHandle : THandle;
- StopRec : integer;
- FRecording : boolean;
- FOnStopRec : TNotifyEvent;
- function GetElapTime: integer;
- procedure SetMsgList(MsgList: TMessageCollection);
- function Add_Raw_Message(Msg: TWMMessage; x, y, VkKey, Delay, HWND: integer; Button: TMouseButton): TMessageItem;
- procedure Add_Shift(hwnd: THandle; Shift: TShiftState; UpDown: TWMMessage; Delay: integer);
- procedure SimClientToScreen(hwnd: THandle; var x, y: integer);
- procedure FixUp_Playback_Delays;
- procedure FixUp_Record_Delays;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- // Low-level Message Creation Functions
- procedure Add_ClickEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
- x, y, Delay: integer);
- procedure Add_DragEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
- StartX, StartY, StopX, StopY, NumMoves, Delay: integer);
- procedure Add_VirtualKey(hwnd: THandle; VkKey, Delay: integer; UpDown: TWMMessage);
- // High-level Message Creation Functions
- procedure Add_Window_Click(hwnd: THandle; x, y: integer);
- procedure Add_Window_Drag(hwnd: THandle; StartX, StartY, StopX, StopY: integer);
- procedure Add_Screen_Click(x, y: integer);
- procedure Add_Screen_Drag(StartX, StartY, StopX, StopY: integer);
- procedure Add_ASCII_Keys(const Keystrokes: string);
- public
- // Playback & Cancel Functions
- procedure Play; // Plays messages, then returns
- procedure Play_Async; // Returns immediately
- procedure Abort;
- procedure Record_Input;
- procedure Stop_Record;
- property Running: boolean read FRunning;
- property Recording: boolean read FRecording;
- property ElapTime: integer read GetElapTime; // Elapsed running time in msec
- // Helper Functions
- procedure FocusWin(hwnd: THandle);
- function FindTopLevelWin(const FindText: string): THandle;
- published
- property Messages: TMessageCollection read FMsgList write SetMsgList;
- property DefaultDelay: integer read FDelay write FDelay default 50;
- property OnStopRecord: TNotifyEvent read FOnStopRec write FOnStopRec;
- end;
- procedure Register;
- implementation
- var
- CurSim : TMsgSimulator; // Only one TMsgSimulator can play at a time
- Cur : integer; // Current Message to play in the MsgList
- NumCur : integer; // Number of times current message has been played
- procedure Register;
- begin
- RegisterComponents('Samples', [TMsgSimulator]);
- end;
- // *********************************************************************
- // TMessageItem
- constructor TMessageItem.Create(Collection: TCollection);
- begin
- inherited;
- Delay := TMessageCollection(Collection).Owner.DefaultDelay;
- end;
- procedure TMessageItem.Fill_EM_From_Props;
- begin
- em.hwnd := hwnd;
- if (Msg = mmMouseDown) and (Button = mbLeft) then em.message := WM_LBUTTONDOWN;
- if (Msg = mmMouseUp) and (Button = mbLeft) then em.message := WM_LBUTTONUP;
- if (Msg = mmMouseDown) and (Button = mbRight) then em.message := WM_RBUTTONDOWN;
- if (Msg = mmMouseUp) and (Button = mbRight) then em.message := WM_RBUTTONUP;
- if (Msg = mmMouseDown) and (Button = mbMiddle) then em.message := WM_MBUTTONDOWN;
- if (Msg = mmMouseUp) and (Button = mbMiddle) then em.message := WM_MBUTTONUP;
- case Msg of
- mmMouseMove : em.message := WM_MOUSEMOVE;
- mmKeyDown : em.message := WM_KEYDOWN;
- mmKeyUp : em.message := WM_KEYUP;
- end;
- if (Msg = mmKeyDown) or (Msg = mmKeyUp) then begin
- // Keystroke Message
- em.paramL := VkKey;
- em.paramH := MapVirtualKey(VkKey, 0);
- end else begin
- // Mouse Message
- em.paramL := PosX;
- em.paramH := PosY;
- end;
- end;
- procedure TMessageItem.Fill_Props_From_EM;
- begin
- hwnd := em.hwnd;
- case em.message of
- WM_LBUTTONDOWN : begin Msg := mmMouseDown; Button := mbLeft; end;
- WM_LBUTTONUP : begin Msg := mmMouseUp; Button := mbLeft; end;
- WM_RBUTTONDOWN : begin Msg := mmMouseDown; Button := mbRight; end;
- WM_RBUTTONUP : begin Msg := mmMouseUp; Button := mbRight; end;
- WM_MBUTTONDOWN : begin Msg := mmMouseDown; Button := mbMiddle; end;
- WM_MBUTTONUP : begin Msg := mmMouseUp; Button := mbMiddle; end;
- WM_MOUSEMOVE : Msg := mmMouseMove;
- WM_KEYDOWN : Msg := mmKeyDown;
- WM_KEYUP : Msg := mmKeyUp;
- end;
- if (Msg = mmKeyDown) or (Msg = mmKeyUp) then begin
- // Keystroke Message
- VkKey := em.paramL;
- end else begin
- // Mouse Message
- PosX := em.paramL;
- PosY := em.paramH;
- end;
- end;
- // *********************************************************************
- // TMessageCollection
- constructor TMessageCollection.Create(AOwner: TMsgSimulator);
- begin
- inherited Create(TMessageItem);
- FOwner := AOwner;
- end;
- function TMessageCollection.Add: TMessageItem;
- begin
- Result := TMessageItem(inherited Add);
- end;
- function TMessageCollection.GetItem(Index: Integer): TMessageItem;
- begin
- Result := TMessageItem(inherited GetItem(Index));
- end;
- function TMessageCollection.GetOwner: TPersistent;
- begin
- Result := FOwner;
- end;
- procedure TMessageCollection.SetItem(Index: Integer; Value: TMessageItem);
- begin
- inherited SetItem(Index, Value);
- end;
- procedure TMessageCollection.Update(Item: TCollectionItem);
- begin
- Assert(not FOwner.Running);
- end;
- // *********************************************************************
- // TMsgSimulator
- constructor TMsgSimulator.Create(AOwner: TComponent);
- begin
- inherited;
- FDelay := 50;
- FMsgList := TMessageCollection.Create(Self);
- end;
- destructor TMsgSimulator.Destroy;
- begin
- if Running then Abort;
- FMsgList.Free;
- FMsgList := nil;
- inherited;
- end;
- procedure TMsgSimulator.SetMsgList(MsgList: TMessageCollection);
- begin
- FMsgList.Assign(MsgList);
- end;
- function TMsgSimulator.Add_Raw_Message(Msg: TWMMessage; x, y, VkKey, Delay, HWND: integer; Button: TMouseButton): TMessageItem;
- begin
- Result := Messages.Add;
- Result.Msg := Msg;
- Result.PosX := x;
- Result.PosY := y;
- Result.VkKey := VkKey;
- Result.Delay := Delay;
- Result.HWND := HWND;
- Result.Button := Button;
- end;
- procedure TMsgSimulator.Add_Shift(hwnd: THandle; Shift: TShiftState; UpDown: TWMMessage; Delay: integer);
- begin
- // NOTE: Keystrokes do not require an hwnd, so use 0
- if Shift = [] then exit;
- if ssShift in Shift then Add_Raw_Message(UpDown, 0, 0, VK_SHIFT, Delay, 0, mbLeft);
- if ssCtrl in Shift then Add_Raw_Message(UpDown, 0, 0, VK_CONTROL, Delay, 0, mbLeft);
- if ssAlt in Shift then Add_Raw_Message(UpDown, 0, 0, VK_MENU, Delay, 0, mbLeft);
- end;
- // x, y are in Screen coordinates
- procedure TMsgSimulator.Add_ClickEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
- x, y, Delay: integer);
- begin
- Add_Shift(hwnd, Shift, mmKeyDown, Delay);
- Add_Raw_Message(mmMouseDown, x, y, 0, Delay, hwnd, Button);
- Add_Raw_Message(mmMouseUp, x, y, 0, Delay, hwnd, Button);
- Add_Raw_Message(mmMouseMove, x, y, 0, Delay, hwnd, Button);
- Add_Shift(hwnd, Shift, mmKeyUp, Delay);
- end;
- // x, y are in Screen coordinates
- procedure TMsgSimulator.Add_DragEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
- StartX, StartY, StopX, StopY, NumMoves, Delay: integer);
- var
- i, x, y : integer;
- begin
- Add_Shift(hwnd, Shift, mmKeyDown, Delay);
- Add_Raw_Message(mmMouseDown, StartX, StartY, 0, Delay, hwnd, Button);
- for i := 0 to NumMoves do begin
- x := (StopX - StartX) * i div NumMoves + StartX;
- y := (StopY - StartY) * i div NumMoves + StartY;
- Add_Raw_Message(mmMouseMove, x, y, 0, Delay, hwnd, Button);
- end;
- Add_Raw_Message(mmMouseUp, StopX, StopY, 0, Delay, hwnd, Button);
- Add_Shift(hwnd, Shift, mmKeyUp, Delay);
- end;
- procedure TMsgSimulator.Add_VirtualKey(hwnd: THandle; VkKey, Delay: integer; UpDown: TWMMessage);
- begin
- Add_Raw_Message(upDown, 0, 0, vkKey, Delay, hwnd, mbLeft);
- end;
- procedure TMsgSimulator.SimClientToScreen(hwnd: THandle; var x, y: integer);
- var
- p : TPoint;
- begin
- if hwnd = 0 then exit;
- p := Point(x, y);
- Windows.ClientToScreen(hwnd, p);
- x := p.x;
- y := p.y;
- end;
- // x, y are in the Window's coordinates
- procedure TMsgSimulator.Add_Window_Click(hwnd: THandle; x, y: integer);
- begin
- SimClientToScreen(hwnd, x, y);
- Add_ClickEx(hwnd, mbLeft, [], x, y, DefaultDelay);
- end;
- // StartXY & StopXY are in the Window's coordinates
- procedure TMsgSimulator.Add_Window_Drag(hwnd: THandle; StartX, StartY, StopX, StopY: integer);
- begin
- SimClientToScreen(hwnd, StartX, StartY);
- SimClientToScreen(hwnd, StopX, StopY);
- Add_DragEx(hwnd, mbLeft, [], StartX, StartY, StopX, StopY, 10, DefaultDelay);
- end;
- // x, y are in Screen coordinates
- procedure TMsgSimulator.Add_Screen_Click(x, y: integer);
- var
- hwnd : THandle;
- begin
- hwnd := Windows.WindowFromPoint(Point(x, y));
- Add_ClickEx(hwnd, mbLeft, [], x, y, DefaultDelay);
- end;
- // x, y are in Screen coordinates
- procedure TMsgSimulator.Add_Screen_Drag(StartX, StartY, StopX, StopY: integer);
- var
- hwnd : THandle;
- begin
- hwnd := Windows.WindowFromPoint(Point(StartX, StartY));
- Add_DragEx(hwnd, mbLeft, [], StartX, StartY, StopX, StopY, 10, DefaultDelay);
- end;
- procedure TMsgSimulator.Add_ASCII_Keys(const Keystrokes: string);
- var
- i : integer;
- c : byte;
- Shift : boolean;
- begin
- for i := 1 to Length(Keystrokes) do begin
- c := VkKeyScan(Keystrokes[i]) and 255;
- Shift := (VkKeyScan(Keystrokes[i]) and 256) <> 0;
- if Shift then Add_Raw_Message(mmKeyDown, 0, 0, VK_SHIFT, 1 {DefaultDelay}, 0, mbLeft);
- Add_Raw_Message(mmKeyDown, 0, 0, c, DefaultDelay, 0, mbLeft);
- Add_Raw_Message(mmKeyUp, 0, 0, c, 1 {DefaultDelay}, 0, mbLeft);
- if Shift then Add_Raw_Message(mmKeyUp, 0, 0, VK_SHIFT, 1 {DefaultDelay}, 0, mbLeft);
- end;
- end;
- procedure TMsgSimulator.Play;
- begin
- Play_Async;
- Assert(Application <> nil, 'TMsgSimulator.Play: Application = nil');
- while (not Application.Terminated) and (not AbortSim) and (not PlayDone) do begin
- Application.ProcessMessages;
- Sleep(1);
- end;
- end;
- procedure UnHook;
- begin
- Win32Check(UnhookWindowsHookEx(CurSim.play_hk));
- CurSim.play_hk := 0;
- CurSim.PlayDone := True;
- CurSim.StopTime := GetTickCount;
- CurSim.FRunning := False;
- CurSim := nil;
- end;
- function JournalPlaybackProc(code: integer; wp: WParam; lp: LPARAM): LResult; stdcall;
- var
- pe : PEventMsg;
- begin
- Assert(CurSim <> nil, 'CurSim = nil!');
- Assert(CurSim.PlayDone = False, 'Still Playing?');
- Result := CallNextHookEx(CurSim.play_hk, code, wp, lp);
- if code < 0 then exit;
- if CurSim.AbortSim then begin
- UnHook;
- exit;
- end;
- if code = HC_GETNEXT then begin
- pe := @CurSim.Messages[Cur].em;
- PEventMsg(lp)^ := pe^;
- Result := 0;
- if (NumCur = 0) and (Cur > 0) then begin
- Result := CurSim.Messages[Cur].em.time - CurSim.Messages[Cur-1].em.time;
- end;
- NumCur := NumCur + 1;
- exit;
- end;
- if code = HC_SKIP then begin
- Cur := Cur + 1;
- NumCur := 0;
- if Cur = CurSim.Messages.Count then begin
- UnHook;
- end;
- exit;
- end;
- end;
- procedure TMsgSimulator.FixUp_Playback_Delays;
- var
- i : integer;
- begin
- for i := 0 to Messages.Count-1 do begin
- Messages[i].Fill_EM_From_Props;
- if i = 0 then Messages[i].em.time := 0
- else Messages[i].em.time := Messages[i-1].em.time + Messages[i].Delay;
- // TODO: Fix up HWNDs? -bpz
- end;
- end;
- // This function returns immediately
- procedure TMsgSimulator.Play_Async;
- begin
- StartTime := GetTickCount;
- StopTime := StartTime;
- if Messages.Count = 0 then exit;
- FRunning := True;
- AbortSim := False;
- PlayDone := False;
- Assert(CurSim = nil, 'A TMsgSimulator is already playing or recording!');
- CurSim := Self;
- FixUp_Playback_Delays;
- // Set up the JournalPlayback Hook
- Cur := 0;
- NumCur := 0;
- play_hk := SetWindowsHookEx(WH_JOURNALPLAYBACK, JournalPlaybackProc, HInstance, 0);
- end;
- function TMsgSimulator.GetElapTime: integer;
- begin
- if Running then
- Result := GetTickCount - StartTime
- else
- Result := StopTime - StartTime;
- end;
- procedure TMsgSimulator.Abort;
- begin
- Assert(Running, 'Must be running to Abort!');
- AbortSim := True;
- end;
- function JournalRecordProc(code: integer; wp: WParam; lp: LPARAM): LResult; stdcall;
- var
- pe : PEventMsg;
- mi : TMessageItem;
- begin
- Result := 0;
- case code of
- HC_ACTION : if (CurSim.StopRec = 0) then begin
- pe := PEventMsg(lp);
- if (pe.message = WM_KEYDOWN) and ((pe.paramL and 255) = VK_CANCEL) then begin
- CurSim.Stop_Record;
- exit;
- end;
- mi := CurSim.Messages.Add;
- mi.em := pe^;
- mi.Fill_Props_From_EM;
- end;
- HC_SYSMODALON : Inc(CurSim.StopRec);
- HC_SYSMODALOFF : Dec(CurSim.StopRec);
- end;
- end;
- procedure TMsgSimulator.Record_Input;
- begin
- Assert(CurSim = nil, 'A TMsgSimulator is already playing or recording!');
- CurSim := Self;
- StopRec := 0;
- Messages.Clear;
- FRecording := True;
- rec_hk := SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProc, HInstance, 0);
- end;
- procedure TMsgSimulator.FixUp_Record_Delays;
- var
- i : integer;
- begin
- for i := 0 to Messages.Count-1 do begin
- if i = Messages.Count-1 then Messages[i].Delay := 0
- else Messages[i].Delay := Messages[i+1].em.time - Messages[i].em.time;
- end;
- end;
- procedure TMsgSimulator.Stop_Record;
- begin
- if Recording then begin
- Win32Check(UnhookWindowsHookEx(CurSim.rec_hk));
- rec_hk := 0;
- CurSim := nil;
- FRecording := False;
- FixUp_Record_Delays;
- if Assigned(OnStopRecord) then
- OnStopRecord(Self); // This is useful when the user hits CTRL-BREAK to stop recording rather than pressing a "Stop" button
- end;
- end;
- procedure TMsgSimulator.FocusWin(hwnd: THandle);
- var
- tmp : THandle;
- begin
- // Get the top-level window
- tmp := hwnd;
- while GetParent(tmp)<>0 do
- tmp := GetParent(tmp);
- SetForegroundWindow(tmp);
- Windows.SetFocus(hwnd);
- end;
- function EnumWindowsProc(hwnd: THandle; lp: LParam): boolean; stdcall;
- var
- buf : array[0..MAX_PATH] of char;
- ms : TMsgSimulator;
- begin
- Result := True;
- ms := TMsgSimulator(lp);
- Assert(ms<>nil);
- GetWindowText(hwnd, buf, sizeof(buf));
- if Pos(ms.FindText, buf)<>0 then ms.FindHandle := hwnd;
- end;
- function TMsgSimulator.FindTopLevelWin(const FindText: string): THandle;
- begin
- Self.FindText := FindText;
- FindHandle := DWORD(-1);
- EnumWindows(@EnumWindowsProc, LParam(Self));
- Result := FindHandle;
- end;
- initialization
- CurSim := nil;
- end.