

Программирование на языке 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);
}
читать отзывы (0)
Нетрудно заметить, что метод 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(), который мы можем использовать для того, чтобы произвести какие-то действия ДО создания окна.
