1. 程式人生 > >列舉遍歷所有子視窗控制代碼控制元件型別標題

列舉遍歷所有子視窗控制代碼控制元件型別標題

為指定的父視窗列舉子視窗、按鈕

  很早就寫過類似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.其函式原形是:
01.function FindWindow(lpClassName, lpWindowName: PChar): HWND; stdcall;
02.lpClassName:視窗類名.如果只知道標題,可以為空.視窗類名可以用很多工具獲得.如winsignt32.
lpWindowName:視窗標題.
呼叫方式舉例:
01.var wndhwnd:HWND;
02.wndhwnd:=FindWindow(nil,’某視窗標題’);
03.if wndhwnd<>0 then file://找到此視窗控制代碼.
04.begin
05. xxxxx
06.end
07.else begin
08. MessageBox(self.handle,’沒找到該視窗控制代碼’,’提示’,0);
09.end;
  有了這個視窗控制代碼,就離我們的初始目的不遠了:控制其他窗體上的視窗控制元件.同樣,首先要得到其他窗體上視窗控制元件的控制代碼.我們用這個API函式:EnumChildWindows.其函式原形是:
01.function EnumChildWindows(hWndParent: HWND;
02. lpEnumFunc: TFNWndEnumProc;
03. lParam: LPARAM): BOOL; stdcall;
  這個函式和EnumWindow函式很有些想象.其作用也很相似.它的功能就是列舉視窗控制代碼為hWndParent的窗體上所有的視窗控制元件的控制代碼.同樣也是以回撥函式引數的形式給出的. 我們再舉一個實際的例子,來說明這個函式的用法.程式的功能是讓使用者輸入一個視窗標題,然後呼叫FindWindow函式找到此視窗控制代碼.通過這個控制代碼,我們在一個Memo裡顯示該視窗上所有的視窗控制元件.同樣先編寫回調函式.
01.function EnumChildWndProc(AhWnd:LongInt;
02. AlParam:lParam):boolean;stdcall;
03.var04. WndClassName: array[0..254] of Char;
05. WndCaption: array[0..254] of Char;
06.begin
07. GetClassName(AhWnd,wndClassName,254);
08. GetWindowText(aHwnd,WndCaption,254);
09. with form1.memo1 do
10. begin
11. lines.add( string(wndClassName));
12. lines.add( string(wndCaption));
13. lines.add(‘——-‘);
14. end;
15. result:=true;
16.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;
程式清單如下:
01.unit Unit1;
02.
03.interface
04.
05.uses
06. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
07. Dialogs, StdCtrls;
08.
09.type10. TForm1 = class(TForm)
11. Memo1: TMemo; //用來顯示找到的控制元件
12. Label1: TLabel; 
13. Edit1: TEdit;  //輸入標題.
14. Button1: TButton;
15. procedure Button1Click(Sender: TObject);
16. private
17. { Private declarations }
18. public19. { Public declarations }
20. end;
21.
22.var
23. Form1: TForm1;
24.
25. function EnumChildWndProc(AhWnd:LongInt;
26. AlParam:lParam):boolean;stdcall;
27.
28.implementation
29.
30.{$R *.dfm}
31.function EnumChildWndProc(AhWnd:LongInt;
32. AlParam:lParam):boolean;stdcall;
33.
var
34. WndClassName: array[0..254] of Char;
35. WndCaption: array[0..254] of Char;
36.begin
37. GetClassName(AhWnd,wndClassName,254);
38. GetWindowText(aHwnd,WndCaption,254);
39. with form1.memo1 do
40. begin
41. lines.add( string(wndClassName));
42. lines.add( string(wndCaption));
43. lines.add(‘——-‘);
44. end;
45. result:=true;
46.end;
47.
48.procedure TForm1.Button1Click(Sender: TObject);
49.var
50. hWnd:LongInt;
51.begin
52. memo1.Lines.Clear;
53. Memo1.Lines.Add(Edit1.Text+’ 有如下控制元件類名稱’);
54. hWnd:=FindWindow(nil,pchar(Edit1.Text));
55. if hWnd<>0 then
56. begin
57. EnumChildWindows(hWnd,@EnumChildWndProc,0);
58. end
59. else MessageBox(self.handle,’沒找到該視窗控制代碼’,’提示’,0);
60.end;
61.end.
  有了控制元件控制代碼,我們當然就可以隨心所欲了.比如:
01.SendMessage(hWnd,WM_SETTEXT,0,LongInt(Pchar(‘sdafdsf’)));
  就可以給控制元件傳送文字.其他還可以傳送不同的訊息可以做很多事情.但是,有很大一個問題:假設一個窗體上有很多相同的控制元件,並且根本沒辦法區分他們,即使我們能找到所有的控制元件控制代碼,我們又不能區分到底哪個是我們想要的,同樣是乾著急.我想了很長時間,後來在大富翁裡找到了答案,只要用到一個小技巧,就可以解決了.

>

【Demo 0028】獲取窗體資訊
作為一個窗體,除了具有窗體類資訊外還有其自身的一些資訊如風格,擴充套件風格,窗體處理函式外還包括一個使用者資訊,我們可以通過資訊空間將我們要公共資訊置入其中實現資訊共享. 接下來我們看看它的功能.
(一) 函式宣告
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex );
獲取指定窗體特定標識資訊, 此函式支援32位和64位
LONG GetWindowLong(HWND hWnd, int nIndex );
功能同GetWindowLongPtr一致, 但其僅支援32位
nIndex 值可參考
GWL_EXSTYLE
Retrieves the extended window styles. For more information, see CreateWindowEx.
GWL_STYLE
Retrieves the window styles.
GWL_WNDPROC
Retrieves the address of the window procedure, or a handle representing the address of the window procedure.
You must use the CallWindowProc function to call the window procedure.
GWL_HINSTANCE
Retrieves a handle to the application instance.
GWL_HWNDPARENT
Retrieves a handle to the parent window, if any.
GWL_ID
Retrieves the identifier of the window.
GWL_USERDATA
Retrieves the user data associated with the window. This data is intended for use by the application that created the window. Its value is initially zero.
The following values are also available when the hWnd parameter identifies a dialog box.
DWL_DLGPROC
Retrieves the address of the dialog box procedure, or a handle representing the address of the dialog box procedure.
You must use the CallWindowProc function to call the dialog box procedure.
DWL_MSGRESULT
Retrieves the return value of a message processed in the dialog box procedure.
DWL_USER
Retrieves extra information private to the application, such as handles or pointers.

 Code1:
 1.  找到NotePad窗體控制代碼
 2.  將100 置入NotePad主窗體UserData中

HWND hWndNotePad = FindWindowEx(NULL, NULL, _T(“Notepad”), NULL);
SetWindowLongPtr(hWndNotePad, GWLP_USERDATA, 100);

 Code2:
 1.  獲取NotePad窗體控制代碼
 2.  獲取NotePad窗體資訊並顯示

HWND hWndNotePad = FindWindowEx(NULL, NULL, _T(“Notepad”), NULL);
if (NULL != hWndNotePad && IsWindow(hWndNotePad))
{
int nIndexList[] = { GWL_EXSTYLE, GWL_STYLE, GWLP_WNDPROC, GWLP_HINSTANCE, GWLP_HWNDPARENT,
GWLP_ID, GWLP_USERDATA, DWLP_DLGPROC, DWLP_MSGRESULT, DWLP_USER };
int* nValueList = new int[sizeof(nIndexList) / sizeof(*nIndexList)];
const TCHAR* szInfo[] = { _T(“Extend Style:\t 0xX\n”),
_T(“Style:\t 0xX\n”),
_T(“WndProc: 0xX\n”),
_T(“hInstance: 0xX\n”),
_T(“hParent:\t 0xX\n”),
_T(“ID: 0xX\n”),
_T(“UserData: 0xX\n”),
_T(“DlgProc:\t 0xX\n”),
_T(“MsgResult: 0xX\n”),
_T(“User: 0xX\n”)};
assert((sizeof(nIndexList) / sizeof(*nIndexList)) == (sizeof(szInfo) / sizeof(*szInfo)));

TCHAR szClassInfo[1024] = {0};
TCHAR szTemp[256];
for (int ii = 0; ii < sizeof(nIndexList) / sizeof(*nIndexList); ii++)
{
    nValueList[ii] = GetWindowLongPtr(hWndNotePad, nIndexList[ii]);
    TCHAR szTemp[256];
    _stprintf_s(szTemp, szInfo[ii], nValueList[ii]);
    _tcscat(szClassInfo, szTemp);
}
SetWindowText(GetDlgItem(hWnd, ID_LABINFO), szClassInfo);
OutputDebugString(szClassInfo);
delete[] nValueList;

}

我們可以看到從Demo0028置入100UserData資訊

此函式我們使用比較多,通常與SetWindowLongPtr配合使用於,用於設定使用者資訊、讀取窗體風格,以及讀取同一程序窗體過程函式以便恢復.
(二) 特別說明
1. 如果用於替換窗體過程函式需要同一個程序中(見後序章節)

>

【Demo 0029】擷取同進程窗體訊息
今天這個話題比較簡單,僅擷取同進程的窗體訊息,如果我們再做一些處理做成一個DLL然後將DLL駐入到指定程序的窗體中那更有意思了,我們將在後面的章節裡再研究一下。我們開始學習了。本節與上一節都在講述著同相的內容圍繞著GetWindowLongPtr, SetWindowLongPtr兩個API進行的
(一) 函式宣告
LONG_PTR GetWindowLongPtr(HWND hWnd, int nIndex );

  LONG_PTR SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong );
  獲取/修改Window窗體資訊值

  Code1:  通過修改窗體過程函式來擷取窗體訊息
 1.  為了儲存原窗體過程函式, 我們將它到設定USER_DATA位,實現各函式資料共享;
 2.  通過SetWindowLongPtr, 來設定新的窗體過程函式;    

if (!bHook)
{
TCHAR szTmp[MAX_PATH];
TCHAR szClsName[MAX_PATH];
GetClassName(hWnd, szClsName, MAX_PATH);
WNDCLASSEX wcx = {0};
wcx.cbSize = sizeof(WNDCLASSEX);

SetWindowText(GetDlgItem(hWnd, ID_BTNHOOKMSG), _T("Stop Hook"));
OutputDebugString(_T("\n============== Start Hook Message ================\n"));

GetClassInfoEx(GetModuleHandle(NULL), szClsName, &wcx);
_stprintf_s(szTmp, _T("WndProc Value before SetWindowLongPtr: 0x%0X  ==> 0x%0X\n"), wcx.lpfnWndProc, GetWindowLongPtr(hWnd, GWLP_WNDPROC));
OutputDebugString(szTmp);
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)WndProc);
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG)_HookWndProc);
GetClassInfoEx(GetModuleHandle(NULL), szClsName, &wcx);
_stprintf_s(szTmp, _T("WndProc Value After SetWindowLongPtr: 0x%0X  ==> 0x%0X\n"), wcx.lpfnWndProc, GetWindowLongPtr(hWnd, GWLP_WNDPROC));
OutputDebugString(szTmp);
OutputDebugString(_T("==================================================================\n"));
bHook = true;

} else {
SetWindowText(GetDlgItem(hWnd, ID_BTNHOOKMSG), _T(“Start Hook”));
OutputDebugString(_T(“\n============== Stop Hook Message ================\n”));
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG)0);
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG)WndProc);
bHook = false;
}
3. 在新的窗體過程函式中列印被擷取訊息

//////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK _HookWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
TCHAR szTemp[256];
_stprintf_s(szTemp, _T(“[HookProc msg] => hWnd: 0xX nMsg: X wParam: 0xX lParam: 0xX\n”),
hWnd, nMsg, wParam, lParam);
OutputDebugString(szTemp);
WNDPROC _oldWndProc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (NULL == _oldWndProc)
{
_oldWndProc = (WNDPROC)GetClassLongPtr(hWnd, GCLP_WNDPROC);
}
return _oldWndProc(hWnd, nMsg, wParam, lParam);
}

演示結果:
1. 沒修改窗體過程前
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBA lParam: 0x2000012
[WndProc msg] => hWnd: 0x010BBA nMsg: 0000A0 wParam: 0x000012 lParam: 0x282023C
[WndProc msg] => hWnd: 0x010BBA nMsg: 0002A2 wParam: 0x000000 lParam: 0x000000
[WndProc msg] => hWnd: 0x010BBA nMsg: 000086 wParam: 0x000000 lParam: 0x000000
[WndProc msg] => hWnd: 0x010BBA nMsg: 000006 wParam: 0x000000 lParam: 0x000000
[WndProc msg] => hWnd: 0x010BBA nMsg: 00001C wParam: 0x000000 lParam: 0x001B10
[WndProc msg] => hWnd: 0x010BBA nMsg: 000008 wParam: 0x000000 lParam: 0x000000
[WndProc msg] => hWnd: 0x010BBA nMsg: 000281 wParam: 0x000000 lParam: 0xC000000F
[WndProc msg] => hWnd: 0x010BBA nMsg: 000282 wParam: 0x000001 lParam: 0x000000
2. 修改窗體過程之後
============== Start Hook Message ================
WndProc Value before SetWindowLongPtr: 0x8C11B8 ==> 0x8C11B8

WndProc Value After SetWindowLongPtr: 0x8C11B8 ==> 0x8C10DC

[WndProc msg] => hWnd: 0x010BBA nMsg: 000111 wParam: 0x0003E9 lParam: 0x010BBE
[HookProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[HookProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[HookProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
3. 恢復原窗體過程
============== Stop Hook Message ================
[WndProc msg] => hWnd: 0x010BBA nMsg: 000111 wParam: 0x0003E9 lParam: 0x010BBE
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
[WndProc msg] => hWnd: 0x010BBA nMsg: 000020 wParam: 0x010BBE lParam: 0x2000001
(二) 特別說明
演示的程式碼裡我們也在設定窗體過程函式前後列印了WNDCLASSEX類資訊, 也釋出我們設定的窗體過程並沒有修改了WNDCLASS wndProc;

>

【Demo 0031】遍歷頂層窗體
今天我們將講述如何遍歷系統中所有的頂層窗體,講述之前我們提個問題,什麼是頂層窗體,頂層窗體有些什麼特點呢? 我引用了Codeguru上的老外的描述看看他是怎麼說的:

Q: What is a top-level window?
A: A top-level window is a window that is not child, i.e. it has not WS_CHILD style set.
Notes
unlike the child windows, a top-level window can be displayed anywhere in the screen;
many definitions state that a top-level window is “a window that has no parent”;
that is correct but can lead in a confusion: many people think that every window which is created passing a valid hWndParent in CreateWindow(Ex) “has a parent” then, according to the definition it is not top-level;
in fact hWndParent may be either a handle to parent or owner window;
if hWndParent is a valid window handle and WS_CHILD style is not set, then we have a top-level owned window;
a top-level window can or can not be owned but is never a child; further we can say that it can have an owner but never has a parent.
top-level windows can be either overlapped windows (having WS_OVERLAPPED style and generally used as application main window) or popup windows (having WS_POPUP style, usually temporary windows like message boxes and dialogs);
the coordinates used in CreateWindow(Ex), MoveWindow, SetWindowPos, and so on are always scren coordinates (relative to top-left corner of the screen).
Examples
Code:
// create a top-level window (not owned)
HWND hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPED, // WS_CHILD style is not set, so it’s a top-level window.
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, // no handle to the owner, so it’s not owned.
NULL, hInstance, NULL);
Code:
// create a top-level window (owned)
HWND hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPED, // WS_CHILD style is not set, so it’s a top-level window
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
hWndParent, // handle to the owner, so it’s an owned window
NULL, hInstance, NULL);
正如上文所說, 頂層窗體就是不具體WS_CHILD風格的窗體它可以在螢幕上任務拖動, 從文中還加深對CreateWindow 中HWNDPARENT引數的理解,它在風格不為WS_CHILD時,這個Parent 不是父窗體而是擁有者窗體, 同時也讓我明白怎麼樣的窗體是擁有者窗體 (有學到新東西, 心情爽極了! ^_^), 再引用一下什麼時擁有者窗體:
Q: What is an owned window?
A: An owned window is a top-level window that has an owner.
It has the following properties:
being a top-level window, it can be displayed anywhere in the screen;
it stays always in the front of its owner window;
it is hidded when its owner is hidden or minimized;
it is destroyed when its owner is being destroyed;
Notes
An owned window is created by passing the owner window handle as hWndParent parameter in CreateWindow(Ex) function call.
WS_CHILD style must not be set, otherwise results a child and not an owned top-level window.
Example
Code:
// create an owned top-level window
HWND hWnd = CreateWindow(szWindowClass, szTitle,
WS_OVERLAPPED, // WS_CHILD style is not set
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
hWndParent, // handle to the OWNER window
NULL, hInstance, NULL);
有了對上文的講述,我們對下面的內容就好理解多了,通常頂層窗體都是應用窗體的主窗體,可用這個方法找到系統中執行的應用程式(有UI的),系統為達到此目的提供對應的函式:
一、函式宣告
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
功能: 遍歷螢幕中所有的頂層窗體,並通過預定的回撥函式返回遍歷到窗體的控制代碼直到遍歷結束或回撥函式返回FALSE
回撥函式型別: typedef BOOL (CALLBACK* WNDENUMPROC)(HWND, LPARAM);

二、程式碼演示
1. 設定遍歷頂層窗體的回撥函式,並將ListBox控制元件做為引數->(我們通過一個ListBox將頂層窗體資訊顯示出來)
EnumWindows(EnumWndProc, (LPARAM)GetDlgItem(hWnd, IDC_LSTWNDLIST));
SetWindowText(GetDlgItem(hWnd, IDC_BTNDATAREAD), _T(“Refresh”));

2.  遍歷到頂層窗體後

//////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWndProc(HWND hWnd, LPARAM lParam)
{
HWND hListbox = (HWND)lParam;
if (NULL == hWnd) return FALSE;
if (NULL != hListbox && IsWindow(hListbox))
{
TCHAR szWndInfo[512] = {0};
TCHAR szWndTitle[256] = {0};
TCHAR szClsName[64] = {0};
GetWindowText(hWnd, szWndTitle, 256);
GetClassName(hWnd, szClsName, 64);
_stprintf(szWndInfo,
_T(“´°¿ÚX”)
_T(“\”%s\”“)
_T(“%s”),
hWnd,
szWndTitle,
szClsName);
SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)szWndInfo);
}
return TRUE;
}
備註: 若我們要提前退出遍歷函式,可將此回撥函式返加值設為FALSE即可提前結束遍歷;

>

【Demo 0032】遍歷子窗體
在上節中我們學習如何遍歷頂層窗體,本節中我們繼續學習如何遍歷子窗體,在例程中我們用使用了二種方法來遍歷子窗體;
1. 使用EnumChildWindows 遍歷
BOOL EnumChildWindows(HWND hWndParent, WNDENUMPROC lpEnumFunc, LPARAM lParam );
功能: 遍歷指定窗體的子窗體,將遍歷到的子窗體通知預設的回撥函式,並以遍歷完子窗體或回撥函式返回FALSE結束, 最後一引數用於將資訊傳遞給回撥函式
說明: a. 與函式與EnumChildWindows函式用法與EnumWindows幾乎一樣, EnumWindows 僅能遍歷頂層窗體; 此函式列舉的窗體為CreateWindowEx時風格設有WS_CHILD
且父窗體被指定為EnumChildWindows中第一個引數
b. EnumChildWindows 不能列舉到遍歷前己銷燬和遍歷期間己建立的窗體

  1. 使用GetWindow 遍歷
    HWND GetWindow(HWND hWnd, UINT uCmd);
    功能: 獲取與指定窗體相對關係(Z-ORDER,OWNER), 根據uCmd引數不同返回不同的窗體
    GW_CHILD 返回hWnd窗體第一個子窗體
    GW_ENABLEDPOPUP
    GW_HWNDFIRST 返回與hWnd窗體具有相同z-oder的窗體
    GW_HWNDLAST 意思與GW_HWNDFIRST 相同, 僅表示返回的是最後一個
    GW_HWNDNEXT 通常與GW_HWNDFIRST配合使用返回下一個具有相同z-oder的窗體
    GW_HWNDPREV 通常與GW_HWNDLAST配合使用返回上一個具有相同z-oder的窗體
    GW_OWNER 獲取指定窗體擁有者窗體或父窗體
    說明: 此方法遍歷子窗體沒有EnumChildWindows函式穩定,但它可以列舉到遍歷期間消毀的窗體.

我們例程功能主要演示了這兩種遍歷方法的使用, 演示過程中我們將遍歷到的窗體分別顯示到二個ListBox窗體中, 看看演示程式碼
1. 呼叫方法
case WM_COMMAND:
{
switch (wParam)
{
case IDC_BTNDATAREAD:
{
HWND hListbox = GetDlgItem(hWnd, IDC_LSTWNDLIST);
HWND hListbox1 = GetDlgItem(hWnd, IDC_LSTWNDLIST1);
SendMessage(hListbox, LB_RESETCONTENT, 0, 0);
SendMessage(hListbox1, LB_RESETCONTENT, 0, 0);

        HWND hParent = GetDesktopWindow();
        _EnumChildWindows(hParent, hListbox);
        EnumChildWindows(hParent, EnumChildWndProc, (LPARAM)hListbox1);

        SetWindowText(GetDlgItem(hWnd, IDC_BTNDATAREAD), _T("Refresh"));
    }
    break;
}
break;

}
2. _EnumChildWindows - GetWindow方法遍歷

//////////////////////////////////////////////////////////////////////////
void _EnumChildWindows(HWND hParent, HWND hDisplay)
{
HWND hWndChild = GetWindow(hParent, GW_CHILD);
HWND hWndTemp = GetWindow(hWndChild, GW_HWNDFIRST);
do {
TCHAR szWndInfo[512] = {0};
TCHAR szWndTitle[256] = {0};
TCHAR szClsName[64] = {0};

    GetWindowText(hWndTemp, szWndTitle, 256);
    GetClassName(hWndTemp, szClsName, 64);
    _stprintf(szWndInfo,
                _T("´°¿ÚX ")
                _T("\"%s\"")
                _T("%s ")
                _T("%s"),
                hWndTemp,
                szWndTitle,
                szClsName,
                IsWindowVisible(hWndTemp) ? _T("Visible") : _T("Invisible"));
    SendMessage(hDisplay, LB_ADDSTRING, 0, (LPARAM)szWndInfo);
} while (NULL != (hWndTemp = GetWindow(hWndTemp, GW_HWNDNEXT)));

}
3. EnumChildWindows 方法
//////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumChildWndProc(HWND hWnd, LPARAM lParam)
{
HWND hListbox = (HWND)lParam;
if (NULL == hWnd) return FALSE;

if (NULL != hListbox && IsWindow(hListbox))
{
    TCHAR szWndInfo[512]    = {0};
    TCHAR szWndTitle[256]    = {0};
    TCHAR szClsName[64]        = {0};

    GetWindowText(hWnd, szWndTitle, 256);
    GetClassName(hWnd, szClsName, 64);
    _stprintf(szWndInfo,
              _T("´°¿ÚX ")
              _T("\"%s\" ")
              _T("%s ")
              _T("%s"),
              hWnd,
              szWndTitle,
              szClsName,
              IsWindowVisible(hWnd) ? _T("Visible") : _T("Invisible"));
    SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)szWndInfo);
}

return TRUE;

}
遍歷結果圖, 我也參考了SPY++ 的遍歷方法

根據上圖的結果我發現兩個問題, 1. 使用GetWindow與EnumChildWindows枚舉出來的窗體不一致; 2. SPY++ 遍歷的結果與通過GetWindow的得到的結果是一樣的,說明SPY++也是使用了這種方式.

>

【Demo 0033】遍歷窗體中控制元件
今天學習的內容在Demo032中基本上己學過了, 此例僅在Demo32基礎上做了一點延伸。
在上節中我們瞭解EnumchildWindows遍歷桌面的子窗體, 本節使 用此函式來獲取窗體中所有控制元件.
一、程式碼演示
//////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumChildWndProc(HWND hWnd, LPARAM lParam)
{
HWND hListbox = (HWND)lParam;
if (NULL == hWnd) return FALSE;
if (NULL != hListbox && IsWindow(hListbox))
{
TCHAR szWndInfo[512] = {0};
TCHAR szWndTitle[256] = {0};
TCHAR szClsName[64] = {0};
GetWindowText(hWnd, szWndTitle, 256);
GetClassName(hWnd, szClsName, 64);
_stprintf(szWndInfo,
_T(“´°¿ÚX “)
_T(“\”%s\” “)
_T(“%s “)
_T(“%s”),
hWnd,
szWndTitle,
szClsName,
IsWindowVisible(hWnd) ? _T(“Visible”) : _T(“Invisible”));
SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)szWndInfo);
}
return TRUE;
}
HWND hListbox = GetDlgItem(hWnd, IDC_LSTWNDLIST);
SendMessage(hListbox, LB_RESETCONTENT, 0, 0);
HWND hParent = FindWindowEx(NULL, NULL, _T(“Notepad”), NULL);
EnumChildWindows(hParent, WndEnumProc, (LPARAM)GetDlgItem(hWnd, IDC_LSTWNDLIST));
SetWindowText(GetDlgItem(hWnd, IDC_BTNDATAREAD), _T(“Refresh”));
TCHAR szTmp[128];
DWORD dwcount = SendMessage(GetDlgItem(hWnd, IDC_LSTWNDLIST), LB_GETCOUNT, 0, 0);
_stprintf(szTmp, _T(“size: %d”), dwcount);
OutputDebugString(szTmp);
本例以Notepad 窗體為目標, 通過EnumChildWindows 獲取NotePad中所有的控制元件
二、顯示結果

執行後發現NodePad 有兩個控制元件1. Edit 2. statusbar  且不可見

>

【Demo 0035】獲取窗體狀態
本章學習內容非常少就一個API(GetWindowPlacement)
1. 程式碼演示
//////////////////////////////////////////////////////////////////////////
BOOL CALLBACK WndEnumProc(HWND hWnd, LPARAM lParam)
{
HWND hListbox = (HWND)lParam;
if (NULL == hWnd) return FALSE;
if (NULL != hListbox && IsWindow(hListbox) && IsWindowVisible(hWnd))
{
TCHAR szWndInfo[512] = {0};
TCHAR szWndTitle[256] = {0};
TCHAR szClsName[64] = {0};
WINDOWPLACEMENT wp = {0};
wp.flags = 0;
wp.length = sizeof(wp);
RECT rtWnd, rtClient;
GetWindowRect(hWnd, &rtWnd);
GetClientRect(hWnd, &rtClient);
GetWindowText(hWnd, szWndTitle, 256);
GetClassName(hWnd, szClsName, 64);
GetWindowPlacement(hWnd, &wp);
_stprintf(szWndInfo,
_T(“\”%s\”“)
_T(” %d “)
_T(“wnd[%d,%d,%d,%d], client[%d,%d,%d,%d], normal[%d,%d,%d,%d] min[%d, %d] max[%d, %d]”),
szWndTitle,
wp.showCmd,
rtWnd.left, rtWnd.top, rtWnd.right, rtWnd.bottom,
rtClient.left, rtClient.top, rtClient.right, rtClient.bottom,
wp.rcNormalPosition.left, wp.rcNormalPosition.top, wp.rcNormalPosition.right, wp.rcNormalPosition.bottom,
wp.ptMinPosition.x, wp.ptMinPosition.y,
wp.ptMaxPosition.x, wp.ptMaxPosition.y);
SendMessage(hListbox, LB_ADDSTRING, 0, (LPARAM)szWndInfo);
}
return TRUE;
}
2. BOOL GetWindowPlacement(HWND hWnd, WINDOWPLACEMENT *lpwndpl )
功能: 該函式返回指定視窗的顯示狀態以及被恢復的、最大化的和最小化的視窗位置
typedef struct _WINDOWPLACEMENT {
UINT length;
UINT flags;
UINT showCmd;
POINT ptMinPosition;
POINT ptMaxPosition;
RECT rcNormalPosition;
} WINDOWPLACEMENT;
返回值說明:
showCmd – 直接COPY MSDN
SW_HIDE
Hides the window and activates another window.
SW_MAXIMIZE
Maximizes the specified window.
SW_MINIMIZE
Minimizes the specified window and activates the next top-level window in the z-order.
SW_RESTORE
Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position.
An application should specify this flag when restoring a minimized window.
SW_SHOW
Activates the window and displays it in its current size and position.
SW_SHOWMAXIMIZED
Activates the window and displays it as a maximized window.
SW_SHOWMINIMIZED
Activates the window and displays it as a minimized window.
SW_SHOWMINNOACTIVE
Displays the window as a minimized window.
This value is similar to SW_SHOWMINIMIZED, except the window is not activated.
SW_SHOWNA
Displays the window in its current size and position.
This value is similar to SW_SHOW, except the window is not activated.
SW_SHOWNOACTIVATE
Displays a window in its most recent size and position.
This value is similar to SW_SHOWNORMAL, except the window is not actived.
SW_SHOWNORMAL