读取外部程序的标题和内容
Delphi 为我们提供了三个方便的函数:
GlobalAllocPtr {简化自 API 的 GlobalAlloc} GlobalReAllocPtr {简化自 API 的 GlobalReAlloc} GlobalFreePtr {简化自 API 的 GlobalFree}
读写本程序以外的数据时可以使用它们, 很方便, 譬如:
p := GlobalAllocPtr(0, Len); {分配} p := GlobalReAllocPtr(p, Len, 0); {重分配} GlobalFreePtr(p); {释放}
注意 GlobalAllocPtr 的第一个参数和 GlobalReAllocPtr 的最后一个参数, 上面给的都是 0;
这两个参数的意义是一样的, 规范一点应该写成 GMEM_FIXED (表示分配固定内存), 常用的参数还有:
GMEM_MOVEABLE {分配可移动内存} GMEM_ZEROINIT {同时清空内存} GHND {分配可移动内存同时清空} GPTR {分配固定内存同时清空}
procedure TForm1.Button2Click(Sender: TObject); var p: Pointer; Len: Integer; begin Len := 6+1; {假如想要读出 6 个字符, 要流出结束的空字符} p := GlobalAllocPtr(0, Len*2); {分配内存 Len*2 是针对双字节字符} SendMessage(Memo1.Handle, WM_GETTEXT, Len, Cardinal(p)); ShowMessage(PChar(p)); {CodeGe} {在上一例的基础上继续, 先获取实际长度} Len := SendMessage(Memo1.Handle, WM_GETTEXTLENGTH, 0, 0); Len := (Len + 1) * 2; p := GlobalReAllocPtr(p, Len, GHND); {重新分配内存} SendMessage(Memo1.Handle, WM_GETTEXT, Len, Cardinal(p)); ShowMessage(PChar(p)); {CodeGear Delphi 2009}
GlobalFreePtr(p);
end;
{获取已打开的所有记事本的标题} procedure TForm1.Button1Click(Sender: TObject); var h: HWnd; p: array[0..254] of char; begin Memo1.Clear; h := GetWindow(Handle, GW_HWNDFIRST); while h <> 0 do begin GetClassName(h, p, Length(p)); if p = 'Notepad' then begin GetWindowText(h, p, Length(p)); Memo1.Lines.Add(p); end; h := GetWindow(h, GW_HWNDNEXT); end; end;
--------------- 读取记事本的标题 ----------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var
p: Pointer;
Len: Integer;
h:hwnd;
begin
h:=findwindow('notepad',nil);
Len := 6+1; {假如想要读出 6 个字符, 要留出结束的空字符}
p := GlobalAllocPtr(0, Len*2); {分配内存 Len*2 是针对双字节字符}
SendMessage(h, WM_GETTEXT, Len, Cardinal(p));
ShowMessage(PChar(p)); {CodeGe}
GlobalFreePtr(p);
end;
--------------- 读取记事本的内容 ----------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var h: HWND; p: Pointer; Len: Integer; begin h := FindWindow('Notepad', nil); if h = 0 then Exit; h := GetWindow(h, GW_CHILD); if h = 0 then Exit; Len := SendMessage(h, WM_GETTEXTLENGTH, 0, 0) + 1; p := GlobalAllocPtr(0, Len * SizeOf(Char)); SendMessage(h, WM_GETTEXT, Len, Cardinal(p)); ShowMessage(PChar(p)); GlobalFreePtr(p); end;
//声明: GetWindow( hWnd: HWND; {指定的窗口句柄} uCmd: UINT {指定的关系选项} ): HWND; {失败返回0; 成功返回符合的窗口句柄} //uCmd 可选值: GW_HWNDFIRST = 0; {同级别第一个} GW_HWNDLAST = 1; {同级别最后一个} GW_HWNDNEXT = 2; {同级别下一个} GW_HWNDPREV = 3; {同级别上一个} GW_OWNER = 4; {属主窗口} GW_CHILD = 5; {子窗口}
{要有个 Memo 接受数据} procedure TForm1.Button1Click(Sender: TObject); var h: HWnd; p: array[0..254] of char; begin h := GetWindow(Handle, GW_HWNDFIRST); while h <> 0 do begin if GetWindowText(h, p, 255) > 0 then Memo1.Lines.Add(p); h := GetWindow(h, GW_HWNDNEXT); end; end;
{用 API 实现的获取文本容器中选择的文本的函数} function GetEditSeleteText(h: HWND): string; var len,sx,ex: Integer; {文本总长度, 选择的开始位置, 选择的结束位置} buf: PChar; {所有文本} begin {获取文本总长度} len := SendMessage(h, WM_GETTEXTLENGTH, 0, 0) + 1; {为接受所有文本的缓冲区分配内存} buf := GlobalAllocPtr(0, len); {这里没有使用 GetMem, 因为需要全局的, 不然无法面对其他程序} {获取所有文本} SendMessage(h, WM_GETTEXT, len, Longint(buf)); {获取选择的开始位置和结束位置} SendMessage(h, EM_GETSEL, Longint(@sx), Longint(@ex)); {截取选择的文本} Result := Copy(buf, sx+1, ex-sx); {释放内存} GlobalFreePtr(buf); end; {测试 TEdit, 同时与 VCL 的获取方法对比} procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage(GetEditSeleteText(Edit1.Handle) + ' - ' + Edit1.SelText); end;
var h: HWND; procedure TForm1.Timer1Timer(Sender: TObject); var pt: TPoint; arr: array[0..254] of Char; begin if GetCursorPos(pt) then {如果能获取点} begin h := WindowFromPoint(pt); {返回句柄} GetClassName(h, arr, Length(arr)); {获取该句柄窗口的类名} Text := arr; {显示在标题} end; end; end.
//声明: GetClassName( hWnd: HWND; {指定窗口句柄} lpClassName: PChar; {缓冲区} nMaxCount: Integer {缓冲区大小} ): Integer; {返回类名大小; 失败返回 0}
//测试1: 新建一个工程, 主窗口的类名默认是 TForm1, 用程序获取一下看看 var ps: array[0..254] of Char; begin GetClassName(Handle, ps, 255); ShowMessage(ps); {TForm1} end;
//测试2: 看看 "计算器" 窗口的类名(先启动计算器) var h: HWND; ps: array[0..254] of Char; begin h := FindWindow(nil, '计算器'); {这句是获取计算器窗口的句柄} GetClassName(h, ps, 255); ShowMessage(ps); {SciCalc} end;
//测试3: 看看记事本窗口的类名(先重新启动记事本): var h: HWND; ps: array[0..254] of Char; begin h := FindWindow(nil, '无标题 - 记事本'); {这句是获取记事本窗口的句柄} GetClassName(h, ps, 255); ShowMessage(ps); {Notepad} end;
FindWindow( lpClassName, {窗口的类名} lpWindowName: PChar {窗口的标题} ): HWND; {返回窗口的句柄; 失败返回 0} //FindWindowEx 比 FindWindow 多出两个句柄参数: FindWindowEx( Parent: HWND; {要查找子窗口的父窗口句柄} Child: HWND; {子窗口句柄} ClassName: PChar; {} WindowName: PChar {} ): HWND; { 如果 Parent 是 0, 则函数以桌面窗口为父窗口, 查找桌面窗口的所有子窗口; 如果 是 HWND_MESSAGE, 函数仅查找所有消息窗口; 子窗口必须是 Parent 窗口的直接子窗口; 如果 Child 是 0, 查找从 Parent 的第一个子窗口开始; 如果 Parent 和 Child 同时是 0, 则函数查找所有的顶层窗口及消息窗口. }
//测试1: 试着找找新建程序主窗口的句柄 var h: HWND; begin {现在我们知道窗口的标题是: Form1、窗口的类名是: TForm1} h := FindWindow('TForm1', 'Form1'); ShowMessage(IntToStr(h)); {656180; 这是随机, 每次启动窗口肯定不一样} {假如不知道类名} h := FindWindow(nil, 'Form1'); ShowMessage(IntToStr(h)); {656180} {假如不知道标题名} h := FindWindow('TForm1', nil); ShowMessage(IntToStr(h)); {656180} {其实这个窗口的句柄不就是 Self.Handle 吗} ShowMessage(IntToStr(Handle)); {656180} end;
//测试2: 找计算器窗口的句柄(先打开计算器) var h: HWND; begin {如果不是简体中文系统, 这样可能不灵} h := FindWindow(nil, '计算器'); ShowMessage(IntToStr(h)); {1508334} {最好这样, 但你得提前知道计算器窗口的类名是: SciCalc} h := FindWindow('SciCalc', nil); ShowMessage(IntToStr(h)); {1508334} end;
procedure TForm1.Button1Click(Sender: TObject); const className = 'IEFrame'; {这是 IE 浏览器的类名} var h: HWnd; buf: array[Byte] of Char; begin h := GetWindow(Handle, GW_HWNDFIRST); while h <> 0 do begin GetClassName(h, buf, Length(buf)); if buf = className then {找到咋处理? 显示它的标题吧} begin GetWindowText(h, buf, Length(buf)); Memo1.Lines.Add(buf) end; h := GetWindow(h, GW_HWNDNEXT); end; end;
枚举当前所有的 IE 窗口
procedure TForm1.Button1Click(Sender: TObject); const className = 'IEFrame'; {这是 IE 浏览器的类名} var h: HWnd; buf: array[Byte] of Char; begin h := GetWindow(Handle, GW_HWNDFIRST); while h <> 0 do begin GetClassName(h, buf, Length(buf)); if buf = className then {找到咋处理? 显示它的标题吧} begin GetWindowText(h, buf, Length(buf)); Memo1.Lines.Add(buf) end; h := GetWindow(h, GW_HWNDNEXT); end; end;
如果有个按钮,是disabled那种效果,就是灰掉,不能点的那种,怎么获取这样按钮句柄呢?
可以从母体中遍历控件找到它.
如果是使用鼠标探测的话, 可以通过判断鼠标指针是否在控件范围内来确认.
怎么判断鼠标指针是否在控件范围内?
可以通过控件的事件:
OnMouseEnter (进入控件范围)
OnMouseLeave (离开控件范围)
也可以用函数: PtInRect