

Программирование на языке MFC
Мой второй блог в серии программирования
Что мы можем здесь увидеть? То, что в описании этого класса присутствует поле rnJTemplateList класса CPtrList, только подтверждает нашу догадку о том, что de facto объекты класса CDocManager являются списками указателей на какие-то шаблоны. Для того чтобы убедиться, что наша догадка верна, достаточно взглянуть на исходный код методов GetFirstDocTemplatePosition() и GetNext-DocTemplate(), который находится в файле docmgr.cpp:
POSITION CDocManager::GetFirstDocTemplatePosition() const {
return m_templateList.GetHeadPosition() ;
}
CDocTemplate* CDocManager::GetNextDocTemplate(
POSITIONS pos) const
{
return (CDocTemplate*)m_templateList.GetNext(pos) ;
Теперь возникает вопрос о том, что за шаблоны включаются в список. Позвольте, уважаемый читатель, высказать предположение о том, что этими шаблонами являются шаблоны ДОКУМЕНТОВ, с которыми работает данное приложение. Я не буду сейчас останавливаться на том, что такое шаблон документа. Это станет ясно из дальнейшего изложения.
Сейчас нам необходимо научиться создавать объект этого класса. Естественно, что для создания объекта мы воспользуемся конструктором. Раз конструктор класса CDocManager параметров не имеет, то создание объекта этого класса затруднений не вызовет. Но каким-то образом нам необходимо передать нашему приложению информацию о том, с документами каких типов (шаблонов) он будет иметь дело! Вспомним, что у класса CWinApp для добавления шаблона в список есть метод AddDocTemplate(), который, фактически является вызовом одноименного метода класса CDocManager. Наверное, именно этим методом и следует воспользоваться при добавлении шаблона документа!
Похожие статьи: CDocTemplate, CWinApp, Объекты класса CDocManager
читать отзывы (0)
Наверное, из описания класса CWinApp мы не сумеем извлечь еще какую-то информацию о классе CDocManager. Настало время взглянуть на описание этого класса. Думаю, нет необходимости описывать всю внутреннюю реализацию этого класса. Достаточно будет, если мы рассмотрим назначение наиболее часто используемых полей и методов.
Класс CDocManager в файле afxwin.h описан следующим образом:
class CDocManager : public CObject {
DECLARE_DYNAMIC(CDocManager) public:
// Constructor CDocManager();
//Document functions
virtual void AddDocTemplate(CDocTemplate* pTemplate); virtual POSITION GetFirstDocTemplatePosition() const; virtual CDocTemplate* GetNextDocTemplate(
POSITIONS pos) const; virtual void RegisterShellFileTypes(BOOL bCompat); void UnregisterShellFileTypes();
virtual CDocument* OpenDocumentFile(LPCTSTR IpszFileName);
// open named file
virtual BOOL SaveAllModified(); // save before exit virtual void CloseAllDocuments(BOOL bEndSession);
// close documents before exiting virtual int GetOpenDocumentCount();
// helper for standard commdlg dialogs virtual BOOL DoPromptFileName(CString& fileName
UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate);
//Commands
// Advanced: process async DDE request
virtual BOOL OnDDECommand(LPTSTR IpszCommand); virtual void OnFileNew(); virtual void OnFileOpen();
// Implementation protected:
CPtrList m_templateList;
int GetDocumentCount(); // helper to count number
// of total documents
public:
static CPtrList* pStaticList;
// for static CDocTemplate objects static BOOL bStaticInit;
// TRUE during static initialization static CDocManager* pStaticDocManager;
// for static CDocTemplate objects
public:
virtual -CDocManager(); #ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const; #endif };
Похожие статьи: CDocTemplate, CWinApp, DoPromptFileName, OnFileOpen, OpenDocumentFile, Класс CDocManager
Значит, либо я сделал что-то не так, либо в программе не определил какие-то данные. Второй вариант, конечно, более вероятен. Для того чтобы убедиться в этом, давайте, уважаемый читатель, посмотрим на то место, в котором была обнаружена ошибка. Как оказалось, на этом месте находится исходный текст метода OnFileOpenQ класса CWinApp. Ниже приведен текст этого метода:
void CWinApp::OnFileOpen() {
ASSERT(m_pDocManager != NULL); m_pDocManager->OnFileOpen();
}
Помните, я говорил, что имеющий уши да услышит? Во-первых, судя по наименованию переменных, используемых в приведенном выше тексте метода, MFC по умолчанию считает, что наше приложение построено в соответствии с идеологией «Документ/представление»! Другими словами, именно эта идеология считается приоритетной при работе с MFC! Значит, именно ЭТОЙ идеологии в MFC отводится очень важная роль. Возможно, подумал я, что понимание одного из ключевых моментов MFC приблизит меня к пониманию идеологии всей библиотеки в целом! Но оставим «лирическое отступление» и продолжим нашу работу.
Во-вторых, здесь MFC сама подсказывает нам причину ошибки в программе! Из приведенного выше текста метода мы можем сделать три важных заключения. Заключение первое состоит в том, что у класса CWinApp есть член m_pDocManager, который, судя по его названию, является указателем на какой-то менеджер документов. Заключение второе – вероятнее всего, именно этот менеджер документов и отвечает за управление документами в приложении. И наконец, заключение третье. Очевидно, что единственной причиной, которая может привести к выдаче приведенного выше сообщения об ошибке, является равенство нулю указателя на менеджер документов. Следовательно, причина этой ошибки состоит в том, что мы просто-напросто не создали менеджер документов!
А что следует из этих заключений? Из этих заключений следует один очень важный вывод.
Тсли наше приложение не обрабатывает команду ID_FILE_OPEN и обработку производит метод CWinApp::OnFileOpen, то перед вызовом этого метода должен быть создан объект класса CDocManager и указатель на этот объект должен быть присвоен полю CWinApp::m_pDocManager.
К сожалению, искать информацию о менеджере документов в MSDN совершенно бесполезно. И в описании членов класса CWinApp мы также не найдем и упоминания об m_pDocManager. Придется опять смотреть исходные коды MFC…
Похожие статьи: CWinApp, OnFileOpen
Я начал реализовывать этот план и какое-то время работал в этом направлении. Однако достаточно быстро я одумался и задал себе один вопрос: а почему, собственно говоря, мне приходится делать всю черновую работу самому? Исходя из того, что я читал про MFC ранее, все должно быть намного проще. Если я иду в правильном направлении, то почему мне приходится делать все самому вручную? Где же хваленые возможности MFC? Все же, наверное, я чего-то не понимаю. И здесь мне пришла в голову мысль, результатом которой и явилось правильное решение. А не попробовать ли мне возложить обработку открытия файла на MFC? Ведь не зря же у класса CWinApp есть метод OnFileOpenQ, не так ли? Карту сообщений своего объекта приложения я немного изменил. Теперь обработчик команды ID__FILE__OPEN стал выглядеть следующим образом:
ON_COMMAND( ID_FILE_OPEN, CWinApp::OnFileOpen )
Тем самым обработку открытия файла я возложил на MFC. Мне осталось только понять, к чему это приведет…
Программа откомпилировалась без ошибок, что меня, честно говоря, несколько удивило. Однако, когда я запустил программу на выполнение в отладочном режиме, то, выбрав элемент «Ореп» меню «File», я получил сообщение, которое приведено на.
Похожие статьи: CWinApp, OnFileOpen
Характерной особенностью этой программы является то, что она практически ничего не делает. Я написал этот вариант только для того, чтобы посмотреть, как MFC будет реагировать на открытие файла и т.д. Но, с другой стороны, обратите внимание на то, что обработку открытия файла я, тем не менее, возложил на основное окно приложения. Этот факт привел к тому, что было потеряно достаточно много времени. Тем не менее, это вынудило меня хорошенько пораскинуть мозгами, и в конце концов эти размышления привели меня к правильному решению.
Я старался рассуждать логически. Я хочу отобразить содержимое файла. При этом хочу сделать это в соответствии с требованиями архитектуры «документ/представление». Но у меня нет ясности по многим вопросам. Когда, в какой момент файл на диске должен превратиться в документ? Наверное, в момент открытия файла. Другими словами, при обработке команды на открытие файла, полученной от меню, я должен открыть файл и превратить его в документ. Но какой файл надо открыть? Естественно, тот файл, который я укажу. Где укажу? Конечно же, в стандартном диалоговом окне для выбора открываемого файла! Значит, мне необходимо первым делом подготовить данные для открытия стандартного диалога. После того как я выберу файл, мне необходимо будет его открыть, разобрать по косточкам и каким-то образом содержимое файла превратить в документ… Н-да, работы непочатый край…
А теперь текст непосредственно программы:
#include "stdafx.h"
// Объявляем класс приложения, его поля и методы.
class CDocViewlApp : public CWinApp
{
public:
CDocViewlApp () ; :* protected:
afx_msg void OnFileOpen(); virtual BOOL Initlnstance (); DEC LARE_ME S S AG E_MA P()
};
CDocViewlApp::CDocViewlApp()
{
}
void CDocViewlApp::OnFileOpen()
{
}
class CMainFrame : public CMDIFrameWnd {
DECLARE_DYNAMIC( CMainFrame ); public:
CMainFrame () ; «’
};
IMPLEMENT_DYNAMIC(CMainFrame, CMDIFrameWnd)
CMainFrame::CMainFrame()
{
}
BOOL CDocViewlApp::Initlnstance() {
#ifdef _AFXDLL
Enable3dControls() ; #else
Enable3dControlsStatic(); #endif
CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame( IDR_RESOURCE ); m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP( CDocViewlApp, CWinApp )
ON_COMMAND( ID_FILE_OPEN, OnFileOpen ) END_MESSAGE_MAP()
CDocViewlApp theApp;
Похожие статьи: CWinApp, OnFileOpen
С чего я начал? Написал небольшую программу, при помощи которой хотел всего-навсего выяснить характер взаимодействия объектов разных классов при работе в соответствии с идеологией архитектурой «документ/представление». Эта программа не должна делать ничего, кроме отображения пустого документа в (естественно!) пустом окне отображения. Впоследствии я планировал использовать текст этой программы как заготовку для других программ.
Ниже приведен текст файла ресурсов моей программы. Комментарии, созданные средой Visual С++, я предварительно удалил.
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "afxres.h"
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LAN G__RU SSI AN, SUBLANG_DE FAULT #pragma code_page(1251) #endif //_WIN32
#ifdef APSTUDIO_INVOKED
1 TEXTINCLUDE DISCARDABLE BEGIN
"resource. h\0"
END
2 TEXTINCLUDE DISCARDABLE . BEGIN
"#include xw‘afxres.h""\r\n" "\0"
END
3 TEXTINCLUDE DISCARDABLE BEGIN
"\r\n" "\0"
END
#endif // APSTUDIO_INVOKED
IDR_RESOURCE ICON DISCARDABLE "ResWCommon.ico"
IDR_RESOURCE MENU DISCARDABLE BEGIN
ID_FILE_OPEN ID APP EXIT
POPUP "&File"
BEGIN
MENUIТЕМ "&Open\tCtrl+0", MENUIТЕМ SEPARATOR MENUIТЕМ "E&xit\tCtrl+x",
END
POPUP "SWindow", GRAYED BEGIN
MENUITEM "Tile &horizontally\tCtrl+H", ID_WINDOW_TILEHORZ, GRAYED
MENUITEM "Tile &vertically\tCtrl+V", ID_WINDOW_TILE_VERT, GRAYED
MENUIТЕМ "&Cascade\tCtrl+C", ID_WINDOW_CASCADE
END
POPUP "Help" BEGIN
MENUIТЕМ "&About", ID_APP_ABOUT
END
END
STRINGTABLE DISCARDABLE BEGIN
ID_FILE_OPEN "Open an existing document"
END
"Display program information, version number and copyright" "Quit the application"
STRINGTABLE DISCARDABLE BEGIN
ID_APP_ABOUT
ID_APP_EXIT
END
STRINGTABLE DISCARDABLE BEGIN
IDR_RESOURCE "PE-file viewer and disassembler"
END
Тем не менее, мы может прекратить работу архива в любое время при помощи метода Abort(), исходный текст которого находится в файле агссоге.срр:
void CArchive::Abort() {
ASSERT(m_bDirectBuffer ||
m_lpBufStart == NULL || AfxIsValidAddress(m_lpBufStart,
m_lpBufMax – m_lpBufStart, IsStoring()));
ASSERT(m_bDirectBuffer ||
m_lpBufCur == NULL || AfxIsValidAddress(m_lpBufCur,
m_lpBufMax – m_lpBufCur, IsStoring()));
// disconnect from the file mjpFile = NULL;
if (!m_bUserBuf) {
ASSERT(!m_bDirectBuffer); delete[] m_lpBufStart; m_lpBufStart = NULL; m_lpBufCur = NULL;
}
delete m_pSchemaMap; m_pSchemaMap = NULL;
// m_pStoreMap and m_pLoadArray are unioned, // so we only need to delete one
ASSERT((CObject*)m_pStoreMap == (CObject*)m_pLoadArray); delete (CObject*)m_pLoadArray; m_pLoadArray = NULL;Посмотрим, что происходит с архивом в случае прекращения работы посредством вызова метода AbortQ. Первым делом метод «отсоединяет» архив от файла, присваивая полю m__pFile значение NULL. Затем в том случае, если у архива есть ассоциированный с ним буфер, производится удаление буфера. Если в буфере остались данные, которые не были записаны в файл, то эти данные будут потеряны. Указатели на начало буфера и на текущую позицию буфера делаются равными NULL. Затем удаляется указатель на хэш-таблицу, содержащую номера схем классов, а за ней и хэш-таблица (или массив) сохраненных объектов.
Похожие статьи: Load, Метод Abort
Для нас главным в этом методе является то, что происходит вызов метода 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);
}
Похожие статьи: PreCreateWindow
Нетрудно заметить, что метод 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;
}
Похожие статьи: PreCreateWindow
Как легко заметить, конструктору CCtrlView::CCtrlView() в качестве параметра передается имя класса, которое тут же записывается в поле m_strClass. Отметим этот факт. Затем в методе CFrameWnd::CreateView() вызывается метод Create() нашего объекта представления:
BOOL CWnd::Create(LPCTSTR IpszClassName, LPCTSTR IpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
// can’t use for desktop or pop-up windows // (use CreateEx instead) ASSERT(pParentWnd != NULL); ASSERT((dwStyle & WS_POPUP) == 0); return CreateEx (0,
IpszClassName,
IpszWindowName,
dwStyle I WS_CHILD,
rect.left, rect.top,
rect.right – rect.left,
rect.bottom – rect.top,
pParentWnd->GetSafeHwnd(),
(HMENU)nID,
(LPVOID)pContext);
}
Мне бы хотелось, чтобы вы вспомнили, что происходит при получении окном класса CMDIChildWnd сообщения WM_CREATE. Во время работы этого метода вызываются еще много других методов, в том числе CFrameWnd::CreateView(). Именно при работе этого метода и происходит вызов метода CreateObject() для объекта класса окна представления. Естественно, метод CreateObject() вызывает конструктор объекта окна представления. И, разумеется, вызываются строго по порядку конструкторы классов, от которых унаследован наш объект. Если мы в качестве окна приложения используем объект класса CEditView, то первым вызывается конструктор CEditView::CEditView(). Исходный код этого конструктора находится в файде viewedit.cpp:
// pass a NULL style because dwStyleDefault stays for // backward compatibility
CEditView::CEditView() : CCtrlView(_T("EDIT"), NULL)
m_nTabStops = 8*4; // default 8 character positions m_hPrinterFont = NULL; m_hMirrorFont = NULL; m_pShadowBuffer = NULL; m_nShadowSize = 4);
}
Перед тем, как отработает этот конструктор, управление будет передано конструктору класса CCtrlView.
Исходный текст конструктора класса CCtrlView можно найти в файле viewcore.cpp:
CCtrlView::CCtrlView(LPCTSTR IpszClass, DWORD dwStyle) {
m_strClass = IpszClass;
m_dwDefaultStyle = dwStyle;
}
Похожие статьи: CMDIChildWnd
Я начал писать программу и остановился, словно услышал вопрос, задаваемый читателем: «Как же так, если, к примеру, мне хочется отображать данные в виде дерева или, скажем, в виде текста, мне так и придется прорисовывать дерево или использовать TextOut()?» Что ж, вопрос вполне закономерный. Мне тоже очень не хотелось думать, что разработчики MFC остановились на полпути и не довели дело до конца. Помните, читатель, мое федположение о том, что класс CView является абстрактным и предоставляет только базовые возможности для объектов класса окна представления? Отправной точкой дальнейших изысканий послужило предположение, что более специализированные классы окон представлений наследуются от CView. Заглянув в исходные коды MFC, я слегка обомлел. Оказывается, от CView наследуются классы CCtrlView, CScrollView. В свою очередь от CCtrlView унаследованы классы CListView, CTreeView, CEditView, CRichEditView. От CScrollView унаследованы CFormView, CPreviewView. От CFormView унаследованы классы CDaoRecordView, CRecordView, CHtmlView, COIeDBRecord-View. Графически это можно представить так:

(CVievT)
-[CCtrlView ]
—( CListView ]
—[CTreeView]
-(CEditVievT)
CRichEditView]
-(CScrollView ~)
—[CFormView ]
-(CDAORecordView)
—(CRecordView ]
-(CHtmlView ]
—[CQIeDBRecordView ]
—[ CPreviewView]
Даже судя по названиям, уже можно сделать выводы о предназначении этих классов. Наверное, класс окна отображения в моей программе мне нужно было наследовать не от CView, а от более специализированного класса, скажем, CEditView. Что ж, попробуем так и сделать. Но теперь нам нужно отследить,каким образом работают объекты этого класса. Давайте, читатель, подумаем вместе вами. На каком этапе начинает работать объект класса, унаследованного от CView? Наверное, при создание объекта окна представления. Давайте попробуем проследить, что произойдет в момент создания окна представления.
А теперь давайте займемся подсчетами. Вся моя программа со всеми описаниями классов, комментариями и пропусками состоит из 128 строк. Теперь вспомним, что нам пришлось бы сделать в том случае, если бы мы писали программу без использования архитектуры "документ/представление", используя только функции API. Во-первых, нам понадобилось бы так называемое «стандартное заклинание», в котором необходимо заполнить структуру типа WND-CLASS, зарегистрировать класс окна и запустить цикл обработки сообщений. Во-вторых, понадобилась бы обработка, как минимум, сообщения WM_CREAfE, во время которой необходимо создать окно класса «MDICLIENT». В-третьих, регистрация класса окна представления и написание оконной процедуры окна представления. .. Можно только представить себе, сколько строк содержалось бы в исходном коде такого приложения!
Я достаточно долго программировал с использованием только API, поэтому не буду утверждать, что MFC хуже или лучше «чистого» API. Тем не менее, возможности, которые предоставляет архитектура «документ/представление», не могут не впечатлять.
Какое сообщение получает окно в том случае, когда окно должно перерисовать себя? Естественно, сообщение WM__PAINT. Какой метод должен отвечать за обработку сообщения WM_PAINT у объекта класса CView? Конечно же, CView::OnPaint(). Следовательно, нам неплохо было бы заглянуть в исходный текст этого метода, который, кстати, находится в файле viewcore.cpp:
void CView::OnPaint() {
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}_________
[CView::OnPaint ) Видно, что вызов конструктора класса
Ucview.OnPrepareDC) СРа‘ПЮС опРеделяет‘ чт0 «”вод будет
^ – > осуществляться на окно класса CView.
Что ж, логично, иного трудно было ожидать. Теперь заглянем в код
метода CView::OnPrepareDC():
void CView::OnPrepareDC(CDC* pDC, CPrintlnfo* plnfo) {
ASSERT_VALID(pDC);
UNUSED(pDC); // unused in release builds
// Default to one page printing if doc length not known if (plnfo != NULL)
plnfo->m_bContinuePrinting =
(pInfo->GetMaxPage() != Oxffff || (plnfo->m_nCurPage == 1) ) ;
}
void CView::OnDraw(CDC*)
{
}
только подтверждает нашу догадку. Наверное, для того чтобы убедиться в правильности нашей догадки, лучше всего будет привести написанную программу, осуществляющую вывод информации в окно представления. Сделаем так, чтобы программа выводила в окно представления имя файла, на основе которого создан документ. Итак, описание класса CDocView нашей программы принимает следующий вид:

class CDocView : public CView {
DECLARE_DYNCREATE ( CDocView ) public:
CDocView();
virtual ~CDocView(); void OnDraw( CDC* pDC );
};
Ниже я привожу текст метода OnDraw():
void CDocView::OnDraw( CDC* pDC ) {
pDC->TextOut( 10, 10, GetDocument()->GetPathName () );
}
Программа откомпилировалась без ошибок!
Обратите внимание, читатель, что указатель на структуру типа CCreateContext (в нее чуть раньше мы записали наши указатели на документ,шаблон документа, а также указатели на фрейм и на окно представления документа) записывается сначала в поле lpCreateParams структуры cs типа CREATESTRUCT, а потом в поле IParam структуры mcs типа MDICREATESTRUCT Указатель на структуру mcs посылается окну типа MDICLIENT посредством сообщения WM_MDICREATE. Что же происходит дальше? После получения этого сообщения создается дочернее окно MDI, которое будет играть роль окна фрейма. Естественно, при своем создании оно получает сообщение WM_CREATE, на что реагирует вызовом метода CMDIChildWnd::OnCreate():
int CMDIChildWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) {
// call base class with IParam context (not MDI one) MDICREATESTRUCT* lpmcs;
lpmcs = (MDICREATESTRUCT*)lpCreateStruct->lpCreateParams; CCreateContext* pContext = (CCreateContext*)lpmcs->lParam;
return OnCreateHelper(lpCreateStruct, pContext);
Из переданной окну структуры типа CREATESTRUCT выбирается указатель на CCreateContext, после чего указатели на структуры типов CREATESTRUCT и CCreateStruct передаются методу CFrameWnd::OnCreateHelper:
int CFrameWnd::OnCreateHelper(LPCREATESTRUCT lpcs.
CCreateContext* pContext)
if (CWnd::OnCreate(lpcs) == -1) return -1;
// create special children first
if (!OnCreateClie*nt (lpcs, pContext) ) {
TRACEO("Failed to create client pane/view for frame.\n") ; return -1;
}
// post message for initial message string
PostMessage(WM_SETMESSAGESTRING, AFX__IDS_IDLEMESSAGE);
// make sure the child windows have been properly sized RecalcLayout();
return 0; // create ok
}
BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT,
CCreateContext* pContext)
{
// default create client will create a view if asked for it if (pContext != NULL && pContext->m_pNewViewClass != NULL) {
if (CreateView (pContext, AFX_IDW_PANE_FIRST) == NULL) return FALSE;
}
return TRUE;
}
CWnd* CFrameWnd::CreateView(CCreateContext* pContext,
UINT nID)
{
ASSERT(m_hWnd != NULL); ASSERT(::IsWindow(m_hWnd)); ASSERT(pContext != NULL);
ASSERT(pContext->m_pNewViewClass != NULL);
// Note: can be a CWnd with PostNcDestroy self cleanup CWnd* pView =
(CWnd*)pContext->m_pNewViewClass->CreateObject() ; if (pView == NULL) {
TRACE1("Warning: Dynamic create of view type
%hs failed.\n",
pContext->m_pNewViewClass->m_lpszClassName) ; return NULL;
ASSERT_KINDOF(CWnd, pView);
// views are always created with a border! if (!pView->Create(NULL, NULL,
AFX_WS_DEFAULT_VIEW, CRect(0,0,0, 0), this, nID,
pContext))
{
TRACEO("Warning: could not create view for frame.\n");
return NULL; // can’t continue without a view
}
if (afxData.bWin4 && (pView->GetExStyle () &
WS_EX_CLIENTEDGE))
{
// remove the 3d style from the frame, since the view is // providing it.
// make sure to recalc the non-client area
ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);
}
return pView;
}
Смотрите внимательно, читатель! Сначала создается объект (представление) того класса, который мы указали при создании шаблона документа. После этого создается непосредственно окно представления. При этом видно, родительским окном объявляется окно фрейма! Так что же получается? MFC решило за программиста одну из достаточно часто встречавшихся задач -расположение дочернего окна поверх клиентской области родительского окна. Теперь программисту нет необходимости самому создавать дочернее окно и отслеживать изменения размеров родительского окна – эти задачи решает за него MFC. Единственное, что в этом случае требуется от программиста, так это написать приложение в соответствии с требованиями архитектуры «документ/представление»!
Осталось понять одну «мелочь» – как осуществляется отображение данных документа в окне представления.
Я опять старался рассуждать логически.
Похожие статьи: CMDIChildWnd
Читатель, взгляните, пожалуйста, на исходный код метода 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)
Похожие статьи: CMDIChildWnd, PreCreateWindow
Во-первых, для того чтобы рассмотреть вопрос отображения документа, нам необходимо вспомнить, что у нас до сих пор указатель на информацию времени исполнения окна представления нашей программы равен NULL. В связи с этим необходимо описать класс окна представления..Следрвательно, наша программа немного изменилась. Сразу после описания класса документа я добавил описание класса окна представления:
class CDocView : public CView {
DECLARE_DYNCREATE ( CDocView ) public:
CDocView();
virtual -CDocView();
>;
IMPLEMENT_DYNCREATE( CDocView, CView )
CDocView::CDocView()
{
}
CDocView::-CDocView()
{
}
Кроме этого, я изменил метод lnitlnstance() класса CDocViewl:
BOOL CDocViewlApp::Initlnstance() {
#ifdef _AFXDLL
Enable3dControls(); #else
Enable3dControlsStatic(); #endif
LoadStdProfileSettings(); CDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate( IDR_DOCUMENT,
RUNTIME__CLASS ( CDoc ) , RUNTIME_CLASS( CMDIChildWnd ), RUNTIME_CLASS( CDocView ) ) ; AddDocTemplate( pDocTemplate ); CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame( IDR_RESOURCE ); m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();
return TRUE;
}
Когда я постарался откомпилировать эту программу, я получил сообщение о том, что у класса, наследуемого от CView, не определен метод OnDraw(). Ну, конечно же! Откуда же наш произвольный класс окна представления будет знать, каким образом ему необходимо перерисовываться? Давайте опять постараемся рассуждать логически. Какое сообщение получает окно, когда ему нужно перерисовать себя? Правильно, WM_PAINT. Но для обработки сообщения WM_PAINT объекты MFC используют метод OnPaintQ.
Не является исключением и объекты класса CView и унаследованных от него. Исходный код этого метода находится в файле viewcore.cpp:
void CView::OnPaint() {
// standard paint routine CPaintDC dc(this); OnPrepareDC(&dc); OnDraw(&dc);
}
Очевидно, что перерисовка осуществляется методом OnDraw(). Взглянем на исходный код этого метода, который также находится в файле viewcore.cpp:
void CView::OnDraw(CDC*)
{
}
To, что этот метод не делает ничего, подтверждает нашу догадку о том, что нам нужно переписать именно этот метод. Что ж описание нашего класса CDocView придется немного изменить. Теперь оно будет выглядеть так:
class CDocView : public CView {
DEСLARE_DYNCREATE ( CDocView ) public:
CDocView();
virtual ~CDocView(); void OnDraw( CDC* pDC );
};
Естественно, нам придется переопределить и добавить в программу метод OnDravtr(). Пока этот метод нас не очень интересует, поэтому оставим его пустым:
void CDocView::OnDraw( CDC* pDC )
{
}
Наверное, отсюда можно сделать вывод, что класс CView предоставляет только базовые возможности для отображения данных. Надеюсь, у нас еще будет шанс разобраться с этим.
Любознательный читатель уже, наверное, приготовил мне вопрос: да, конечно, все что рассказывается, это верно, но каким же образом создается окно представления и как оно относится к окну фрейма и документу?
Похожие статьи: CDocTemplate, CMDIChildWnd, lnitlnstance
Но вернемся от нашей программы к методу CDocument.On-OpenDocument(). Естественно, что после того, как мы считали содержимое архива в поля нашего документа, архив и файл нашей программе больше не нужны и мы их можем спокойно закрыть, что и происходит. Для этого используются методы CArchive::Close() и CDocument::ReleaseFile().
Что ж, та задача, которую мы поставили перед собой, мы успешно выполнили: узнали не только, как подготовиться к открытию файла, но и прошли по всему пути от файла до документа, начиная от подготовки диалогового окна для открытия файла, продолжая созданием окна фрейма и завершая чтением файла в поля документа. Кроме того, мы узнали какие ресурсы при этом используются, частично определили формат и назначение этих ресурсов. На этом наша работа с файлом завершена до момента сохранения файла на диске. Но теперь нам необходимо понять, что происходит при отображении документа.
Документ, естественно, рождается из файла, поэтому первым делом метод, вызывая GetFile(), открывает файл, имя которого, как я уже говорил, он получил в качестве аргумента. Затем начинается самое интересное. На основе файла создается объект класса САг-chive, т. е. АРХИВ. В архитектуре «документ/представление» считается, что программист должен иметь возможность легко сохранять созданные в памяти структуры данных в дисковом файле, после чего он должен иметь возможность вновь считать их из файла. Архив позволяет программисту читать и записывать в файл не просто некоторые объемы информации, а ОБЪЕКТЫ всевозможных типов! Именно поэтому его использование в большинстве случаев представляется оправданным. И дальше вызывается метод Serialize(), который работает уже не с файлом, а с архивом! По умолчанию этот метод не делает ничего. Естественно, откуда программе знать, что и как программисту захотелось сохранить в архиве? А для программиста здесь раздолье! Можно проверить формат открытого файла, определить, при необходимости, его структуру, выполнить все мыслимые и немыслимые действия. Для того чтобы продемонстрировать работу метода Serialize(), давайте попробуем открыть файл в нашей программе и считать его в буфер. Внесем небольшие изменения в наш класс документа – добавим два поля, в которые запишем, во-первых, размер файла, а второй будет являться указателем на буфер, в который мы будем читать файл. После внесенных изменений наша программа выглядит следующим образом:
#include "stdafx.h" #include <afxcview.h> #include "resource.h"
// Объявляем класс приложения, его поля и методы.
class CDoc : public CDocument {
DECLARE_DYNCREATE( CDoc ) int nFileLength; void* pMyFile; public: CDocO ;
void Serialize ( CArchive &ar ); virtual -CDoc();
}/
• IMPLEMENT_DYNCREATE( CDoc, CDocument )
CDoc::CDoc()
{
}
CDoc::-CDoc()
{
}
void CDoc::Serialize( CArchive &ar ) {
if( ar.IsStoring() )
{
}
else {
// Определяем длину файла.
nFileLength = ar.GetFile()->GetLength();’
TRACE( _T( "File length = %d.\n"), nFileLength’ );
// Выделяем буфер в памяти, в который будем считывать файл. pMyFile = new chart nFileLength ];
// Считываем файл в буфер.
ar.Read( pMyFile, nFileLength ); }
}
class CDocViewlApp : public CWinApp {
public:
CDocViewlApp(); protected:
afx_msg void OnFileOpen(); virtual BOOL Inifrlnstance(); DEC LARE_ME S S AGE_MA P()
};
CDocViewlApp::CDocViewlApp()
{
}
void CDocViewlApp::OnFileOpen()
{
}
class CMainFrame : public CMDIFrameWnd {
DECLARE_DYNAMIC( CMainFrame ); public:
CMainFrame();
>;
IMPLEMENT_DYNAMIC (CMainFrame, CMDIFrameWnd)
CMainFrame::CMainFrame()
{
}
BOOL CDocViewlApp::Initlnstance() {
#ifdef _AFXDLL
Enable3dControls(); #else
Enable3dControlsStatic(); #endif
LoadStdProfileSettings(); CDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate( IDR_DOCUMENT,
RUNTIME_CLASS( CDoc ) , RUNTIME_CLASS( CMDIChildWnd ), NULL );
AddDocTemplate( pDocTemplate ); CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame ( IDR__RESOURCE ) ; m_pMainWnd = pMainFrame; pMainFrame->ShowWindow(m_nCmdShow ); pMainFrame->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP( CDocViewlApp, CWinApp )
ON_COMMAND( ID_FILE_OPEN, CWinApp::OnFileOpen ) END_MESSAGE_MAP()
CDocViewlApp theApp;
Если мы запустим нашу программу на выполнение (внутри среды разработки) и откроем какой-нибудь файл, то увидим, что на отладочном мониторе появилось сообщение о размере открытого файла. Любознательный читатель может проверить, произойдет ли считывание содержимого файла в буфер. Я абсолютно уверен, что в обычных условиях файл будет считан в буфер без каких-либо проблем.
Похожие статьи: CDocTemplate, CMDIChildWnd, CWinApp, OnFileOpen
Я надеюсь, что читатель помнит о том, что пока при создании шаблона нашего документа указатель на информацию времени исполнения класса окна представления равен нулю. Следовательно, пока мы можем на некоторое время оставить вопрос о создании окна представления и вернуться в метод CMDIChildWnd::LoadFrame().
Из текста метода CMDIChildWnd::LoadFrame() кроме сказанного выше становится понятным и назначение начальной подстроки строкового ресурса – эта подстрока определяет заголовок создаваемого окна фрейма.
Теперь, когда мы знаем все о том, как происходит создание нового документа и создание нового фрейма, нам придется вернуться к методу ь CMultiDocTemplate::OpenDocumentFile() и продолжить его рассмотрение на имя открываемого файла. Текст этого метода, приведенный ниже, можно найти в файле doccore.cpp:
BOOL CDocument::OnOpenDocument(LPCTSTR IpszPathName) {
if (IsModifiedO )
TRACEO("Warning: OnOpenDocument replaces an unsaved
document.\n") ;
CFileException fe;
CFile* pFile = GetFile(IpszPathName,
CFile::modeRead|CFile::shareDenyWrite,
&fe) ; if (pFile == NULL) {
ReportSaveLoadException(IpszPathName,
return FALSE;
DeleteContents ();
&fe, FALSE,
AFX_IDP FAILED TO_OPEN_DOC);
SetModifiedFlag(); // dirty during de-serialize
CArchive loadArchive(pFile,
CArchive::load | CArchive:rbNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; TRY {
CWaitCursor wait;
if (pFile->GetLength() != 0)
Serialize(loadArchive); // load me
loadArchive.Close(); ReleaseFile(pFile, FALSE);
}
CATCH_ALL(e) {
ReleaseFile(pFile, TRUE);
DeleteContents(); // remove failed contents
TRY {
ReportSaveLoadException(IpszPathName, e, FALSE,
AFX_IDP_FAILED_TO_OPEN_DOC);
}
END_TRY
DELETE_EXCEPTION(e); return FALSE;
}
END_CATCH_ALL
SetModifiedFlag(FALSE); // start off with unmodified
return TRUE;
}
Я прошу читателя уделить этому методу ОСОБОЕ внимание. Немного опережая события, скажу, что именно здесь мы увидим, в какой момент из файла получается документ, какой метод необходимо переопределить, чтобы всю рутинную работу возложить на MFC.
Похожие статьи: CMDIChildWnd, OpenDocumentFile
Вполне очевидно, что этот метод используется для того, чтобы определить класс и стили окна, которое будет создаваться.
Сначала определяется класс окна. В том случае, если класс окна до сих пор не определен (и ТОЛЬКО в этом случае), то полю IpszClass присваивается значение _afxWndFrameOrView. Что находится в этой переменной, станет ясным после того, как мы взглянем на участок кода, находящийся в файле afximpl.h:
// special AFX window class name mangling
#ifndef _UNICODE #define _UNICODE_SUFFIX #else
#define _UNICODE_SUFFIX _TPu") #endif
#ifndef _DEBUG #define DEBUG SUFFIX
#else
#define _DEBUG_SUFFIX _T(M") #endif
#ifdef _AFXDLL #define _STATIC_SUFFIX #else
#define _STATIC_SUFFIX _T(^s") #endif
#define AFX_WNDCLASS(s) \ _TPAfx") _T(s) _T("42") _DEBUG_SUFFIX
#define AFX_WND #define AFX_WNDCONTROLBAR #define AFX_WNDMDIFRAME #define AFX_WNDFRAMEORVIEW #define AFX WNDOLECONTROL
STATIC SUFFIX UNICODE SUFFIX
AFX_WNDCLASS("Wnd") AFX_WNDCLASSrControlBar") AFX_WNDCLASS("MDIFrame") AFX_WNDCLASS("FrameOrView") AFX_WNDCLASSrOleControl")
С другой стороны, в файле wincore.cpp мы увидим:
const TCHAR _afxWnd[] = AFX_WND;
const TCHAR _afxWndControlBar[] = AFX_WNDCONTROLBAR; const TCHAR _afxWndMDIFrame[] = AFX_WNDMDIFRAME; const TCHAR _afxWndFrameOrView[] = AFX_WN D FRAME ORVIEW; const TCHAR _afxWnd01eControl[] = AFX_WNDOLECONTROL;
Исходя из этих фактов, а также зная, что в настоящий момент мы генерируем отладочную версию нашей программы, сделаем вывод о том, что классом создаваемого окна фрейма будет являться «AfxFrameOrView42d». В качестве упражнения я предлагаю читателю проверить правильность этого утверждения.
Определив класс и стили создаваемого окна фрейма, мы получили все необходимое для создания окна. Следовательно, никаких проблем создание окна фрейма вызвать не должно.
Похожие статьи: Значение afxWndFrameOrView
Уважаемый читатель, обратите, пожалуйста, внимание, на тот факт, что сразу после создания структуры 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;
}
Похожие статьи: PreCreateWindow
Для только что созданного объекта осуществляется вызов метода 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(), который мы можем использовать для того, чтобы произвести какие-то действия ДО создания окна.
Похожие статьи: CMDIChildWnd, PreCreateWindow
Пока мы видим, что сразу после проверки того, что указатель на документ и идентификатор ресурсов ненулевые, создается структура типа CCreateContext. Описание этой структуры находится в файле afxext.h:
struct CCreateContext // Creation information structure // All fields are optional and may be NULL
{
// for creating.,new views CRuntimeClass* m_pNewViewClass;
// runtime class of view to create or NULL CDocument* m_pCurrentDoc;
// for creating MDI children (CMDIChildWnd::LoadFrame) CDocTemplate* m_pNewDocTemplate;
// for sharing view/frame state from the // original view/frame
CView* m_pLastView; CFrameWnd* m_pCurrentFrame;
// Implementation CCreateContext();
};
В поля этой структуры записываются указатели на документ, шаблон документа, а также указатели на фрейм, который будет создан, и на окно представления документа. Обратим внимание на этот факт, дело в том, что эти данные будут использованы в дальнейшем.
Похожие статьи: CDocTemplate, CMDIChildWnd
