MFC学习: 1.CObject类为MFC总类,该类下面有一个重要的类CCmdTarget。而CCmdTarget类下面又有四个重要的继承类,分别为:CWinThreadCDocumentCDocTemplate CWnd类。所以,可以得出一个大概继承图,如图所示: CObject--->CCmdTarget ---->CWinThread ---->CWinApp
                                                          ----->CDocTemplate
                                                          ----->CDocument
                                                          ----->CWnd
      
其中CWnd类下属又有几个重要的继承类,分别为CFrameWndCDialogCViewControls CWnd---->CFrameWnd
                             ---->CDialog
                             ---->CView
                             ---->Controls
   
归纳一下,CCmdTarget类为MFC主要类的总钢。应用程序(CWinApp)间接继承于它,文档模板(CDocTemplate)继承于它,文档类(CDocument)继承于它,还有一个非常重要的类窗口类(CWnd)继承于它。 窗口类是一切Windows可见窗口,(包括主窗口,子框窗口,对话框,控件,View窗口)的父类。凡是能可见的,基本上是继承于CWnd,而抽象于其中的(即不可见的)则不继承于它。

<?xml:namespace prefix = o />

2.MFC头文件: STDAFX.H:该文件用来作为Precompiled header file,其内只是载入其它的MFC头文件。

AFXWIN.H:每个MFC程序都必须载入它,因为它以及它所载入的文件声明了所有的MFC类。此文件内包含AFX.H,后者又包含AFXVER_.H,后者又包含AFXV_W32.H,后者又包含WINDOWS.H

AFXEXT.H:凡使用工具栏、状态栏的程序必须载入此文件。

AFXDLGS.H:凡使用通用型对话框(Common Dialog)的MFC程序要载入此文件。

AFXCMN.H:凡使用Windows9x新增的通用型控件(Common Control)之MFC程序需载入此文件。

AFXCOLL.H:凡使用Collections Class(处理数据结构如数据,链表类等)之程序需载入此文件。

AFXDLLX.H:凡MFC extension DLLs需载入此文件。

AFXRES.H:MFC程序的RC文件必须载入此文件。MFC对于标准Windows资源(如File,Edit等)的ID均有默认定义,这此定义在该头文件内。

3.什么是Precompiled Header 一个应用程序在发展过程中需不断的编译,而Windows程序载入的.H头文件非常巨大且内容不变,编译器如果不Precompiled的话,每次需要编译的时间非常多,所以Precompiled Header就是将.H文件一次编译后的结果存储起来,第二次编译时就可以直接从磁盘中读取。

4.关于应用程序的进入点WinMain和窗口过程WndProc MFC代码由于类封装了API,所以,根本就看不到程序的进入WinMain和窗口处理函数WndProc,但是,由于这两个函数有相当程序的不变性,所以,MFC就把有着相当固定行为的WinMain内部操作封装在WinApp中,把有着相当固定行为的WndProc内部操作封装在CFrameWnd中。也就是说: CWinApp代表程序本体         ■CFrameWnd代表一个主框窗口(Frame Window)

5.CWinApp类取代了SDK中的WinMain地位:
    CWinApp本身就代表一个程序本体,程序本体是指与程序本身有关而不与窗口有关的数据或动作。比如,WinMain传递的四个参数,注册窗口类,分派消息等。比如,CWinApp类中就定义了WinMain函数的四个参数(m_hInstance,m_hPrevInstance,m_lpCmdLine,m_nCmdShow)。还定义了一些重要的如InitApplication(),InitInstance(),Run()等函数。传统的WinMain函数就是由此三个函数完成。
    另外,CWinApp中还应该有个主窗口的handle,是的,但它不在CWinApp类中,而是在CWinThread类中实现,在CWinThread类中,有一个指向窗口类的指针的成员变量。如下所示 CWnd* m_pMainWnd

6.CFrameWnd类取代了SDK中的WndProc地位
    众所周知,WndProc是用来处理窗口(包括初始化,处理消息,显示等)的函数,那么CFrameWnd也是,首先在头文件中,我们要继承一个CFrameWnd的类,并在此定义要处理的消息(DECLARE_MESSAGE_MAP),然后,在源文件中,定义该类的消息实现(BEGIN_MESSAGE_MAP,END_MESSAGE_MAP)

7.MFC中阴晦的WinMain:
    首先,我们必须在源文件中定义了一个CMyWinApp的实体它是第一步操作。(如: CMyWinApp  MyWinApp;注:代码详见:G:\Program\MyMFC\Hello\Hello.dsw),CMyWinApp继承自CWinApp,CMyWinApp无构造函数,但需调用CWinApp构造函数。配置好MyWinApp实体的一些信息,包括获得该线程的句柄,线程的ID,该实体的句柄等。配置完后,_tWinMain登场,注意,我们没有撰写WinMain的代码,WinMain是MFC早已准备好并由链接器直接加到应用程序代码中的。_tWinMain是为了支持Unicode而准备的一个宏,实质还是WinMain。好,_tWinMain函数做了什么工作呢?它只是在里面调用AfxWinMain函数。如下:return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); AfxWinMain是一个全局的函数,并不属于任一个类。
    下面让我们来看一看AfxWinMain函数做了些什么工作?

第一、它会获得Application Object的指针(亦CWinApp派生对象的指针),在此是获得MyWinApp的指针,以备后用。它是通过下面这句来实现的:
            CWinApp* pApp = AfxGetApp();  //AfxGetApp为全局函数,不属于任何类,详见第8条... 注:凡是函数前有Afx字样的都是全局函数。

第二、调用AfxWinInit(...)函数,用作MFC GUI程序初始化的一部分,这个函数详见后解...

第三、用上面获得的pApp调用InitApplication()函数,如下:pApp->InitApplication(); InitApplication函数详见后解...

第四、用pApp调用InitInstance()函数,如下:pApp->InitInstance(); InitInstance函数详见后解...

第五、用pApp调用Run()函数,如下: nReturnCode = pApp->Run();  Run函数详见后解...

第六、最后调有AfxWinTerm函数,结束该应用程序。
    所以,主要的AfxWinMain做的工作就应该如下代码所示:
int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)

{

int nReturnCode = -1;

CWinApp* pApp = AfxGetApp();   //详见第8

AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);  //详见第9

pApp->InitApplication();     //详见第10

pApp->InitInstance();        //详见第11

nReturnCode = pApp->Run();   //详见第12

AfxWinTerm();

return nReturnCode;

}

8.AfxGetApp是如何获得CWinApp继承类(在此即CMyWinApp)的实体的指针的? AfxGetApp是一个全局函数,定义于AFXWIN1.INL中,如下: _AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; } afxCurrentWinApp则又是一个宏,定于AFXWIN.H中: #define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp 而在CWinApp类的构造函数中,有一段代码是这样定义的: AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

pModuleState->m_pCurrentWinApp = this;

这样,应该看清楚他是如何获得CWinApp继承类(在此是指CMyWinApp类)的实体的指针了吧? 那么,pApp->InitApplication() / pApp->InitInstance() / pApp->Run()就可以理解成这样: CMyWinApp::InitApplication(); / CMyWinApp::InitInstance(); / CMyWinApp::Run() 因为CMyWinApp只继承了InitInstance的实现,所以,就导致调用: CMyApp::InitApplication(); / CMyWinApp::InitInstance(); / CWinApp::Run()

9.AfxWinInit ----AFX内部的初始化操作: AfxWinInitCWinApp构造函数后的第一个操作,也就是第二步操作,以下是它的操作摘要: BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow)

{

ASSERT(hPrevInstance == NULL);

AFX_MODULE_STATE* pState = AfxGetModuleState();

pState->m_hCurrentInstanceHandle = hInstance;

pState->m_hCurrentResourceHandle = hInstance;

// fill in the initial state for the application

CWinApp* pApp = AfxGetApp(); //还是获得CMyWinApp实体的指针。

if (pApp != NULL)

{

// Windows specific initialization (not done if no CWinApp)

pApp->m_hInstance = hInstance; //设定WinMain四个参数的初始值。

pApp->m_hPrevInstance = hPrevInstance;

pApp->m_lpCmdLine = lpCmdLine;

pApp->m_nCmdShow = nCmdShow;

pApp->SetCurrentHandles();

}

if (!afxContextIsDLL)

AfxInitThread();

return TRUE;

}

10.CWinApp::InitApplication()函数: AfxWinInit函数对内部初始化之后,进入第三步操作:InitApplication,由于CMyWinApp继承自CWinApp,InitApplication又是CWinApp的一个虚拟函数,我们在CMyWinApp中没有改写它(大部分情况下也不需要改写它),所以我们调用的是CWinApp::InitApplication(),下面我们来看看InitApplicationMFC中做了什么动作? BOOL CWinApp::InitApplication()

{

if (CDocManager::pStaticDocManager != NULL)

{

if (m_pDocManager == NULL)

CDocManager::pStaticDocManager = NULL;

}

if (m_pDocManager != NULL)

m_pDocManager->AddDocTemplate(NULL);

else

CDocManager::bStaticInit = FALSE;

return TRUE;

}

这些操作都是MFC为内部管理而做的。只要记住一点,我们的派生类无需改写它,它是关于CDocManager的类,关于该类详见后解...

11.CMyWinApp::InitInstance()函数:通过上面应该知道,我们的CMyWinApp类应该(而且必须)改写此函数,因为该函数在CWinApp类中只是一个空函数而已。在CMyWinApp中要做些什么动作呢?看看写的代码吧?代码详见G:\Program\MyMFC\Hello\Hello.dsw:
BOOL CMyWinApp::InitInstance()

{

m_pMainWnd = new CMyFrameWnd;

m_pMainWnd->ShowWindow(m_nCmdShow);

m_pMainWnd->UpdateWindow();

return TRUE;

} 哦,它生成一个CMyFrameWnd的实体,把主应用程序与窗口函数联系起来(上面说过,CFrameWnd等于于SDK中的WndProc)。在SDK的代码中,要让窗口ShowUpdate之前必须还要做些工作,什么工作呢?看看SDK程序: int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

static TCHAR szAppName[] = TEXT ("SysMets2") ;

HWND         hwnd ;

MSG          msg ;

WNDCLASS     wndclass ;

wndclass.style         = CS_HREDRAW | CS_VREDRAW ;   //注册窗口类

wndclass.lpfnWndProc   = WndProc ;

wndclass.cbClsExtra    = 0 ;

wndclass.cbWndExtra    = 0 ;

wndclass.hInstance     = hInstance ;

wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;

wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName  = NULL ;

wndclass.lpszClassName = szAppName ;

if (!RegisterClass (&wndclass))

{

MessageBox (NULL, TEXT ("This program requires Windows NT!"),

szAppName, MB_ICONERROR) ;

return 0 ;

}

hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),  //构建窗口

WS_OVERLAPPEDWINDOW | WS_VSCROLL,

CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL) ;

ShowWindow (hwnd, iCmdShow) ;

UpdateWindow (hwnd) ;

while (GetMessage (&msg, NULL, 0, 0))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

return msg.wParam ;

} 其中,在ShowWindowUpdateWindow之前,要做两件事,一是注册窗口类(红色部份),二是构建窗口(蓝色部分),那么,我们可以肯定,就这一句代码:m_pMainWnd = new CMyFrameWnd; 就可以为我们做完了这两件事注册窗口和构建窗口。是怎么做的呢?首先,它调用CMyFrameWnd的构造函数,该构造函数的实现是这样的:(代码详见G:\Program\MyMFC\Hello\Hello.dsw) CMyFrameWnd::CMyFrameWnd()

{

Create(NULL,"Hello MFC",WS_OVERLAPPEDWINDOW,rectDefault,NULL,"MainMenu");

}
    调用CMyFrameWnd中的Create成员函数,CMyFrameWnd中实现该Create函数了吗?没有,那再往父类追朔,CFrameWnd中实现Create函数了吗?实现了!那调用CFrameWnd::Create()函数。
BOOL CFrameWnd::Create( LPCTSTR lpszClassName,

LPCTSTR lpszWindowName,

DWORD dwStyle = WS_OVERLAPPEDWINDOW,

const RECT& rect = rectDefault,

CWnd* pParentWnd = NULL,

LPCTSTR lpszMenuName = NULL,

DWORD dwExStyle = 0,

CCreateContext* pContext = NULL );

lpszClassName指定WNDCLASS窗口类,我们至今为止还没有注册WNDCLASS窗口类,所以,只能先定义为NULL,往后再看,lpszWindowName指定窗口的标题。后面的一些参数都有默认值,关于这些默认值的一些说明,可以详见<MFC深入浅出》第三篇第6282-283页。 调用CFramwWnd::Create函数,可以引发注册窗口类动作,也就是上面的第一件事:看看它是怎么实现的?

BOOL CFrameWnd::Create(...)  //此处省略了它的参数 {
       HMENU hMenu = NULL;
       if(lpszMenuName!=NULL)
       {
           HINSTANCE hInst == AfxFindResourceHandle(lpszMenuName,RT MENU);
           hMenu = ::LoadMenu(hInst,lpszMenuName);        }
       m_strTitle = lpszWindowName;
       CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,

rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,

pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext);

return TRUE;

}

CFrameWnd::Create函数中调用CreateEx函数,而CreateEx函数会调致调用CWnd::CreateEx函数的操作: BOOL CWnd::CreateEx(...)   //此处省略了它的参数

{

CREATESTRUCT cs;

cs.dwExStyle = dwExStyle;

cs.lpszClass = lpszClassName;

cs.lpszName = lpszWindowName;

cs.style = dwStyle;

cs.x = x;

cs.y = y;

cs.cx = nWidth;

cs.cy = nHeight;

cs.hwndParent = hWndParent;

cs.hMenu = nIDorHMenu;

cs.hInstance = AfxGetInstanceHandle();

cs.lpCreateParams = lpParam;

PreCreateWindow(cs);

AfxHookWindowCreate(this); //此動作將在第9章探討。

HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,

cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,

cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

...

}

啊哈!CreateWindowEx我们可以容易看出来,它把我们的第二件事,也就是构建窗口完成了,那么,第一件事(注册窗口函数),就是在调用PreCreateWindows函数中完成的。看看PreCreateWindow函数长得什么样?

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)

{

if (cs.lpszClass == NULL)

{

AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG);

cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background

}
       ...
    }
有点头目了,因为出现了RegisterClass的字样,再看看AfxDeferRegisterClass是什么东东: #define AfxDeferRegisterClass(fClass) \
    ((afxRegisterClass & fClass)? TRUE : AfxEndDeferRegisterClass(fClass))
afxRegisterClass又是一个宏: #define afxRegisterClass AfxGetModuleState()->m_fRegisteredClasses 好,最主要最主要的注册窗口动作将发生在AfxEndDeferRegisterClass中: 关于此函数代码有点长,可以参见《深入浅出MFC》第285-286页。但它最主要做了几件事,一,成生一个WNDCLASS对象,二、指定wndclasslpfnWndProchInstancehCursor的值,三、调用全局函数AfxRegisterClass注册窗口。 看看,就一个CMyFrameWnd* MyFrameWnd = new CMyFrameWnd代码,并且在CMyFrameWnd构造函数中改写Create函数,就可以把注册窗口类、构建窗口的一些繁琐工作全部在台面下做完。台上完全不动丝毫声色。回顾一下上面这一大段的说明的简要架构: 调用CMyFrameWnd::CMyFrameWnd()--->调用CFrameWnd::Create()函数--->调用CWnd::CreateEx()函数--->

CFrameWnd::PreCreateWindow()---> 调用AfxRegisterClass() -->调用::CreateWindowEx()

12.CWinApp::Run – 程序生命的活水源头

我们的程序到这里,已到第五步了。窗口类已注册好了,窗口也Create出来了,ShowWindow和UpdateWindow也执行了,但是,对照SDK程序,WinMain中还有一步没有做,就是翻译消息和分派消息(TranslateMessage和DispatchMessage)。别急,这些工作做在了CWinApp::Run中。

我们的CMyWinApp没有改写Run这个虚拟函数,所以,应该执行CWinApp::Run()函数,这是什么样的执行情况呢?

int CWinApp::Run()

{

if (m_pMainWnd == NULL && AfxOleGetUserCtrl())

{

         // Not launched /Embedding or /Automation, but has no main window!

TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");

AfxPostQuitMessage(0);

}

return CWinThread::Run();

}

其中,m_pMainWnd是在CWinThread定义的一个指向CWnd的指针,它的作用就像:AfxGetApp()->m_pMainWnd; 指向本应用程序的窗口类的指针。这里的意思应该是判断应用程序中窗口类存不存在,如不存在,代码发出警告后退出。如存在,执行CWinThread::Run()
    int CWinThrad::Run()
    {
BOOL bIdle = TRUE;

LONG lIdleCount = 0;

for (;;)
    {

while (bIdle && !::PeekMessage(&m_msgCur,NULL,NULL,PM_NOREMOVE)

{

// call OnIdle while in bIdle state

if (!OnIdle(lIdleCount++))
       bIdle = FALSE;

}

// phase2: pump messages while available

do

{ // pump message, but quit on WM_QUIT

if (!PumpMessage())

return ExitInstance();

// reset "no idle" state after pumping "normal" message

if (IsIdleMessage(&m_msgCur))

{

bIdle = TRUE;

lIdleCount = 0;

}

} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));

}
       ASSERT(FALSE);
    }
    CWinThread::Run()
中有一个PumpMessage()函数,是我们分派消息的地方: BOOL CWinThread::PumpMessage()
    {

if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))

{

return FALSE;

}

// process this message

if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))

{

::TranslateMessage(&m_msgCur);

::DispatchMessage(&m_msgCur);

}

return TRUE;

} 到此,对于SDKWinMain函数中所做的全部动作,在MFC中已一一揪出来了。可以看出,它们全隐含在各式各样的类中。彼此百转曲折,但最后还是完成了和SDK一模一样的操作。下面就只剩下窗口处理函数的问题了,我们在AfxEndDeferRegisterClass中看到过这样的代码: wndcls.lpfnWndProc = DefWindowProc; 意思是说,把所有的消息都发送给DefWindowProc这个函数,但是,我们了解到,处理消息的是继承CFrameWnd的类(即CMyFrameWnd),DefWindowProc是怎么把收到的消息再传给CFrameWnd类的呢?其中牵扯到了MFC的乾坤大挪移,这部分讨论见后,亦可见《深入浅出MFC》第九章。

13.来龙去脉整理 摘自《深入浅出MFC

程序的诞生: Application object 產生,内存於是獲得配置,初值亦設立了。

AfxWinMain 執行AfxWinInit,後者又呼叫AfxInitThread,把消息队列尽量加大到96

AfxWinMain 執行InitApplication。這是CWinApp 的虛函数,但我們通常不改

AfxWinMain 執行InitInstance。這是CWinApp 的虛函数,我們必須改寫它。

CMyWinApp::InitInstance 'new' 了一個CMyFrameWnd 对象

CMyFrameWnd 构造函数调用Create,產生主窗口。我們在Create 參數中指定的窗口类是NULL,于是MFC根据窗口种类,自行为我们注册一个AfxFrameOrView42d的窗口类

回到InitInstance 中繼續執行ShowWindow,顯示窗口。

執行UpdateWindow,於是發出WM_PAINT

回到AfxWinMain,執行Run,進入消息循环。

程序的运行: 程式獲得WM_PAINT 訊息(藉由CWinApp::Run 中的::GetMessage 迴路)。

WM_PAINT 經由::DispatchMessage 送到視窗函式CWnd::DefWindowProc 中。

CWnd::DefWindowProc 將訊息繞行過訊息映射表格(Message Map)。

繞行過程中發現有吻合項目,於是呼叫項目中對應的函式。此函式是應用程式

利用BEGIN_MESSAGE_MAP END_MESSAGE_MAP 之間的巨集設立起來的。

標準訊息的處理常式亦有標準命名,例如WM_PAINT 必然由OnPaint 處理。

程序的死亡:

使用者選按【File/Close】,於是發出WM_CLOSE

CMyFrameWnd 並沒有設置WM_CLOSE 處理常式,於是交給預設之處理常式。

預設函式對於WM_CLOSE 的處理方式是呼叫::DestroyWindow, 並因而發出WM_DESTROY

預設之WM_DESTROY 處理方式是呼叫::PostQuitMessage,因此發出WM_QUIT

CWinApp::Run 收到WM_QUIT 後會結束其內部之訊息迴路,然後呼叫ExitInstance,這是CWinApp 的一個虛擬函式。

如果CMyWinApp 改寫了ExitInstance , 那麼CWinApp::Run 所呼叫的就是CMyWinApp::ExitInstance,否則就是CWinApp::ExitInstance

最後回到AfxWinMain,執行AfxWinTerm,結束程式。

14.什么中Callback函数: 凡是由用户设计而却由Windows系统调用的函数,统称为Callback函数,这些函数都有一定的类型,以配合Windows的调用操作。 某些API函数以Callback作为其参数之一,这些API,如SetTimerLineDDAEnumObjects.通常这种API会在进行某种行为之后或满足某种状态之时调用该Callback函数。另外,如果类的成员函数是一个Callback函数,它必须被声明为”static”,才能把C++编译器加诸于函数的一个隐藏参数this去掉。 那为什么要去掉this指针呢?因为callback函数是给windows函数调用的,Windows并不借助任何对象调用该函数。如果去掉static的话,就会导致堆栈中有一个随机变量会成为this指针,而结果就是程序的崩溃。记住:凡在类中声明为static的东西(不管是函数或变量)都并不和对象结合在一起,它们是类的一部分,不属于对象。

15.Document/View学习: Document: DocumentMFCCDocument中被实例化,CDocument本身没有什么实际用途。它只是提供一个空壳。当你开发自己的程序时,应该从CDocument派生出一个属于自己的CDocument类,并且在类中声明一些成员变量,用以承载(容纳)数据。然后再(至少)改写专门负责文件读写操作的Serialize函数。由于CDocument派生自CObject,所以它就有了CObject所支持的一切性质,包括运行时类型识别(RTTI)、动态创建(Dynamic Creation)、文件读写(Serialization)。又由于它派生自CCmdTarget,所以它可以接收来自菜单或工具栏里的WM_COMMAND消息。 View:View是负责描述(呈现)Document中的数据。ViewMFCCView中被实例化,CView本身并无实际用途,它只是提供一个空壳。当你开发自己的程序时,应该从CView派生出一个属于自己的CView类,并且在类中(至少)改写专门负责显示数据的OnDraw函数,OnDraw是专门针对屏幕的,如果要针对打印机,还要改写OnPaint函数。由于CView派生自CWnd,所以它可接收一般的Windows消息(如:WM_SIZE,WM_PAINT等),又由于它又派生自CCmdTarget,所以它可以接收来自菜单或工具栏的WM_COMMAND消息。 Document Frame:前面说过,Document是负责操纵如何存取数据的,而CView是负责如何显示数据的,那么Document Frame是干什么用的的,答案是它是专门负责显示界面的。比如,你有两种不同的数据类型,一种为TEXT,一种为Bitmap,你想要在用户在显示不同类型数据时,有不同的使用者界面,那和,Document Frame就起作用了。 Document TemplateMFCDocument/View/Frame视为三位一体,每当使用者欲打开(或新增)一份文件,程序应该做出DocumentViewFrame各一份。这个“三口组”成为一个运行单元,由所谓的Document Template掌管,MFCCDocTemplate类掌管此事,它是怎么掌管的?看看以下代码:(代码详见:G:\Program\MyMfc\Scrible\Scrible.dsw工作区中的Scrible0.cpp源文件中) CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(

IDR_SCRIBTYPE,

RUNTIME_CLASS(CScribleDoc),

RUNTIME_CLASS(CChildFrame), // custom MDI child frame

RUNTIME_CLASS(CScribleView));

AddDocTemplate(pDocTemplate); //如果程序能处理多种类型,那么就要多次调用AddDocTemplate(pDocTemplate)

转载于:https://www.cnblogs.com/shipfi/archive/2005/03/19/121629.html

以前看书时记得一些笔记(二),很早了,现在再看都有些看不懂了相关推荐

  1. 突然感到很无聊当没有看书时

    今天星期五,按常规是该看书的.可.............. 教室被人占了, 去计算机室,明天考试,准备计算机室 只好心情很烦的回了. 现在第一次感到自己没有去看书是多么烦的一件事啊!!! 转载于:h ...

  2. go redis 清空所有值_【大厂面试】面试官看了赞不绝口的Redis笔记二

    时间复杂度: O(1) 将一个或多个值 value 插入到列表 key 的表头.如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执 ...

  3. 复习支持向量机(SVM)没空看书时,掌握下面的知识就够了

    支持向量机(support vector machines, SVM)是一种二类分类模型.它的基本模型是定义在特征空间上的间隔最大的线性分类器:支持向量机还包括核技巧,这使它成为实质上的非线性分类器. ...

  4. 白帽子讲Web安全(对看书之后的一点笔记)

    第一章.世界观安全 1.网络安全 最先研究计算机系统和网络安全的人称之为"Hacker",人们习惯于称他们为黑客.字面意思的理解就是专门破坏规则,寻找一些系统漏洞,以获得某些权力. ...

  5. 关于《指针的艺术》看书时所遇到的问题

    2011年1月5号 指针与结构体 struct person { char *name; // 姓名 char address[30]; // 家庭地址 int height; // 身高 struc ...

  6. 看视频时,类加载器没太理解,现在再整理下几个要点

    类加载器负责把类加载到java虚拟机即JVM中,指定类的名称,类加载器就会定位这个类的定义,每一个java类必须由类加载器加载. 当启动JVM的时候,我们可以使用三个类加载器:引导类加载器.扩展类加载 ...

  7. 看完了这篇实时数仓建设,才发现以前的都白看了(内有美团案例)

    看来大家还是对数据仓库和大数据平台比较感兴趣,今天就和大家再聊聊怎么做实时数仓吧.实时数仓可谓是决定性的东西,能决定什么?决定你的报表和BI到底能不能实时展现数据. 1.数据仓库的发展趋势 1.1 数 ...

  8. JS-基础笔记二次总结(B站跟过黑马的同学看过来)

    这里都是基于我在B站跟过的黑马教学视频上的PPT进行二次总结的笔记,结合了自己平时的加注和记录.欢迎跟过黑马视频的小伙伴们一起来复习Pink老师授予我们的快乐知识,在此也非常感谢黑马机构,让我有系统的 ...

  9. 看书学习感悟系列(三)

    前言: 大家周末好,我是txp,一个正点搞技术.爱学习.爱生活的同学!今天给大家带来是本月的看书学习感悟系列,在之前我已经分享过两次这个系列,这个系列主题会参插技术和非技术部分在里面,非技术部分包括理 ...

最新文章

  1. 美国计算机专业录取率,美国留学高录取率院校计算机专业申请条件是什么? 爱问知识人...
  2. 车主无忧:天下武功,唯快不破,神策让我们快人一步
  3. Docker使用中遇到的问题
  4. 块分割,维特比算法小结
  5. 瑟瑟发抖!拍照比“剪刀手”竟会泄露指纹信息...
  6. 利用装饰器实现mock和api的局部分离切换
  7. wpf 代码获取contextmenu_wpf – 如何在动态创建的ContextMenu中添加水平分隔符?
  8. 洛谷 P1113 杂务
  9. Java对接微信公众号模板消息
  10. Q1财报蛋壳公寓再亏损:盈利死穴如何破?
  11. 倾斜模型精细化处理_浅谈几个倾斜摄影三维模型的修补软件 - 纳金网
  12. springboot项目日志记录访问客户端ip地址
  13. Fiddler+手机模拟器+java采集抖音评论数据
  14. 520七夕情人节告白网页代码—浪漫3D相册
  15. 从小白到入门,入行量化必须知道的几点
  16. 【Java】 # java实现坐标转换工具类
  17. ArcGIS 西安80的投影坐标转CGCS2000的投影坐标
  18. CocosCreator优化之DrawCall漫谈
  19. Linux 的 Parted 指令
  20. WBEM简介 (转)

热门文章

  1. Unity3D之UGUI基础5:Toggle复选框
  2. bzoj 3238: [Ahoi2013]差异(后缀数组+单调栈)
  3. 51nod-1785:数据流中的算法
  4. cube一站式云原生机器学习平台-推理服务的工程化加速
  5. python后台架构Django教程——连接读写mysql数据库
  6. Ubuntu16.4配置caffe详细流程
  7. 兰州交通大学计算机科学与技术专业排名,专业排行
  8. golang中值类型/指针类型的变量区别总结
  9. 圣诞节的整理前两周的内容4
  10. Python 解决面试题47 不用加减乘除做加法