外部程序控制技术

 最近做一个项目,需要从外部控制挰序,好比做一个外挂,要获取游戏里各个控件的句柄,然后对它进行操作。在网上查了查,这方面的例子无一例外都是C++的,找不到Delphi的,在几个网站上问了,回答的人都说不知道,并且推荐我用C++进行开发,难道Delphi真的不能对外部程序操作?

经过一天的努力,我证明了Delphi也是可以做到的,并且比C++做起来更方便,我把它做成一个控件,以便随时拖出来就用。

 

 

 

 

 

  1. unit RaOuterControls;
  2. interface
  3. uses
  4. SysUtils, Classes, Windows, TlHelp32;
  5. type
  6. TProcessInfo = record
  7.     pHandle: Cardinal;
  8.     pClassName: string;
  9.     pText: string;
  10. end;
  11. type
  12. TOnSendMessage = procedure(Sender: TObject; SndMsgResult: Cardinal) of object;
  13. TOnWindowChange = procedure(Sender: TObject) of object;
  14. type
  15. TRaOuterControls = class(TComponent)
  16. private
  17.     fProcessHandle: THandle;
  18.     fTextList: TStringList;
  19.     fHandleList: TStringList;
  20.     fClassList: TStringList;
  21.     fWindowCaption: string;
  22.     fSM: Cardinal;
  23.     fSLP: Cardinal;
  24.     fSWP: Cardinal;
  25.     fSMH: THandle;
  26.     fOnSendMessage: TOnSendMessage;
  27.     fOnWindowChange: TOnWindowChange;
  28.     procedure SetProcessHandle(const Value: THandle);
  29.     procedure SetWindowCaption(const Value: string);
  30. protected
  31.     //function FindExeHandle(AExeName: string): THandle;
  32. public
  33.     constructor Create(AOwner: TComponent); override;
  34.     function GetProcessControlInfo(index: Integer): TProcessInfo;
  35.     procedure SendMessageToControl; overload;
  36.     procedure SendMessageToControl(hWnd: THandle; Msg: Cardinal; WParam: Cardinal; LParam: Cardinal); overload;
  37. published
  38.     property OnSendMessage: TOnSendMessage read fOnSendMessage write fOnSendMessage;
  39.     property OnWindowChange: TOnWindowChange read fOnWindowChange write fOnWindowChange;
  40.     property SndMsgHandle: THandle read fSMH write fSMH;
  41.     property SndMessage: Cardinal read fSM write fSM;
  42.     property SndLParam: Cardinal read fSLP write fSLP;
  43.     property SndWParam: Cardinal read fSWP write fSWP;
  44.     property ProcessHandle: THandle read fProcessHandle write SetProcessHandle;
  45.     property HandleList: TStringList read fHandleList;
  46.     property ClassList: TStringList read fClassList;
  47.     property TextList: TStringList read fTextList;
  48.     property WindowCaption: string read fWindowCaption write SetWindowCaption;
  49. end;
  50. var
  51. IHandleList: TStringList;
  52. IClassList: TStringList;
  53. ITextList: TStringList;
  54. function EnumChildWndProc(AhWnd: LongInt; AlParam: LParam): boolean; stdcall;
  55. procedure Register;
  56. implementation
  57. procedure Register;
  58. begin
  59. RegisterComponents('Rarnu Components', [TRaOuterControls]);
  60. end;
  61. function EnumChildWndProc(AhWnd: LongInt;
  62. AlParam: LParam): boolean; stdcall;
  63. var
  64. WndClassName: array[0..511of Char;
  65. WndCaption: array[0..511of Char;
  66. begin
  67. GetClassName(AhWnd, WndClassName, 512); //获取控件名称
  68. GetWindowText(AhWnd, WndCaption, 512); //获取控件标题
  69. IHandleList.Add(IntToStr(AhWnd));
  70. IClassList.Add(string(WndClassName));
  71. ITextList.Add(string(WndCaption));
  72. result := true;
  73. end;
  74. { TRaOuterControls }
  75. constructor TRaOuterControls.Create(AOwner: TComponent);
  76. begin
  77. inherited Create(AOwner);
  78. fTextList := TStringList.Create;
  79. fTextList.Clear;
  80. fHandleList := TStringList.Create;
  81. fHandleList.Clear;
  82. fClassList := TStringList.Create;
  83. fClassList.Clear;
  84. IHandleList := TStringList.Create;
  85. IHandleList.Clear;
  86. IClassList := TStringList.Create;
  87. IClassList.Clear;
  88. ITextList := TStringList.Create;
  89. ITextList.Clear;
  90. end;
  91. function TRaOuterControls.GetProcessControlInfo(
  92. index: Integer): TProcessInfo;
  93. var
  94. piInfo: TProcessInfo;
  95. begin
  96. piInfo.pHandle := 0;
  97. piInfo.pClassName := '';
  98. piInfo.pText := '';
  99. if fHandleList.Count - 1 < index then
  100. begin
  101.     result := piInfo;
  102.     Exit;
  103. end;
  104. piInfo.pHandle := StrToInt(fHandleList.Strings[index]);
  105. piInfo.pClassName := fClassList.Strings[index];
  106. piInfo.pText := fTextList.Strings[index];
  107. result := piInfo;
  108. end;
  109. procedure TRaOuterControls.SendMessageToControl;
  110. var
  111. SndResult: Cardinal;
  112. begin
  113. SndResult := SendMessage(fSMH, fSM, fSWP, fSLP);
  114. if Assigned(OnSendMessage) then
  115.     OnSendMessage(self, SndResult);
  116. end;
  117. procedure TRaOuterControls.SendMessageToControl(hWnd: THandle; Msg, WParam,
  118. LParam: Cardinal);
  119. var
  120. SndResult: Cardinal;
  121. begin
  122. SndResult := SendMessage(hWnd, Msg, WParam, LParam);
  123. if Assigned(OnSendMessage) then
  124.     OnSendMessage(self, SndResult);
  125. end;
  126. procedure TRaOuterControls.SetProcessHandle(const Value: THandle);
  127. begin
  128. fProcessHandle := Value;
  129. IHandleList.Clear;
  130. IClassList.Clear;
  131. ITextList.Clear;
  132. if fProcessHandle <> 0 then EnumChildWindows(fProcessHandle, @EnumChildWndProc, 0);
  133. fTextList := ITextList;
  134. fHandleList := IHandleList;
  135. fClassList := IClassList;
  136. if Assigned(OnWindowChange) then
  137.     OnWindowChange(self);
  138. end;
  139. procedure TRaOuterControls.SetWindowCaption(const Value: string);
  140. begin
  141. fWindowCaption := Value;
  142. ProcessHandle := FindWindow(nil, PChar(fWindowCaption));
  143. end;
  144. end.

 相信你一定看明白了,EnumChildWndProc 其实是一个回调函数,它本身就拥有递归的性质,result:=true表明它可以继续回调,直到条件不成立为止。利用内置API可以方便的完成类名和控件标题的获取,而用C++的话,此时必须先对记录进行声明,这个声明将花费大量的代码。

控件做完后,就开始做一个实例,很简单,我想把我输入在Memo里面的文本直接移动到记事本里,实现代码如下:

 

 

 

 

 

  1. unit Unit1;
  2. interface
  3. uses
  4. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5. Dialogs, ExtCtrls, StdCtrls, RaOuterControls;
  6. type
  7. TForm1 = class(TForm)
  8.     RaOuterControls1: TRaOuterControls;
  9.     Label1: TLabel;
  10.     Timer1: TTimer;
  11.     Label2: TLabel;
  12.     Memo1: TMemo;
  13.     Button1: TButton;
  14.     procedure Timer1Timer(Sender: TObject);
  15.     procedure FormCreate(Sender: TObject);
  16.     procedure Button1Click(Sender: TObject);
  17. private
  18.     { Private declarations }
  19. public
  20.     { Public declarations }
  21. end;
  22. var
  23. Form1: TForm1;
  24. NotePadHandle:THandle;
  25. implementation
  26. {$R *.dfm}
  27. procedure TForm1.Timer1Timer(Sender: TObject);
  28. begin
  29. NotePadHandle:=FindWindow(nil,'无标题 - 记事本');
  30. if NotePadHandle<>0 then
  31.     self.Label1.Caption:='新记事本已打开'
  32. else
  33.     self.Label1.Caption:='请打开一个空的记事本';
  34. end;
  35. procedure TForm1.FormCreate(Sender: TObject);
  36. begin
  37. self.Timer1Timer(self);
  38. end;
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. var
  41. i:integer;
  42. begin
  43. if NotePadHandle=0 then
  44. begin
  45.     ShowMessage('请打开一个新的记事本');
  46.     Exit;
  47. end;
  48. self.RaOuterControls1.ProcessHandle:=NotePadHandle;
  49. //self.ListBox1.Items:=self.RaOuterControls1.ClassList;
  50. for i:=0 to self.RaOuterControls1.ClassList.Count-1 do
  51. begin
  52.     if self.RaOuterControls1.ClassList.Strings[i]='Edit' then
  53.     begin
  54.       self.RaOuterControls1.SendMessageToControl
  55.       (StrToInt(RaOuterControls1.HandleList.Strings[i]),WM_SETTEXT,
  56.       0,Cardinal(PChar(memo1.Lines.Text)));
  57.       Exit;
  58.     end;
  59. end;
  60. end;
  61. end.

除去大部分系统生成的代码外,几乎都是对控件的操作,这里提一下,虽然PCHAR保留过程返回的值是AnsiString,但是却可以用数值形转换,这里用 Cardinal进行了转换,但是实际用中,个人认为还是用LongInt转换比较好,LongInt可以与其他开发平台兼容,而Cardinal仅局限于delphi中。它的原理是把文本转成整型数组的形式存到内存中,然后通过SendMessage函数进行发送。

在遍历中,由于事先知道控件的名称,所以直接用了判断,如果不知道的话还需进一步判断。我在这个控件中封装了GetProcessControlInfo函数,它返回选中的一个记录,使用起来会更加的方便。

测试一下做好的程序,果然,原来在窗体上的文本已经跑到记事本里面去了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值