為指定的父視窗枚舉子視窗、按鈕
很早就寫過類似spy++和查看密碼視窗的東西,一直想給這個小東西再加點特別的。前段時間對軟體安裝註冊發生了興趣,有些軟體如果你不輸入正確註冊碼,那該死的“下一步”按鈕就一直disable。這次我就讓spy++徹底spy到底,把那個註冊用的按鈕置亮,讓我輕鬆進入“下一步”,呵呵...。
我的想法是游標移到指定的視窗上後,探測這個視窗上到底有多少按鈕,如果有,就將它們都Enable。在這裡我不想討論怎樣具體實現這個功能,但你要知道的是想得到這些被disable視窗(按鈕)的控制代碼是根本無法通過WindowFromPoint這個API函數得到的,GetWindow也不要妄想了。順手查了下MSDN,看到EnumChildWindows可是個好東西,可以枚舉一個父視窗的所有子視窗:
- BOOL EnumChildWindows(
- HWND hWndParent, // handle to parent window // 父視窗控制代碼
- WNDENUMPROC lpEnumFunc, // callback function // 回呼函數的地址
- LPARAM lParam // application-defined value // 你自已定義的參數
- );
就這麼簡單,讓我們再定義一個回呼函數,像下面這樣:
- BOOL CALLBACK EnumChildProc(
- HWND hwnd, // handle to child window
- LPARAM lParam // application-defined value
- );
注意:這個回呼函數要麼是類的靜態函數,要麼就是一個全域的函數。
--------------------------------
在調用EnumChildWindows 這個函數時,直到調用到最個一個子視窗被枚舉或回呼函數返回一個false,否則將一直枚舉下去。有了上面的知識,我想你應該知道怎麼做了。有了回呼函數的概念及上面的例子,我們可以繼續了。其實想要找到一個標題已知的視窗控制代碼,用一個API函數就可以了:FindWindow.其函數原形是:
- function FindWindow(lpClassName, lpWindowName: PChar): HWND; stdcall;
- lpClassName:視窗類別名.如果只知道標題,可以為空白.視窗類別名可以用很多工具獲得.如winsignt32.
lpWindowName:視窗標題.
調用方式舉例:
- var wndhwnd:HWND;
- wndhwnd:=FindWindow(nil,'某視窗標題');
- if wndhwnd<>0 then file://找到此視窗控制代碼.
- begin
- xxxxx
- end
- else begin
- MessageBox(self.handle,'沒找到該視窗控制代碼','提示',0);
- end;
有了這個視窗控制代碼,就離我們的初始目的不遠了:控制其他表單上的視窗控制項.同樣,首先要得到其他表單上視窗控制項的控制代碼.我們用這個API函數:EnumChildWindows.其函數原形是:
- function EnumChildWindows(hWndParent: HWND;
- lpEnumFunc: TFNWndEnumProc;
- lParam: LPARAM): BOOL; stdcall;
這個函數和EnumWindow函數很有些想象.其作用也很相似.它的功能就是列舉視窗控制代碼為hWndParent的表單上所有的視窗控制項的控制代碼.同樣也是以回呼函數參數的形式給出的. 我們再舉一個實際的例子,來說明這個函數的用法.程式的功能是讓使用者輸入一個視窗標題,然後調用FindWindow函數找到此視窗控制代碼.通過這個控制代碼,我們在一個Memo裡顯示該視窗上所有的視窗控制項.同樣先編寫回呼函數.
- function EnumChildWndProc(AhWnd:LongInt;
- AlParam:lParam):boolean;stdcall;
- var
- WndClassName: array[0..254] of Char;
- WndCaption: array[0..254] of Char;
- begin
- GetClassName(AhWnd,wndClassName,254);
- GetWindowText(aHwnd,WndCaption,254);
- with form1.memo1 do
- begin
- lines.add( string(wndClassName));
- lines.add( string(wndCaption));
- lines.add('-------');
- end;
- result:=true;
- end;
然後在一事件裡調用EnumChildWindows函數.
- procedure TForm1.Button1Click(Sender: TObject);
- var
- hWnd:LongInt;
- begin
- memo1.Lines.Clear;
- Memo1.Lines.Add(Edit1.Text+' 有如下控制項類名稱');
- hWnd:=FindWindow(nil,pchar(Edit1.Text));
- if hWnd<>0 then
- begin
- EnumChildWindows(hWnd,@EnumChildWndProc,0);
- end
- else MessageBox(self.handle,'沒找到該視窗控制代碼','提示',0);
- end;
程式清單如下:
- unit Unit1;
- interface
- uses
- Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
- Dialogs, StdCtrls;
- type
- TForm1 = class(TForm)
- Memo1: TMemo; //用來顯示找到的控制項
- Label1: TLabel;
- Edit1: TEdit; //輸入標題.
- Button1: TButton;
- procedure Button1Click(Sender: TObject);
- private
- { Private declarations }
- public
- { Public declarations }
- end;
- var
- Form1: TForm1;
- function EnumChildWndProc(AhWnd:LongInt;
- AlParam:lParam):boolean;stdcall;
- implementation
- {$R *.dfm}
- function EnumChildWndProc(AhWnd:LongInt;
- AlParam:lParam):boolean;stdcall;
- var
- WndClassName: array[0..254] of Char;
- WndCaption: array[0..254] of Char;
- begin
- GetClassName(AhWnd,wndClassName,254);
- GetWindowText(aHwnd,WndCaption,254);
- with form1.memo1 do
- begin
- lines.add( string(wndClassName));
- lines.add( string(wndCaption));
- lines.add('-------');
- end;
- result:=true;
- end;
- procedure TForm1.Button1Click(Sender: TObject);
- var
- hWnd:LongInt;
- begin
- memo1.Lines.Clear;
- Memo1.Lines.Add(Edit1.Text+' 有如下控制項類名稱');
- hWnd:=FindWindow(nil,pchar(Edit1.Text));
- if hWnd<>0 then
- begin
- EnumChildWindows(hWnd,@EnumChildWndProc,0);
- end
- else MessageBox(self.handle,'沒找到該視窗控制代碼','提示',0);
- end;
- end.
有了控制項控制代碼,我們當然就可以隨心所欲了.比如:
- SendMessage(hWnd,WM_SETTEXT,0,LongInt(Pchar('sdafdsf')));
就可以給控制項發送文本.其他還可以發送不同的訊息可以做很多事情.但是,有很大一個問題:假設一個表單上有很多相同的控制項,並且根本沒辦法區分他們,即使我們能找到所有的控制項控制代碼,我們又不能區分到底哪個是我們想要的,同樣是干著急.我想了很長時間,後來在大富翁裡找到了答案,只要用到一個小技巧,就可以解決了.
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=588250