1. 程式人生 > 其它 >Delphi 查詢標題已知的視窗控制代碼,遍歷視窗控制元件控制代碼(轉)

Delphi 查詢標題已知的視窗控制代碼,遍歷視窗控制元件控制代碼(轉)

用我的方法來控制其他程式窗體上的視窗控制元件,必須先了解什麼是回撥函式。我的理解是這樣的:


回 調函式寫出來不是自己的程式去呼叫的,反而是讓其他的東西去呼叫,比如windows作業系統,比如其他的程式等等之類的。但是什麼時候被呼叫卻不知道 了。回撥函式一般是按照呼叫者的要求定義好引數和返回值的型別,你向呼叫者提供你的回撥函式的入口地址,然後呼叫者有什麼事件發生的時候就可以隨時按照你 提供的地址呼叫這個函式通知你,並按照預先規定好的形式傳遞引數。所以很多人打比方,說回撥函式還真有點像您隨身帶的BP機:告訴別人號碼,在它有事情時 Call您!

所以一個回撥函式寫出來之後,一定有個註冊的動作,就是告訴呼叫者,你怎麼樣找到我寫的函式。某些Windows API 函式會要求以回撥函式地址作為其引數之一,例如SetTimer 、LineDDA 、EnumObjects,以及我們下面要用到的EnumWindows。

在Delphi裡宣告一個回撥函式的格式很簡單,例如:

function EnumWindowsProc(AhWnd:LongInt;lParam:LongInt):boolean;stdcall;

首先是函式名稱可以隨便亂取,但函式引數的型別一般不得亂來,其順序,資料型別等都有規定的,因為這些都是讓其他程式呼叫的,他們已經規定好了的,但引數名稱可以隨便亂叫。注意後面一定要帶上“stdcall”,

stdcall是標準呼叫,也就是說採用標準windows引數傳遞方式來呼叫函式。

編寫函式體就很簡單了,利用傳遞過來的引數就可以了,只要記住,這些引數是別人送給你的,你只要知道這些引數代表了什麼意思。

再看個向呼叫者註冊回撥函式入口地址的函式。
function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam:LPARAM): BOOL; stdcall;

TFNWndEnumProc其實就是指標型別。其中的lpEnumFunc就是回撥函式的入口地址了。

下面是呼叫EnumWindows的格式:
EnumWindows(@EnumWindowsProc,0);

通過向系統註冊回撥函式的入口地址,系統就能在需要的時候,呼叫回撥函式,傳遞引數給它,也許這些引數就是我們想要的。

EnumWindows函式的功能是:列舉螢幕上所有程式中的頂層視窗,將視窗控制代碼以引數的形式傳遞給回撥函式。找到一個視窗,就呼叫一次回撥函式。列舉結束的條件是:要麼列舉完所有的視窗,要麼回撥函式返回False。

lParam: LPARAM引數是程式定義的值,這個值被傳遞到回撥函式。

回過頭來再看一下EnumWindowsProc:

function EnumWindowsProc(AhWnd:LongInt;lParam:LongInt):boolean;stdcall;

當系統找到了一個視窗後,就開始呼叫這個回撥函式,將視窗的控制代碼作為第一個引數傳遞過來,將在EnumWindows中lParam: LPARAM這個程式定義的值作為第二個引數傳遞過來。

所以我們可以在EnumWindowsProc函式中利用傳遞過來的兩個引數來做某些處理了。

下面我們新建一個程式列舉系統中所有程式的頂層視窗,我們要得到視窗的標題,要得到視窗類名稱。

得到視窗標題用:

function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer; stdcall;

該函式功能是將視窗控制代碼為hWnd的視窗的標題拷入到一個緩衝區lpString。nMaxCount是拷入緩衝區內的最大的字元數。

要得到視窗標題還可以傳送訊息:WM_GETTEXT,其實GetWindowText就是傳送WM_GETTEXT訊息的。

要得到視窗類名稱用:

function GetClassName(hWnd: HWND; lpClassName: PChar; nMaxCount: Integer): Integer; stdcall;

其引數意義和上面的函式差不多。不詳細解釋了。

我們先編寫回調函式:EnumWindowsProc。現在告訴自己,我們已經有了兩個引數的值了。這兩個引數是系統給我們的.

為了顯示視窗標題和類名,我們用一個TMemo控制元件。

先在interface部分宣告函式。

function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;stdcall;

注意我將第二個引數改了,不要緊,到時候呼叫的時候注意看。

然後在implementation部分定義函式:
function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;
var
lpszClassName,lpszWindowText:array[0..254] of char; //定義兩個緩衝區。
begin
GetWindowText(AhWnd,lpszWindowText,254);//得到視窗標題
GetClassName(AhWnd,lpszClassName,254);//得到視窗類名。
Aform.memo1.lines.add(StrPas(lpszWindowText));
Aform.memo1.lines.add(StrPas(lpszClassName));
Aform.memo1.lines.add('--------------------');
Result:=True;
end;

接著需要做的就是呼叫EnumWindows函式,註冊回撥函式入口地址,讓系統呼叫回撥函式,列舉視窗了。所以再新增一個TButton: btn_listwindow
procedure TForm1.btn_listwindowClick(Sender: TObject);
begin
EnumWindows(@EnumWindowsProc,LongInt(self));
end;

===================================================================================================

有了回撥函式的概念及上面的例子,我們可以繼續了。其實想要找到一個標題已知的視窗控制代碼,用一個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; file://用來顯示找到的控制元件
Label1: TLabel; 
Edit1: TEdit;  file://輸入標題.
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')));就可以給控制元件傳送文字.其他還可以傳送不同的訊息可以做很多事情.

好的程式碼像粥一樣,都是用時間熬出來的