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;

 

當系統找到了一個窗口後,就開始調用這個回調函數,將窗口的句柄作爲第一個參數傳遞過來,將在EnumWindowslParam: LPARAM這個程序定義的值作爲第二個參數傳遞過來。

 

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

 

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

 

得到窗口標題用:

 

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

 

該函數功能是將窗口句柄爲hWnd的窗口的標題拷入到一個緩衝區lpStringnMaxCount是拷入緩衝區內的最大的字符數。

 

要得到窗口標題還可以發送消息: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;

 

程序清單如下:

unit Unit1;

 

interface

 

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

 

type

TForm1 = class(TForm)

Memo1: TMemo;

btn_listwindow: TButton;

procedure btn_listwindowClick(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

 

var

Form1: TForm1;

 

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

 

implementation

{$R *.dfm}

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;

 

procedure TForm1.btn_listwindowClick(Sender: TObject);

begin

EnumWindows(@EnumWindowsProc,LongInt(self));

end;

 

end.

F9,運行,看看結果。最好是F7單步跟蹤調試一下,看看回調函數是怎麼被調用的。

 

有了回調函數的概念及上面的例子,我們可以繼續了。其實想要找到一個標題已知的窗口句柄,用一個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('哈哈!')));就可以給控件發送文本.其他還可以發送不同的消息可以做很多事情.

 

最後總結一下:在最後的例程中沒有改變窗體控件的代碼,可以向它發消息,這裏自己可以把代碼加在函數調用的後面,如上面的消息就是把句柄爲hWnd的控件的TEXT改爲了哈哈!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章