MsgSimulator.pas
上传用户:llfxmlw
上传日期:2009-09-14
资源大小:335k
文件大小:18k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. unit MsgSimulator;
  2. {
  3.    June 23, 1998   by Ben Ziegler
  4.    6/30/98 - Added a Record Macro function
  5. }
  6. interface
  7. uses
  8.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
  9. type
  10.    TWMMessage = (mmMouseDown, mmMouseUp, mmMouseMove, mmKeyDown, mmKeyUp);
  11.    TMessageItem = class(TCollectionItem)
  12.    protected
  13.       em          : TEventMsg;      // Structure required by JournalPlayback Proc
  14.       FMsg        : TWMMessage;
  15.       FDelay      : DWORD;          // Delay in msec before next message is played
  16.       FX          : integer;        // This means nothing for keystrokes
  17.       FY          : integer;        // This means nothing for keystrokes
  18.       FKey        : integer;        // This means nothing for mouse clicks
  19.       FHWND       : integer;        // Window Handle (not used for keystrokes)
  20.       FButton     : TMouseButton;   // This means nothing for keystrokes
  21.       procedure   Fill_EM_From_Props;
  22.       procedure   Fill_Props_From_EM;
  23.    public
  24.       constructor Create(Collection: TCollection); override;
  25.       property    HWND        : integer read FHWND write FHWND; // No need to save it - it will be different after each run
  26.    published
  27.       property    Msg         : TWMMessage read FMsg write FMsg;
  28.       property    PosX        : integer read FX write FX;
  29.       property    PosY        : integer read FY write FY;
  30.       property    VkKey       : integer read FKey write FKey;
  31.       property    Delay       : DWORD read FDelay write FDelay;
  32.       property    Button      : TMouseButton read FButton write FButton;
  33.    end;
  34.    TMsgSimulator = class;
  35.    TMessageCollection = class(TCollection)
  36.    private
  37.      FOwner       : TMsgSimulator;
  38.      function     GetItem(Index: Integer): TMessageItem;
  39.      procedure    SetItem(Index: Integer; Value: TMessageItem);
  40.    protected
  41.      function     GetOwner: TPersistent; override;
  42.      procedure    Update(Item: TCollectionItem); override;
  43.    public
  44.      constructor  Create(AOwner: TMsgSimulator);
  45.      function     Add: TMessageItem;
  46.      property     Owner: TMsgSimulator read FOwner;
  47.      property     Items[Index: Integer]: TMessageItem read GetItem write SetItem; default;
  48.    end;
  49.    TMsgSimulator = class(TComponent)
  50.    protected
  51.       FRunning    : boolean;     // Simulation is currently running
  52.       play_hk     : THandle;     // JournalPlayback Hook handle
  53.       rec_hk      : THandle;     // RecordPlayback Hook handle
  54.       PlayDone    : boolean;     // Flag to signal that all messages have been simulated
  55.       AbortSim    : boolean;     // Flag to signal aborting the playback of messages
  56.       StartTime   : DWORD;       // Time simulation started (msec)
  57.       StopTime    : DWORD;       // Time simulation stoped (msec)
  58.       FDelay      : integer;     // Default delay between messages
  59.       FMsgList    : TMessageCollection; // Messages to playback
  60.       FTopWin     : string;
  61.       FindText    : string;
  62.       FindHandle  : THandle;
  63.       StopRec     : integer;
  64.       FRecording  : boolean;
  65.       FOnStopRec  : TNotifyEvent;
  66.       function    GetElapTime: integer;
  67.       procedure   SetMsgList(MsgList: TMessageCollection);
  68.       function    Add_Raw_Message(Msg: TWMMessage; x, y, VkKey, Delay, HWND: integer; Button: TMouseButton): TMessageItem;
  69.       procedure   Add_Shift(hwnd: THandle; Shift: TShiftState; UpDown: TWMMessage; Delay: integer);
  70.       procedure   SimClientToScreen(hwnd: THandle; var x, y: integer);
  71.       procedure   FixUp_Playback_Delays;
  72.       procedure   FixUp_Record_Delays;
  73.    public
  74.       constructor Create(AOwner: TComponent); override;
  75.       destructor  Destroy; override;
  76.       // Low-level Message Creation Functions
  77.       procedure   Add_ClickEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
  78.                      x, y, Delay: integer);
  79.       procedure   Add_DragEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
  80.                      StartX, StartY, StopX, StopY, NumMoves, Delay: integer);
  81.       procedure   Add_VirtualKey(hwnd: THandle; VkKey, Delay: integer; UpDown: TWMMessage);
  82.       // High-level Message Creation Functions
  83.       procedure   Add_Window_Click(hwnd: THandle; x, y: integer);
  84.       procedure   Add_Window_Drag(hwnd: THandle; StartX, StartY, StopX, StopY: integer);
  85.       procedure   Add_Screen_Click(x, y: integer);
  86.       procedure   Add_Screen_Drag(StartX, StartY, StopX, StopY: integer);
  87.       procedure   Add_ASCII_Keys(const Keystrokes: string);
  88.    public
  89.       // Playback & Cancel Functions
  90.       procedure   Play;                      // Plays messages, then returns
  91.       procedure   Play_Async;                // Returns immediately
  92.       procedure   Abort;
  93.       procedure   Record_Input;
  94.       procedure   Stop_Record;
  95.       property    Running: boolean read FRunning;
  96.       property    Recording: boolean read FRecording;
  97.       property    ElapTime: integer read GetElapTime; // Elapsed running time in msec
  98.       // Helper Functions
  99.       procedure   FocusWin(hwnd: THandle);
  100.       function    FindTopLevelWin(const FindText: string): THandle;
  101.    published
  102.       property    Messages: TMessageCollection read FMsgList write SetMsgList;
  103.       property    DefaultDelay: integer read FDelay write FDelay default 50;
  104.       property    OnStopRecord: TNotifyEvent read FOnStopRec write FOnStopRec;
  105.    end;
  106. procedure Register;
  107. implementation
  108. var
  109.    CurSim   : TMsgSimulator;  // Only one TMsgSimulator can play at a time
  110.    Cur      : integer;        // Current Message to play in the MsgList
  111.    NumCur   : integer;        // Number of times current message has been played
  112. procedure Register;
  113. begin
  114.   RegisterComponents('Samples', [TMsgSimulator]);
  115. end;
  116. // *********************************************************************
  117. // TMessageItem
  118. constructor TMessageItem.Create(Collection: TCollection);
  119. begin
  120.    inherited;
  121.    Delay := TMessageCollection(Collection).Owner.DefaultDelay;
  122. end;
  123. procedure TMessageItem.Fill_EM_From_Props;
  124. begin
  125.    em.hwnd  := hwnd;
  126.    if (Msg = mmMouseDown) and (Button = mbLeft)   then em.message := WM_LBUTTONDOWN;
  127.    if (Msg = mmMouseUp)   and (Button = mbLeft)   then em.message := WM_LBUTTONUP;
  128.    if (Msg = mmMouseDown) and (Button = mbRight)  then em.message := WM_RBUTTONDOWN;
  129.    if (Msg = mmMouseUp)   and (Button = mbRight)  then em.message := WM_RBUTTONUP;
  130.    if (Msg = mmMouseDown) and (Button = mbMiddle) then em.message := WM_MBUTTONDOWN;
  131.    if (Msg = mmMouseUp)   and (Button = mbMiddle) then em.message := WM_MBUTTONUP;
  132.    case Msg of
  133.       mmMouseMove : em.message := WM_MOUSEMOVE;
  134.       mmKeyDown   : em.message := WM_KEYDOWN;
  135.       mmKeyUp     : em.message := WM_KEYUP;
  136.    end;
  137.    if (Msg = mmKeyDown) or (Msg = mmKeyUp) then begin
  138.       // Keystroke Message
  139.       em.paramL := VkKey;
  140.       em.paramH := MapVirtualKey(VkKey, 0);
  141.    end else begin
  142.       // Mouse Message
  143.       em.paramL := PosX;
  144.       em.paramH := PosY;
  145.    end;
  146. end;
  147. procedure TMessageItem.Fill_Props_From_EM;
  148. begin
  149.    hwnd := em.hwnd;
  150.    case em.message of
  151.       WM_LBUTTONDOWN : begin Msg := mmMouseDown; Button := mbLeft;   end;
  152.       WM_LBUTTONUP   : begin Msg := mmMouseUp;   Button := mbLeft;   end;
  153.       WM_RBUTTONDOWN : begin Msg := mmMouseDown; Button := mbRight;  end;
  154.       WM_RBUTTONUP   : begin Msg := mmMouseUp;   Button := mbRight;  end;
  155.       WM_MBUTTONDOWN : begin Msg := mmMouseDown; Button := mbMiddle; end;
  156.       WM_MBUTTONUP   : begin Msg := mmMouseUp;   Button := mbMiddle; end;
  157.       WM_MOUSEMOVE   : Msg := mmMouseMove;
  158.       WM_KEYDOWN     : Msg := mmKeyDown;
  159.       WM_KEYUP       : Msg := mmKeyUp;
  160.    end;
  161.    if (Msg = mmKeyDown) or (Msg = mmKeyUp) then begin
  162.       // Keystroke Message
  163.       VkKey := em.paramL;
  164.    end else begin
  165.       // Mouse Message
  166.       PosX := em.paramL;
  167.       PosY := em.paramH;
  168.    end;
  169. end;
  170. // *********************************************************************
  171. // TMessageCollection
  172. constructor TMessageCollection.Create(AOwner: TMsgSimulator);
  173. begin
  174.   inherited Create(TMessageItem);
  175.   FOwner := AOwner;
  176. end;
  177. function TMessageCollection.Add: TMessageItem;
  178. begin
  179.   Result := TMessageItem(inherited Add);
  180. end;
  181. function TMessageCollection.GetItem(Index: Integer): TMessageItem;
  182. begin
  183.   Result := TMessageItem(inherited GetItem(Index));
  184. end;
  185. function TMessageCollection.GetOwner: TPersistent;
  186. begin
  187.   Result := FOwner;
  188. end;
  189. procedure TMessageCollection.SetItem(Index: Integer; Value: TMessageItem);
  190. begin
  191.   inherited SetItem(Index, Value);
  192. end;
  193. procedure TMessageCollection.Update(Item: TCollectionItem);
  194. begin
  195.    Assert(not FOwner.Running);
  196. end;
  197. // *********************************************************************
  198. // TMsgSimulator
  199. constructor TMsgSimulator.Create(AOwner: TComponent);
  200. begin
  201.    inherited;
  202.    FDelay   := 50;
  203.    FMsgList := TMessageCollection.Create(Self);
  204. end;
  205. destructor TMsgSimulator.Destroy;
  206. begin
  207.    if Running then Abort;
  208.    FMsgList.Free;
  209.    FMsgList := nil;
  210.    inherited;
  211. end;
  212. procedure TMsgSimulator.SetMsgList(MsgList: TMessageCollection);
  213. begin
  214.    FMsgList.Assign(MsgList);
  215. end;
  216. function TMsgSimulator.Add_Raw_Message(Msg: TWMMessage; x, y, VkKey, Delay, HWND: integer; Button: TMouseButton): TMessageItem;
  217. begin
  218.    Result := Messages.Add;
  219.    Result.Msg    := Msg;
  220.    Result.PosX   := x;
  221.    Result.PosY   := y;
  222.    Result.VkKey  := VkKey;
  223.    Result.Delay  := Delay;
  224.    Result.HWND   := HWND;
  225.    Result.Button := Button;
  226. end;
  227. procedure TMsgSimulator.Add_Shift(hwnd: THandle; Shift: TShiftState; UpDown: TWMMessage; Delay: integer);
  228. begin
  229.    // NOTE:  Keystrokes do not require an hwnd, so use 0
  230.    if Shift = [] then exit;
  231.    if ssShift in Shift then Add_Raw_Message(UpDown, 0, 0, VK_SHIFT, Delay, 0, mbLeft);
  232.    if ssCtrl  in Shift then Add_Raw_Message(UpDown, 0, 0, VK_CONTROL, Delay, 0, mbLeft);
  233.    if ssAlt   in Shift then Add_Raw_Message(UpDown, 0, 0, VK_MENU, Delay, 0, mbLeft);
  234. end;
  235. // x, y are in Screen coordinates
  236. procedure TMsgSimulator.Add_ClickEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
  237.                x, y, Delay: integer);
  238. begin
  239.    Add_Shift(hwnd, Shift, mmKeyDown, Delay);
  240.    Add_Raw_Message(mmMouseDown, x, y, 0, Delay, hwnd, Button);
  241.    Add_Raw_Message(mmMouseUp, x, y, 0, Delay, hwnd, Button);
  242.    Add_Raw_Message(mmMouseMove, x, y, 0, Delay, hwnd, Button);
  243.    Add_Shift(hwnd, Shift, mmKeyUp, Delay);
  244. end;
  245. // x, y are in Screen coordinates
  246. procedure TMsgSimulator.Add_DragEx(hwnd: THandle; Button: TMouseButton; Shift: TShiftState;
  247.                StartX, StartY, StopX, StopY, NumMoves, Delay: integer);
  248. var
  249.    i, x, y : integer;
  250. begin
  251.    Add_Shift(hwnd, Shift, mmKeyDown, Delay);
  252.    Add_Raw_Message(mmMouseDown, StartX, StartY, 0, Delay, hwnd, Button);
  253.    for i := 0 to NumMoves do begin
  254.       x := (StopX - StartX) * i div NumMoves + StartX;
  255.       y := (StopY - StartY) * i div NumMoves + StartY;
  256.       Add_Raw_Message(mmMouseMove, x, y, 0, Delay, hwnd, Button);
  257.    end;
  258.    Add_Raw_Message(mmMouseUp, StopX, StopY, 0, Delay, hwnd, Button);
  259.    Add_Shift(hwnd, Shift, mmKeyUp, Delay);
  260. end;
  261. procedure TMsgSimulator.Add_VirtualKey(hwnd: THandle; VkKey, Delay: integer; UpDown: TWMMessage);
  262. begin
  263.    Add_Raw_Message(upDown, 0, 0, vkKey, Delay, hwnd, mbLeft);
  264. end;
  265. procedure TMsgSimulator.SimClientToScreen(hwnd: THandle; var x, y: integer);
  266. var
  267.    p : TPoint;
  268. begin
  269.    if hwnd = 0 then exit;
  270.    p := Point(x, y);
  271.    Windows.ClientToScreen(hwnd, p);
  272.    x := p.x;
  273.    y := p.y;
  274. end;
  275. // x, y are in the Window's coordinates
  276. procedure TMsgSimulator.Add_Window_Click(hwnd: THandle; x, y: integer);
  277. begin
  278.    SimClientToScreen(hwnd, x, y);
  279.    Add_ClickEx(hwnd, mbLeft, [], x, y, DefaultDelay);
  280. end;
  281. // StartXY & StopXY are in the Window's coordinates
  282. procedure TMsgSimulator.Add_Window_Drag(hwnd: THandle; StartX, StartY, StopX, StopY: integer);
  283. begin
  284.    SimClientToScreen(hwnd, StartX, StartY);
  285.    SimClientToScreen(hwnd, StopX, StopY);
  286.    Add_DragEx(hwnd, mbLeft, [], StartX, StartY, StopX, StopY, 10, DefaultDelay);
  287. end;
  288. // x, y are in Screen coordinates
  289. procedure TMsgSimulator.Add_Screen_Click(x, y: integer);
  290. var
  291.    hwnd : THandle;
  292. begin
  293.    hwnd := Windows.WindowFromPoint(Point(x, y));
  294.    Add_ClickEx(hwnd, mbLeft, [], x, y, DefaultDelay);
  295. end;
  296. // x, y are in Screen coordinates
  297. procedure TMsgSimulator.Add_Screen_Drag(StartX, StartY, StopX, StopY: integer);
  298. var
  299.    hwnd : THandle;
  300. begin
  301.    hwnd := Windows.WindowFromPoint(Point(StartX, StartY));
  302.    Add_DragEx(hwnd, mbLeft, [], StartX, StartY, StopX, StopY, 10, DefaultDelay);
  303. end;
  304. procedure TMsgSimulator.Add_ASCII_Keys(const Keystrokes: string);
  305. var
  306.    i     : integer;
  307.    c     : byte;
  308.    Shift : boolean;
  309. begin
  310.    for i := 1 to Length(Keystrokes) do begin
  311.       c := VkKeyScan(Keystrokes[i]) and 255;
  312.       Shift := (VkKeyScan(Keystrokes[i]) and 256) <> 0;
  313.       if Shift then Add_Raw_Message(mmKeyDown, 0, 0, VK_SHIFT, 1 {DefaultDelay}, 0, mbLeft);
  314.       Add_Raw_Message(mmKeyDown, 0, 0, c, DefaultDelay, 0, mbLeft);
  315.       Add_Raw_Message(mmKeyUp, 0, 0, c, 1 {DefaultDelay}, 0, mbLeft);
  316.       if Shift then Add_Raw_Message(mmKeyUp, 0, 0, VK_SHIFT, 1 {DefaultDelay}, 0, mbLeft);
  317.    end;
  318. end;
  319. procedure TMsgSimulator.Play;
  320. begin
  321.    Play_Async;
  322.    Assert(Application <> nil, 'TMsgSimulator.Play:  Application = nil'); 
  323.    while (not Application.Terminated) and (not AbortSim) and (not PlayDone) do begin
  324.       Application.ProcessMessages;
  325.       Sleep(1);
  326.    end;
  327. end;
  328. procedure UnHook;
  329. begin
  330.    Win32Check(UnhookWindowsHookEx(CurSim.play_hk));
  331.    CurSim.play_hk  := 0;
  332.    CurSim.PlayDone := True;
  333.    CurSim.StopTime := GetTickCount;
  334.    CurSim.FRunning := False;
  335.    CurSim := nil;
  336. end;
  337. function JournalPlaybackProc(code: integer; wp: WParam; lp: LPARAM): LResult; stdcall;
  338. var
  339.    pe : PEventMsg;
  340. begin
  341.    Assert(CurSim <> nil, 'CurSim = nil!');
  342.    Assert(CurSim.PlayDone = False, 'Still Playing?');
  343.    Result := CallNextHookEx(CurSim.play_hk, code, wp, lp);
  344.    if code < 0 then exit;
  345.    if CurSim.AbortSim then begin
  346.       UnHook;
  347.       exit;
  348.    end;
  349.    if code = HC_GETNEXT then begin
  350.       pe := @CurSim.Messages[Cur].em;
  351.       PEventMsg(lp)^ := pe^;
  352.       Result := 0;
  353.       if (NumCur = 0) and (Cur > 0) then begin
  354.          Result := CurSim.Messages[Cur].em.time - CurSim.Messages[Cur-1].em.time;
  355.       end;
  356.       NumCur := NumCur + 1;
  357.       exit;
  358.    end;
  359.    if code = HC_SKIP then begin
  360.       Cur := Cur + 1;
  361.       NumCur := 0;
  362.       if Cur = CurSim.Messages.Count then begin
  363.          UnHook;
  364.       end;
  365.       exit;
  366.    end;
  367. end;
  368. procedure TMsgSimulator.FixUp_Playback_Delays;
  369. var
  370.    i : integer;
  371. begin
  372.    for i := 0 to Messages.Count-1 do begin
  373.       Messages[i].Fill_EM_From_Props;
  374.       if i = 0 then Messages[i].em.time := 0
  375.          else Messages[i].em.time := Messages[i-1].em.time + Messages[i].Delay;
  376.       // TODO:  Fix up HWNDs? -bpz
  377.    end;
  378. end;
  379. // This function returns immediately
  380. procedure TMsgSimulator.Play_Async;
  381. begin
  382.    StartTime := GetTickCount;
  383.    StopTime  := StartTime;
  384.    if Messages.Count = 0 then exit;
  385.    FRunning  := True;
  386.    AbortSim  := False;
  387.    PlayDone  := False;
  388.    Assert(CurSim = nil, 'A TMsgSimulator is already playing or recording!');
  389.    CurSim := Self;
  390.    FixUp_Playback_Delays;
  391.    // Set up the JournalPlayback Hook
  392.    Cur       := 0;
  393.    NumCur    := 0;
  394.    play_hk   := SetWindowsHookEx(WH_JOURNALPLAYBACK, JournalPlaybackProc, HInstance, 0);
  395. end;
  396. function TMsgSimulator.GetElapTime: integer;
  397. begin
  398.    if Running then
  399.       Result := GetTickCount - StartTime
  400.    else
  401.       Result := StopTime - StartTime;
  402. end;
  403. procedure TMsgSimulator.Abort;
  404. begin
  405.    Assert(Running, 'Must be running to Abort!');
  406.    AbortSim := True;
  407. end;
  408. function JournalRecordProc(code: integer; wp: WParam; lp: LPARAM): LResult; stdcall;
  409. var
  410.    pe : PEventMsg;
  411.    mi : TMessageItem;
  412. begin
  413.    Result := 0;
  414.    case code of
  415.       HC_ACTION : if (CurSim.StopRec = 0) then begin
  416.          pe := PEventMsg(lp);
  417.          if (pe.message = WM_KEYDOWN) and ((pe.paramL and 255) = VK_CANCEL) then begin
  418.             CurSim.Stop_Record;
  419.             exit;
  420.          end;
  421.          mi := CurSim.Messages.Add;
  422.          mi.em := pe^;
  423.          mi.Fill_Props_From_EM;
  424.          end;
  425.       HC_SYSMODALON  : Inc(CurSim.StopRec);
  426.       HC_SYSMODALOFF : Dec(CurSim.StopRec);
  427.    end;
  428. end;
  429. procedure TMsgSimulator.Record_Input;
  430. begin
  431.    Assert(CurSim = nil, 'A TMsgSimulator is already playing or recording!');
  432.    CurSim  := Self;
  433.    StopRec := 0;
  434.    Messages.Clear;
  435.    FRecording := True;
  436.    rec_hk := SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProc, HInstance, 0);
  437. end;
  438. procedure TMsgSimulator.FixUp_Record_Delays;
  439. var
  440.    i : integer;
  441. begin
  442.    for i := 0 to Messages.Count-1 do begin
  443.       if i = Messages.Count-1 then Messages[i].Delay := 0
  444.          else Messages[i].Delay := Messages[i+1].em.time - Messages[i].em.time;
  445.    end;
  446. end;
  447. procedure TMsgSimulator.Stop_Record;
  448. begin
  449.    if Recording then begin
  450.       Win32Check(UnhookWindowsHookEx(CurSim.rec_hk));
  451.       rec_hk  := 0;
  452.       CurSim := nil;
  453.       FRecording := False;
  454.       FixUp_Record_Delays;
  455.       if Assigned(OnStopRecord) then
  456.          OnStopRecord(Self);           // This is useful when the user hits CTRL-BREAK to stop recording rather than pressing a "Stop" button
  457.    end;
  458. end;
  459. procedure TMsgSimulator.FocusWin(hwnd: THandle);
  460. var
  461.    tmp : THandle;
  462. begin
  463.    // Get the top-level window
  464.    tmp := hwnd;
  465.    while GetParent(tmp)<>0 do
  466.       tmp := GetParent(tmp);
  467.    SetForegroundWindow(tmp);
  468.    Windows.SetFocus(hwnd);
  469. end;
  470. function EnumWindowsProc(hwnd: THandle; lp: LParam): boolean; stdcall;
  471. var
  472.    buf : array[0..MAX_PATH] of char;
  473.    ms  : TMsgSimulator;
  474. begin
  475.    Result := True;
  476.    ms := TMsgSimulator(lp);
  477.    Assert(ms<>nil);
  478.    GetWindowText(hwnd, buf, sizeof(buf));
  479.    if Pos(ms.FindText, buf)<>0 then ms.FindHandle := hwnd;
  480. end;
  481. function TMsgSimulator.FindTopLevelWin(const FindText: string): THandle;
  482. begin
  483.    Self.FindText := FindText;
  484.    FindHandle := DWORD(-1);
  485.    EnumWindows(@EnumWindowsProc, LParam(Self));
  486.    Result := FindHandle;
  487. end;
  488. initialization
  489.    CurSim := nil;
  490. end.