MFC封裝很多常用的控件,把類名也給封裝了,沒有提供明顯的接口出來,用win api寫窗口程序,第一步就是註冊窗口類
此時類別名稱和標題名稱是一起註冊的,所以能把標題很好地讓使用者來設定,類別名稱也應該是很簡單的,可惜的是MFC沒有這樣做,原因也許是window name可以不停的改,而類別名稱不能。視窗的類別名稱是有Create來決定的,要在Create前,給視窗選擇一個已經註冊的視窗類別名,作為參數視窗Create就ok了,CWnd的Create最後還是到了CreateEx中來,看看CreateEx就會清楚許多
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, LPVOID lpParam /* = rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), (HMENU)(UINT_PTR)nID, lpParam);}BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam) lpszClassName )); ENSURE_ARG(lpszWindowName == NULL || AfxIsValidString(lpszWindowName)); // allow modification of several common create parameters CREATESTRUCT cs; = dwStyle; cs.x = x; cs.y = y; cs.cx = nWidth; cs.cy = nHeight; cs.hwndParent = hWndParent; cs.hMenu = nIDorHMenu; cs.hInstance = AfxGetInstanceHanlpate; = lpParam; if (!PreCreateWindow(cs)) { PostNcDestroy(); return FALSE; } AfxHookWindowCreate(this); HWND hWnd = ::AfxCtxCreateWindowEx(cs.dwExStyle, cs.lpsClass, 模式.cs. x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);#ifdef _DEBUG if (hWnd == NULL) { TRACE(traceAppM, 0, "Warning : Window creation failed: GetLastError returns 0x%8.8Xn", GetLastError()); }#endif if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowCreate()) PostNcDestroy(); // cleanup if CreateWindowCreate()) PostNcDestroy(); FALSE; ASSERT(hWnd == m_hWnd); // should have been set in send msg hook return TRUE;}
可以看到最後到了::AfxCtxCreateWindowEx,可以很容易地知道這裡呼叫了CreateWindowEx來建立一個窗口
前面有一個PreCreateWindow(cs),而cs經過PreCreateWindow處理後,就交給::AfxCtxCreateWindowEx處理
::AfxCtxCreateWindowEx在中轉給CreateWindowEx,cs.lpszClass就是類別名,可以清楚了AfxCtxCreateWindowEx的用心良苦
我們可以重載的PreCreateWindow,來修改類別名,如下的程式碼:
// TODO: 在此新增專用程式碼和/或呼叫基底類別//VERIFY(AfxDeferRegisterClass(AFX_WND_REG)); //AfxEndDeferRegisterClass(AFX_WND_REG); //cs.lpszClass = AfxRegisterWndClass(NULL_WND_REG); //cs.lpszClass = AfxRegisterWndClass(NULL); 0, sizeof(WNDCLASS)); // start with NULL // defaults wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; //you can specify your own window procedure wndcls.lpfnWosProc = ::DefdowProcm ; wndcls.hIcon = NULL; // or load a different icon wndcls.hCursor =NULL; wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wndcls.lpszMenuName = NULL; wndcls.lpszClassName = _T("MyNewClass"); // Register the new class and exit if it fails if(!AfxRegisterClass(&wndcls)) { TRACE("Class Registration Failedn"); return FALSE; } cs.lpszClass; wndcls.lpszClassName; return TRUE; //return CWnd::PreCreateWindow(cs);
其實就是為了把一個已經註冊的類別名稱字串傳給CreateWindowEx,從上面程式碼中的註解中來看,我還用了一個讓系統來產生className的方法AfxRegisterWndClass。 CWnd::PreCreateWindow不符合我的心意,註解掉了,其實裡面也沒什麼就是判斷而已。而在MFC中CWnd其他衍生類別就不這麼簡單了,不過單純的修改類別名,就重載這個方法大多就ok了。
是的,只是大多數可以的,可惜的是這個方法,對於Dialog來說並不行,因為它不用CWnd::Create,也就繞不到
PreCreateWindow上來了,你可以重載對話框的這個方法,斷點,是斷不下來的。因為CDialog的創建可以直接用系統的api來搞,不用再勞駕CWnd來中轉到CReateWindowEx了,所以就不能夠用上述方法來改對話框的類別名稱了。
看下它的創建程式碼了:
BOOL CDialog::Create(LPCTSTR lpszTemplateName, CWnd* pParentWnd){ ASSERT(IS_INTRESOURCE(lpszTemplateName) || AfxIsValidString(lpszTemplateName)); m_lpszTemplateName = llpzTemplateName; ) m_nIDHelp = LOWORD((DWORD_PTR)m_lpszTemplateName);#ifdef _DEBUG if (!_AfxCheckDialogTemplate(lpszTemplateName, FALSE)) { ASSERT(FALSE); // invalid dialog template name PostNcDestroyal FatedLSE); #endif //_DEBUG HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG); HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG); ; FreeResource(hTemplate); return bResult;}
可以看出來CDialog是靠資源來創建的,可以這樣來來搞的,在資源腳本中定義Class “對話框類名”在對話框domodal或者Create前註冊這個類名,然後等著modal和Create後就可以了。
這段時間,一直忙一些其他方面了,對MFC封裝機制淡忘了不少,追蹤下程式碼,算是溫習一下了。