Learn how to create an MFC framework

Source: Internet
Author: User
Tags protected constructor

I haven't used MFC for a long time, so I forget the complicated process of creating windows, documents, and views of the MFC framework.
Next we will track an mfc mdi application for further study or study.
 
Use Appwizard to create an MDI application. The application I created is called mditest. In this way, MFC generates the following classes:
Class Name Function
Cmditestapp is derived from the cwinapp application class.
Cmainframe is derived from the MDI frame window class of cmdiframewnd.
Cmditestdoc is derived from the cdocument document class.
Cchildframe is derived from the MDI subwindow class of cmdichildwnd.
Cmditestview is derived from the cview document display class.
 
The window relationships between cmainframe, cchildframe, and inititestview are shown in the following table:
Cmainframe
(Menu, toolbar ...)
Mdiclient
Cchildframe
Cmditestview
Pdocument = * inititestdoc (pointer with document) [statusbar]
The top-Layer window cmainframe contains an mdiclient window. Cchildframe is included in mdiclient as a sub-window (multiple objects can be included). In cchildframe, it is a real document indicating that the window contains itestview.
 
Let's start from here:
// Initialize itestapp
Bool effecitestapp: initinstance ()
 
As a cwinapp derived class, you usually need to reload the initinstance () and exitinstance () functions to initialize and exit the application. We are concerned about the document template and window Processing Section in initinstance, and ignore some commoncontrol and OLE initialization sections.
 
The entire initinstance code is as follows:
Bool effecitestapp: initinstance ()
{
Initcommoncontrols (); // a large number of comments and error handling are deleted here.
Cwinapp: initinstance ();
Afxoleinit ();
Afxenablecontrolcontainer ();
Setregistrykey (_ T ("Local Application generated by the Application Wizard "));
Loadstdprofilesettings (4); // Load Standard INI File options (including MRU)
Trace ("before cmultidoctemplate/N ");
// Register the application's document template. Document Template
// Will be used as a link between the document, framework window, and view
Cmultidoctemplate * pdoctemplate;
Cdoctemplate = new cmultidoctemplate (idr_mditesttype,
Runtime_class (inititestdoc ),
Runtime_class (cchildframe), // customize the MDI sub-framework
Runtime_class (effecitestview ));
If (! Pdoctemplate)
Return false;
Trace ("before adddoctemplate/N ");
Adddoctemplate (pdoctemplate );
// Create the primary MDI frame window
Trace ("before new cmainframe/N ");
Cmainframe * pmainframe = new cmainframe;
Trace ("before pmainframe-> loadframe/N ");
If (! Pmainframe |! Pmainframe-> loadframe (idr_mainframe ))
Return false;
M_pmainwnd = pmainframe;
Trace ("before parsecommandline/N ");
Ccommandlineinfo using info;
Parsecommandline (partition info );
 
// Schedule the command specified in the command line. If
// Use/regserver,/register,/unregserver, or/unregister to start the application, false is returned.
Trace ("before processshellcommand/N ");
If (! Processshellcommand (cmdinfo ))
Return false;
Trace ("before pmainframe-> showwindow/N ");
// The main window is initialized, so it is displayed and updated.
Pmainframe-> showwindow (m_ncmdshow );
Trace ("before pmainframe-> updatewindow/N ");
Pmainframe-> updatewindow ();
Return true;
}
To study the entire creation process, I added some traces to track the creation sequence.
Ignore the messy initialization at the beginning, starting with cmultidoctemplate:
Cmultidoctemplate * pdoctemplate = new cmultidoctemplate (idr_mditesttype,
Runtime_class (inititestdoc ),
Runtime_class (cchildframe), // customize the MDI sub-framework
Runtime_class (effecitestview ));
Adddoctemplate (pdoctemplate );
(Simplified a little)
Here we first create a cmultidoctemplate-document template, which contains three runtime class information: Document-inititestdoc, framewnd-cchildframe, and view-inititestview.
Then, use the adddoctemplate function to add the newly created document template to the template Manager (we will study the template manager later ).
Then create the main frame window cmainframe:
Cmainframe * pmainframe = new cmainframe;
If (! Pmainframe |! Pmainframe-> loadframe (idr_mainframe ))
Return false;
Among them, we need to study the implementation of loadframe and what is done in it. We will study it later.
Process the command line. Here the first empty document is created:
Ccommandlineinfo using info;
Parsecommandline (partition info );
// Schedule the command specified in the command line. If you start an application with/regserver,/register,/unregserver, or/unregister, false is returned.
If (! Processshellcommand (cmdinfo) // The initial empty document is created here.
Return false;
We will focus on processshellcommand later.
Finally, the main window is displayed:
Pmainframe-> showwindow (m_ncmdshow );
Pmainframe-> updatewindow ();
So far, winapp: initinstance () has completed its work.
There are three branches left over for research. Let's study them now:
1. cdoctemplate
2. cframewnd: loadframe
3. cwnd: processshellcommand
 
Study cdoctemplate
In our example, A cmultidoctemplate is constructed, which is derived from cdoctemplate, So we mainly study cdoctemplate.
The key attributes of cdoctemplate are listed as follows:
Cruntimeclass * m_pdocclass; // class for creating new documents ents
Cruntimeclass * m_pframeclass; // class for creating new frames
Cruntimeclass * m_pviewclass; // class for creating new views
Where:
M_pdocclass indicates the document class type. In this example, It is cmditestdoc.
M_pframeclass indicates the frame window class that holds the view window. In this example, It is cchildframe.
M_pviewclass indicates the View class type of the document to be displayed. In this example, It is initestview.
 
We can think that cdoctemplate is used to describe the relationship between frame-View-Doc. Of course, it has a lot of other attributes. We will ignore them for the moment.
 
We will also see the process of creating documents, frameworks, and views of cdoctemplate in processshellcommand.

Study loadframe
Let's continue to study how cframewnd: loadframe works. The method used is to track the access.
Bool cmdiframewnd: loadframe (uint nidresource, DWORD dwdefaultstyle,
Cwnd * pparentwnd, ccreatecontext * pcontext)
{
// Call the loadframe of the base class cframewnd, pcontext = NULL/pparentwnd = NULL when creating the Main Window
If (! Cframewnd: loadframe (nidresource, dwdefaultstyle, pparentwnd, pcontext ))
Return false;
// Save menu to use when no active MDI child window is present
Assert (m_hwnd! = NULL );
// The main window has a menu, so...
M_hmenudefault =: getmenu (m_hwnd );
If (m_hmenudefault = NULL)
Trace (traceappmsg, 0, "Warning: cmdiframewnd without a default menu./N ");
Return true;
}
Note that the main window cmainframe of our mditest application is derived from cmdiframewnd, so go here and refer to the red comments section in the code. Continue tracing to enter cframewnd: loadframe.
 
Bool cframewnd: loadframe (uint nidresource, DWORD dwdefaultstyle,
Cwnd * pparentwnd, ccreatecontext * pcontext)
{
// Only do this once
Assert_valid_idr (nidresource); // nidresource = 128, idr_mainframe
Assert (m_nidhelp = 0 | m_nidhelp = nidresource );
 
M_nidhelp = nidresource; // ID for help context (+ hid_base_resource)
 
Cstring strfullstring;
If (strfullstring. loadstring (nidresource) // = "mditest"
Afxextractsubstring (m_strtitle, strfullstring, 0); // obtain the first substring
 
Verify (afxdeferregisterclass (afx_wndframeorview_reg ));
// Attempt to create the window
// Geticonwndclass calls the virtual precreatewindow function and calls it elsewhere.
// The precreatewindow of the subclass will be called multiple times.
Lpctstr lpszclass = geticonwndclass (dwdefaultstyle, nidresource );
String strtitle = m_strtitle;
// Call cframewnd: Create () to create a window.
// Note: multiple messages such as wm_create will be sent to cmainframe. The
// Oncreate processing.
If (! Create (lpszclass, strtitle, dwdefaultstyle, rectdefault,
Pparentwnd, makeintresource (nidresource), 0l, pcontext ))
{
Return false; // will self destruct on failure normally
}
 
// Save the default menu handle, as if cmdiframewnd is also saved once?
Assert (m_hwnd! = NULL );
M_hmenudefault =: getmenu (m_hwnd );
 
// Load accelerator Resource
Loadacceltable (makeintresource (nidresource ));
 
// Wm_initialupdate is a message invented by MFC. For more information, see the following description.
If (pcontext = NULL) // send initial update
Sendmessagetodescendants (wm_initialupdate, 0, 0, true, true );
Return true;
}
 
The following are some descriptions extracted from tn024: MFC-defined messages and resources:
Wm_initialupdate
This message is sent by the document template to all descendants of a frame window when it is safe for them to do their initial update. it maps to a call to cview: oninitialupdate but can be used in other cwnd-Derived classes for other one-shot updating.
Wparamnot used (0) lparamnot used (0) returnsnot used (0)
 
To sum up, loadframe does the following:
1. register the window class (afxdeferregisterclass)
2. Create window)
3. Process menus and shortcuts, and send the wm_initialupdate message to all subwindows. The message is actually processed in cview. (For example, if you place a formview on the toolbar, you may receive and process the message)
 
Now, cmainframe has been created, the menu has been loaded, and the toolbar and status line have been created in cmainframe: oncreate. Let's study how the first subwindow was created. This process is not so straightforward compared with cmainframe: loadframe.
 
 
Research on cwnd: processshellcommand
 
The first MDI sub-window is built from here, which is really not intuitive. However, this is the case with MFC.
Bool cwinapp: processshellcommand (ccommandlineinfo & r0000info)
{
Bool bresult = true;
Switch (rcmdinfo. m_nshellcommand)
{
Case ccommandlineinfo: filenew:
If (! Afxgetapp ()-> on1_msg (id_file_new, 0, null, null) // The Key is here
Onfilenew ();
If (m_pmainwnd = NULL)
Bresult = false;
Break;
Case ccommandlineinfo: fileopen: // ignore
Case ccommandlineinfo: fileprintto: // ignore
Case ccommandlineinfo: fileprint:
Case ccommandlineinfo: filedde:
Case ccommandlineinfo: appregister:
Case ccommandlineinfo: appunregister:
}
Return bresult;
}
Go to processshellcommand. To process many different commands, ignore other commands and read the filenew part separately.
Note: it actually enters afxgetapp ()-> on1_msg (id_file_new, 0, null, null.
 
Afxgetapp () actually returns the unique instance of the receivitestapp, which is derived from cwinapp-cwinthread-csf-target-cobject. We didn't reload on1_msg, so we went to the on1_msg processing of c1_target. For research, we cut down some code.
Bool c0000target: on0000msg (uint NID, int ncode, void * pextra,
Afx_cmdhandlerinfo * phandlerinfo)
{
// Some code is deleted here
// Determine the message number and code (packed into ncode)
Const afx_msgmap * pmessagemap;
Const afx_msgmap_entry * lpentry;
Int nmsg = 0;
// Some code is deleted here. After processing, nmsg = wm_command
// Some assertions are removed to simplify the process. The following cycle is used to find the entry for processing this message.
For (pmessagemap = getmessagemap (); pmessagemap-> pfngetbasemap! = NULL;
Pmessagemap = (* pmessagemap-> pfngetbasemap )())
{
Lpentry = afxfindmessageentry (pmessagemap-> lpentries, nmsg, ncode, NID );
If (lpentry! = NULL)
{
// Locate the message processing entry and distribute the message.
Return _ afxdispatch1_msg (this, NID, ncode,
Lpentry-> PFN, pextra, lpentry-> nsig, phandlerinfo );
}
}
Return false; // if not found, no processing is performed.
}
Finally, MFC happily found an entry item, cwinapp: onfilenew (void) to process the message. Continue to the _ afxdispatch1_msg directory.
 
Afx_static bool afxapi _ afxdispatchshortmsg (csf-target * pTARGET, uint NID, int ncode, afx_pmsg PFN, void * pextra, uint_ptr nsig, afx_cmdhandlerinfo * phandlerinfo)
// Return true to stop Routing
{
Union messagemapfunctions MMF;
MMF. PFN = PFN;
Bool bresult = true; // default is OK
If (phandlerinfo! = NULL)
{
// Just fill in the information, don't do it
Phandlerinfo-> pTARGET = pTARGET;
Phandlerinfo-> PMF = MMF. PFN;
Return true;
}
Switch (nsig)
{
Case afxsig1__v:
// Normal command or control notification
Assert (cn_command = 0); // cn_command same as bn_clicked
Assert (pextra = NULL );
PTARGET-> * MMF. pfn1__v_v )();//? Actually, this member function pointed to by pTARGET is called.
Break;
// There are a large number of afxsigcmd_xxx below, which are ignored.
Default: // illegal
Assert (false); Return 0; break;
}
Return bresult;
}
 
(PTARGET-> * MMF. pfn_1__v_v) () calls cwinapp: onfilenew (), and pTARGET = inititestapp class instances. The call is as follows:
 
Void cwinapp: onfilenew ()
{
If (m_pdocmanager! = NULL)
M_pdocmanager-> onfilenew ();
}
 
Enter cdocmanager: onfilenew ()
 
Void cdocmanager: onfilenew ()
{
If (m_templatelist.isempty ())
// A message indicating that no template exists is returned.
Cdoctemplate * ptemplate = (cdoctemplate *) m_templatelist.gethead (); // The first
If (m_templatelist.getcount ()> 1)
// A dialog box (ugly) is displayed, prompting you to select a document template.
// In this example, the ptemplate is the one created in the receivitestapp: initinstance ().
Ptemplate-> opendocumentfile (null );
}
 
Before entering cmultidoctemplate: opendocumentfile, I observed the call stack. The results are as follows:
Mfc71d. dll! Cdocmanager: onfilenew () line 852c ++
Mfc71d. dll! Cwinapp: onfilenew () line 25c ++
Mfc71d. dll! _ Afxdispatch1_msg (csf-target * pTARGET = 0x0042cae8, unsigned int nid = 57600, int ncode = 0, void (void) * PFN = 0x0041153c, void * pextra = 0x00000000, unsigned int nsig = 53, afx_cmdhandlerinfo * phandlerinfo = 0x00000000) Row 89c ++
Mfc71d. dll! Csung target: onsung MSG (unsigned int nid = 57600, int ncode = 0, void * pextra = 0x00000000, afx_cmdhandlerinfo * phandlerinfo = 0x00000000) Row 396 + 0x27c ++
Mfc71d. dll! Cwinapp: processshellcommand (ccommandlineinfo & rcmdinfo = {...}) Row 27 + 0x1ec ++
Mditest.exe! Cmditestapp: initinstance () Row 101 + 0xcc ++
Hope I have not lost my way :)
 
 
Cmultidoctemplate: opendocumentfile is a lot of code. Let's select some.
Cdocument * cmultidoctemplate: opendocumentfile (lpctstr lpszpathname, bool bmakevisible)
{
// The following code deletes the verification and assertion sections
Cdocument * pdocument = createnewdocument (); // create a Document Object
Cframewnd * pframe = createnewframe (pdocument, null); // create a framework window
 
If (lpszpathname = NULL)
{
Cdocument-> onnewdocument (); // initialize the document
}
Else
// Open an existing document
Initialupdateframe (pframe, pdocument, bmakevisible );
Return pdocument;
}
 
Take a look at createnewdocument ()
Cdocument * cdoctemplate: createnewdocument ()
{
// Default implementation constructs one from cruntimeclass
If (m_pdocclass = NULL)
// Error message
// Cruntimeclass * m_pdocclass-> Createobject instantiation document class.
// In this example, it is both initestdoc
Cdocument * pdocument = (cdocument *) m_pdocclass-> Createobject ();
Adddocument (pdocument); // list of documents added to the template. multidoctemplate saves this document.
Return pdocument;
}
 
 
Cmditestdoc has the following definitions, which can only be created from cruntimeclass.
Class program itestdoc: Public cdocument
{
Protected: // create from serialization only
Protected itestdoc (); // protected Constructor
Declare_dyncreate (inititestdoc) // supports creation from cruntimeclass information.
 
 
Then perform createnewframe.
Cframewnd * cdoctemplate: createnewframe (cdocument * pdoc, cframewnd * pother)
{
// Create a frame wired to the specified document
Ccreatecontext context; // This createcontext is passed to loadframe.
Context. m_pcurrentframe = pother; // in this example, = NULL
Context. m_pcurrentdoc = pdoc; // = the document just created
Context. m_pnewviewclass = m_pviewclass; // display the type of the View class in this document
Context. m_pnewdoctemplate = this;
 
If (m_pframeclass = NULL)
// Error prompt and return
// Use the cruntimeclass information to create a framework window object. In this example, It is cchildframe.
Cframewnd * pframe = (cframewnd *) m_pframeclass-> Createobject ();
 
// Here, we can see loadframe again. refer to the previous loadframe.
// The View window is also generated. See trace output.
Pframe-> loadframe (m_nidresource,
Ws_overlappedwindow | fws_addtotitle, null, & context );
Return pframe;
}
 
After loadframe, The View window is created, and then enters cmditestdoc: onnewdocument. Now it is just an empty function with no specific code.
Bool effecitestdoc: onnewdocument ()
{
Trace ("effecitestdoc: onnewdocument () Entry/N ");
If (! Cdocument: onnewdocument ())
Return false;
// Todo: add the re-initialization code here
// (SDI document will reuse this document)
Return true;
}
 
Finally, it is cdoctemplate: initialupdateframe, which is mainly used to activate the newly created framework, documents, and views.
Void cdoctemplate: initialupdateframe (cframewnd * pframe, cdocument * pdoc, bool bmakevisible)
{
// Just delagate to implementation in cframewnd
Pframe-> initialupdateframe (pdoc, bmakevisible );
}
 
Now, all documents, frame windows, and window ports are created, and processshellcommand is returned successfully. The winapp: initinstance:
// The main window is initialized, so it is displayed and updated.
Pmainframe-> showwindow (m_ncmdshow );
Pmainframe-> updatewindow ();
 
 
Let's take a look at the trace output. The intermediate DLL loading is removed:
Before cmultidoctemplate
Before adddoctemplate
Before new cmainframe
Cmainframe: cmainframe ()
Before pmainframe-> loadframe
Cmainframe: precreatewindow entry // Note: precreatewindow is called twice
Cmainframe: precreatewindow entry
Cmainframe: oncreate entry before cmdiframewnd: oncreate
Cmainframe: oncreate before m_wndtoolbar.createex
Cmainframe: oncreate before m_wndstatusbar.create
Before parsecommandline
Before processshellcommand
Cmditestdoc: cmditestdoc () // The document object is created.
Cchildframe: cchildframe () // The child frame window is created.
Cchildframe: precreatewindow entry
Cchildframe: precreatewindow entry
Cchildframe: precreatewindow entry
Inititestview: inititestview () entry // The View window is created in oncreate of the sub-Framework Window
Effecitestview: precreatewindow entry
Cmditestdoc: onnewdocument () Entry
Before pmainframe-> showwindow
Before pmainframe-> updatewindow
 
// Trace at exit
Cmditestview ::~ Cmditestview ()
Cchildframe ::~ Cchildframe ()
Cmditestdoc ::~ Cmditestdoc ()
Cmainframe ::~ Cmainframe ()

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.