Thema: Delphi Messages aufzeichnen

Einzelnen Beitrag anzeigen

Astat

Registriert seit: 2. Dez 2009
Ort: München
320 Beiträge
 
Lazarus
 
#2

Re: Messages aufzeichnen

  Alt 9. Dez 2009, 09:26
Hallo XXcD,

Ist wahrscheinlich Ben Ziegler's Message Simulator Componente, die Du suchst!

Delphi-Quellcode:

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 AOwner: 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).AOwner.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.

lg. Astat
Lanthan Astat
06810110811210410503210511511603209711003210010110 9032084097103
03211611111604403209711003210010110903210010510103 2108101116122
11610103209010110510810103206711110010103210511003 2068101108112
10410503210310111509910411410510109810111003211910 5114100046
  Mit Zitat antworten Zitat