MFC註冊窗口類以及FindWindow按窗口類名查詢

呵呵,最近在研究SendMessage函數,其中需要用到m_hWnd,之後延伸着又嘗試獲得窗口的句柄,於是遇到了FindWindow函數,原型如下:

HWND FindWindow

(

 LPCSTR lpClassName,    //輸入窗口的類名,其實需要是註冊過的類名才行,否則是要報錯或者查找失敗的。

 LPCSTR lpWindowName, //輸入窗口的名稱,就是窗口上面藍色的那行玩意兒寫的字。

);

對於這個函數,如果按照窗口的名稱查找,相信大家都非常簡單,因爲名稱在rc文件裏,是已經註冊過的了(具體的待會給大家貼代碼就能看明白了)。但如果大家使用我們自己定義的窗口名稱查詢,則往往會失敗,甚至是報錯!尤其是在使用SendMessage告知另一個窗口退出時,會導致內存泄露!

對於這樣的情況,編譯器不一定能告訴各位,所以,如果沒有親自注冊窗口類的經驗的話,建議大家如果要使用FindWindow函數獲得窗口句柄的話,儘量通過 窗口的名稱查詢

言歸正傳,出現之前的那種嚴重情況的原因,是在於MFC並沒有一個一個幫大家將我們派生的窗口類進行註冊,而是使用的默認窗口類的方式,具體淵源似乎俺也說不大清楚(嘿嘿,見笑了。。。),咱只能引用這一段話:

“   在早期的MFC中 (MFC 4.0之前的版本)提供了大量的預定以的窗口類,但這些窗口類在默認下已經不再被提供了,因爲涉及到多版本的技術問題(多個版本的MFC加載在同一個地址空間),如涉及到的一個實際的問題就是MFC應用程序和OLE控件都會用到MFC的DLLs。 ”

而默認的窗口類,一般類名都是“#32770(Dialog)”,這個大家通過Spy++檢測窗口的時候就可以看到。
拿我這邊的一個小程序爲例:

默認的類名是 #32770(Dialog)

而紅線之前的,就是“註冊”過的窗口的名稱,這個是MFC自動做了的。那麼,我們能使用這個默認類名進行操作麼?嘿嘿~大家試試嘛···所有的未註冊窗口似乎都是這個類名哦。。。。

OK,返回話題,既然默認是這樣的,那麼如何修改呢?這個咱參閱了這位大大的文章:http://hi.baidu.com/iwangkun/blog/item/97fc18d5ef59dfc251da4b21.html

由於是英文的,可能有的朋友會望而生畏,所以,咱把自己的心得以及經驗總結一下,翻譯出來吧:

首先,請用記事本或寫字本的模式,打開需要註冊窗口的工程目錄下的rc資源文件(各個朋友,建議大家先將工程備份一份,因爲錯誤的修改,很容易導致整個工程出錯哦!)。這個時候,請找到我們要註冊的窗口的數據位置,這個很好找的:

因爲每一個窗口的數據開始位置,都是以窗口的句柄開頭的,

比如我要註冊的窗口類的對應句柄是IDD_CARHIRESYSTEM_DIALOG,

那麼打開文件後,稍微需找一下就會發現這個數據段:

IDD_CARHIRESYSTEM_DIALOG     DIALOGEX               0, 0, 400, 280
STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
    WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "車輛出租系統 CarHireSystem"
FONT 9, "宋體", 0, 0, 0x1
BEGIN
    COMBOBOX        IDC_COMBO_CHANCE,23,35,83,95,CBS_DROPDOWNLIST | CBS_SORT |
                    WS_VSCROLL | WS_TABSTOP
    EDITTEXT        IDC_EDIT_SEARCH,125,33,83,15,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "查詢",IDC_BUTTON_SEARCH,219,29,50,25
    ........等等
END

如上所示,那麼,我們接下來所要做事情,便是添加我們對窗口類的註冊信息,

即:

IDD_CARHIRESYSTEM_DIALOG DIALOGEX 0, 0, 400, 280
STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
    WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "車輛出租系統 CarHireSystem"
CLASS "CCarHireSystemDlg"   //""雙引號內的類名,可以隨意,但注意一定要寫對哦,後面還要用到的。
FONT 9, "宋體", 0, 0, 0x1
BEGIN
    COMBOBOX        IDC_COMBO_CHANCE,23,35,83,95,CBS_DROPDOWNLIST | CBS_SORT |
                    WS_VSCROLL | WS_TABSTOP
    EDITTEXT        IDC_EDIT_SEARCH,125,33,83,15,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "查詢",IDC_BUTTON_SEARCH,219,29,50,25
    ........等等
END

然後保存文件,如此,基本上任務已經完成了,就差進行註冊了。MFC中的註冊函數是 :AfxRegisterClass,格式如下:

BOOL AFXAPI AfxRegisterClass(WNDCLASS *lpWndClass);

那麼,我們要做的事情是什麼呢?   ----- 在窗口完成初始化之前,對窗口類進行註冊。於是,我們找到工程的App類,加入如下代碼:

/////////////////////////////////////////////////////////
WNDCLASS wc;

// Get the info for this class.
// #32770 is the default class name for dialogs boxes.
::GetClassInfo(AfxGetInstanceHandle(), "#32770", &wc);

// Change the name of the class.
wc.lpszClassName = "CCarHireSystemDlg"; //這裏請再次注意,一定要保證和rc資源文件裏保存的類名相同!

// Register this class so that MFC can use it.
AfxRegisterClass(&wc);

/////////////////////////////////////////////////////////

如上,只要AfxRegisterClass(&wc); 不返回FALSE,那麼我們的類名註冊就成功了!

演示截圖:

這個時候,大家再使用FindWindow("CCarHireSystemDlg",NULL)函數,就可以正常使用了!

最後再次重申兩點:

1.修改rc資源文件前,建議大家備份工程。當修改了rc文件後,如果沒有加入上面的註冊代碼,或者加入位置出錯,都會導致程序編譯無問題,但執行報錯!這一點,如果在rc資源文件裏保存的類名稱,與註冊代碼裏的類名稱不相同,也會導致執行報錯。

2.使用FindWindow函數時,如果窗口類還並未註冊,那麼,建議直接將第一個參數設爲NULL,同時在第二個參數上傳入窗口的名稱,進行查詢。注意,是窗口的名稱窗口類生成的對象的窗口標題!如此,也不會導致錯誤。

PS:個人最近總結的一些小經驗,發現網上還沒有什麼類似的資料,於是寫上來,供大家分享,如果有什麼紕漏或者意見,可以留言,咱們一起討論哈!呵呵,理不辨不明嘛~一起加油哦!!


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