Программирование на языке MFC

Мой второй блог в серии программирования

Для нас главным в этом методе является то, что происходит вызов метода PreCreateWindow() для нашего объекта окна пред­ставления:

Пример


BOOL CEditView::PreCreateWindow(CREATESTRUCT& cs) {

m_dwDefaultStyle = dwStyleDefault; return CCtrlView::PreCreateWindow(cs);

}

Вызов же метода PreCreateWindow() родительского объекта все ставит на свои места:

BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs) {

ASSERT(cs.IpszClass == NULL); cs.IpszClass = m_strClass;

// initialize common controls

VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG)) ; AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);

// map default CView style to default style // WS_BORDER is insignificant

if ((cs.style I WS_BORDER) == AFX_WS_DEFAULT_VIEW)

cs.style = m_dwDefaultStyle & (cs.style | ~WS_BORDER);

return CView::PreCreateWindow(cs);

}



03.02.2010

Нетрудно заметить, что метод Create(), в свою очередь, вызы­вает метод CreateEx():

BOOL CWnd::CreateEx(DWORD dwExStyle,

LPCTSTR IpszClassName,

LPCTSTR IpszWindowName,

DWORD dwStyle,

int x,

int y,

int nWidth,

int nHeight,

HWND hWndParent,

HMENU nIDorHMenu,

LPVOID lpParam)

{

/I allow modification of several common create parameters CREATESTRUCT cs; cs.dwExStyle = dwExStyle; cs.IpszClass = IpszClassName; cs.lpszName = IpszWindowName; cs.style = dwStyle; cs.x = x; cs.y = y; cs.cx = nWidth; cs.cy = nHeight; cs.hwndParent = hWndParent; cs.hMenu = nIDorHMenu;

cs.hlnstance = AfxGetlnstanceHandle(); cs.lpCreateParams = lpParam;

if (!PreCreateWindow(cs)) {

PostNcDestroy(); return FALSE;

}

AfxHookWindowCreate(this);

HWND hWnd = ::CreateWindowEx(cs.dwExStyle,

cs.IpszClass, cs.IpszName, cs.style, cs .x, cs.y, cs.cx, cs.cy,

cs.hwndParent,
cs.hMenu,
‘*
cs.hlnstance,

cs.lpCreateParams);

#ifdef _DEBUG

if (hWnd == NULL) {

TRACE1("Warning: Window creation failed:

GetLastError returns 0x%8.8X\n",

GetLastError ());

#endif

if (!AfxUnhookWindowCreate()) PostNcDestroy();

// cleanup if CreateWindowEx fails too soon

if (hWnd == NULL)

return FALSE;
ASSERT (hWnd == m_hWnd) ;
fl

// should have been set in send msg hook return TRUE;

}



Читатель, взгляните, пожалуйста, на исходный код метода CMDIChildWnd::LoadFrame(). В нем вы увидите следующий опе­ратор:

if (!Create(GetlconWndClass(dwDefaultStyle, nIDResource), strTitle, dwDefaultStyle, rectDefault,

(CMDIFrameWnd*) pParentWnd, pContext)) …

BOOL CMDIChildWnd::Create(LPCTSTR IpszClassName,

LPCTSTR IpszWindowName, DWORD dwStyle, const RECT& rect, CMDIFrameWnd* pParentWnd, CCreateContext* pContext)

{

if (pParentWnd == NULL) {

CWnd* pMainWnd = AfxGetThread()->m_pMainWnd; ASSERT(pMainWnd != NULL); ASSERT_KINDOF(CMDIFrameWnd, pMainWnd); pParentWnd = (CMDIFrameWnd*)pMainWnd;

ASSERT(::IsWindow(pParentWnd->m_hWndMDIClient));

// insure correct window positioning pParentWnd->RecalcLayout();

// first copy into a CREATESTRUCT for PreCreate CREATESTRUCT cs; cs.dwExStyle = OL; cs.IpszClass = IpszClassName; cs.lpszName = IpszWindowName; cs.style = dwStyle; cs.x = rect.left; cs.y = rect.top; cs.cx = rect.right – rect.left; cs.cy = rect.bottom – rect.top; cs.hwndParent = pParentWnd->m_hWnd; cs.hMenu = NULL;

cs.hlnstance = AfxGetlnstanceHandle(); cs.lpCreateParams = (LPVOID)pContext;

if (!PreCreateWindow(cs)) {

PostNcDestroy(); return FALSE;

}

// extended style must be zero for MDI Children // (except under Win4)

ASSERT(afxData.bWin4 || cs.dwExStyle == 0);

ASSERT(cs.hwndParent == pParentWnd->m_hWnd); // must not change

// now copy into a MDICREATESTRUCT for real create MDICREATESTRUCT mcs; mcs.szClass = cs :*lpszClass; mcs.szTitle = cs.lpszName; mcs.hOwner = cs.hlnstance;

mcs.x = cs.x; mcs.у = cs.y; mcs.cx = cs.cx; mcs.cy = cs.cy;

mcs.style = cs.style & -(WS_MAXIMIZE | WS_VISIBLE); mcs.lParam = (LONG)cs.lpCreateParams;

// create the window through the MDICLIENT window AfxHookWindowCreate(this); HWND hWnd =

(HWND)::SendMessage(pParentWnd->m_hWndMDIClient, WM_MDICREATE, 0,

(LPARAM)&mcs); if (!AfxUnhookWindowCreate()) PostNcDestroy(); // cleanup if MDICREATE fails too soon

if (hWnd ~ NULL) return FALSE;

// special handling of visibility (always created invisible) if (cs.style & WS_VISIBLE) {

// place the window on top in z-order before showing it ::BringWindowToTop(hWnd);

// show it as specified

if (cs.style & WS_MINIMIZE)

ShowWindow(SW_SHOWMINIMIZED); else if (cs.style & WS_MAXIMIZE)

ShowWindow(SW_SHOWMAXIMIZED) ; else

ShowWindow(SW_SHOWNORMAL);

// make sure it is active (visibility == activation) pParentWnd->MDIActivate(this);

// refresh MDI Window menu

::SendMessage(pParentWnd->m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0) ; }

ASSERT(hWnd == m_hWnd); return TRUE;

}

fCMDIChJldWnd::OnCreate)



Уважаемый читатель, обратите, пожалуйста, внимание, на тот факт, что сразу после создания структуры cs типа CREATESTRUCT все поля этой структуры обнуляются, в том числе обнуляется и поле IpszClass. В таком случае возникает законный вопрос – какого же класса окно будет создано? Ответ на этот вопрос можно найти в ме­тоде CFrameWnd::PreCreateWindow():

Пример

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)

{

if (cs.IpszClass == NULL) {

VERIFY (Af xDef erRegisterClass (AFX_WNDFRAMEORVIEW_REG) ) ; cs.IpszClass = _afxWndFrameOrView; // COLOR_WINDOW background }

if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4) cs.style |= FWS_PREFIXTITLE;

if (afxData.bWin4)

cs.dwExStyle |= WS_EX_CLIENTEDGE;

return TRUE;

}



Для только что созданного объекта осуществляется вызов метода CMDIChildWnd::LoadFrame(). При этом в качестве аргументов ме­тоду передаются пресловутый идентификатор ресурсов, набор сти­лей окна (не объекта, а окна!), которое будет создано, указатель на родительское окно и, наконец, указатель на структуру типа Ccreate-Context, которую мы рассмотрели чуть раньше. Здесь стоит заме­тить, что по умолчанию стилями окна являются WSJDVERLAPPED-WINDOW и FWS_ADDTOTITLE. Исходный текст этого метода на­ходится в файле winmdi.cpp:

Пример

BOOL CMDIChildWnd::LoadFrame(UINT nIDResource,

DWORD dwDefaultStyle, CWnd* pParentWnd, CCreateContext* pContext)

{

// only do this once

ASSERT_VALID_IDR(nIDResource) ;

ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource);
ASSERT(m_hMenuShared == NULL);
// only do once
m_nIDHelp = nIDResource;

// ID for help context (+HID_BASE_RESOURCE)

// parent must be MDI Frame (or NULL for default)

ASSERT(pParentWnd == NULL ||

pParentWnd->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))); // will be a child of MDIClient

ASSERT(!(dwDefaultStyle & WS_POPUP));

dwDefaultStyle |= WS_CHILD;

// if available – get MDI child menus from doc template
ASSERT(m_hMenuShared == NULL);
// only do once

CMultiDocTemplate* pTemplate; if (pContext != NULL &&

(pTemplate = (CMultiDocTemplate*)pContext->

m_pNewDocTemplate) != NULL)

{

ASSERT_KINDOF(CMultiDocTemplate, pTemplate); // get shared menu from doc template

m_hMenuShared = pTemplate->m_hMenuShared; m_hAccelTable = pTemplate->m_hAccelTable;

}

else {

TRACEO(“Warning: no shared menu/acceltable for MDI Child

window.\n”);

// if this happens, programmer must load these manually }

CString strFullString, strTitle;

if (strFullString.LoadString(nIDResource))

AfxExtractSubString(strTitle, strFullString, 0); // first sub-string

ASSERT(m_hWnd == NULL);

if (!Create(GetlconWndClass(dwDefaultStyle, nIDResource), strTitle, dwDefaultStyle, rectDefault,

(CMDIFrameWnd*)pParentWnd, pContext))

{

return FALSE; // will self destruct on failure normally

// it worked ! return TRUE;

}

Что мы можем увидеть в тексте этого модуля? Многое. Здесь даны ответы на еще несколько вопросов, которые мы напрямую не задавали, но которые, как говорится, висели в воздухе. Например, что еще, кроме меню, акселераторов и строки, может входить в состав передаваемых шаблону документа ресурсов? Каково назна­чение оставшихся подстрок строкового ресурса? Из текста модуля очевидно, что, во-первых, то, что в состав ресурсов, переданных шаблону документа, могут входить не только меню, акселераторы и строка, но и ресурсы помощи. Во-вторых, меню, которое мы соз­дали, является меню окна фрейма! Для нас это очень важно, ибо, наконец, мы поняли, что за меню мы должны создавать при созда­нии шаблона документа. Именно к окну фрейма и относятся аксе­лераторы, которые мы можем создать одновременно с меню.

Этот метод непосредственно создает окно фрей­ма. Но до того момента, пока окно фрейма будет создано, проис­ходит еще несколько интересных вещей. Например, перед вызо­вом метода Create() вызывается метод CFrameWnd::GetlconWnd-Class(). В качестве аргументов этому методу передаются стиль окна и опять-таки идентификатор ресурсов. Текст этой функции нахо­дится в файле winfrm.cpp:

LPCTSTR CFrameWnd::GetlconWndClass(DWORD dwDefaultStyle,

UINT nIDResource)

{

ASSERT_VALID_IDR(nIDResource); HINSTANCE hlnst =

AfxFindResourceHandle(MAKEINTRESOURCE(nIDResource),

RT_GROUP_ICON); HICON hlcon = ::LoadIcon(hlnst,

MAKEINTRESOURCE(nIDResource));

if (hlcon != NULL) {

CREATESTRUCT cs;

memset(&cs, 0, sizeof(CREATESTRUCT));

cs.style = dwDefaultStyle;

PreCreateWindow(cs); // will fill IpszClassName with default WNDCLASS name // ignore instance handle from PreCreateWindow.

WNDCLASS wndcls;

if (cs.IpszClass != NULL &&

GetClassInfo(AfxGetlnstanceHandle(), cs.IpszClass,

&wndcls) && wndcls.hlcon != hlcon)

{

// register a very similar WNDCLASS

return AfxRegisterWndClass(wndcls.style,

wndcls.hCursor, wndcls.hbrBackground, hlcon);

}

}

return NULL; // just use the default

}

Так… Оказывается, в состав ресурсов, передаваемых шаблону документа, может входить и иконка, которая будет использоваться при отображении окна фрейма! Функция пытается загрузить икон­ку из ресурсов, и, если иконка не загрузилась, то ничего не проис­ходит и метод Create() использует данные, принятые по умолча­нию, загрузилась, Если же иконка загрузилась нормально, то MFC, естественно, регистрирует класс окна, которому принадлежит икон­ка. После этого вызывается метод PreCreateWindow(), который мы можем использовать для того, чтобы произвести какие-то дейст­вия ДО создания окна.