Temperature is known, learn how to create the MFC framework

xiaoxiao2021-03-06  82

I haven't used MFC for a long time, so that I have forgotten the complex window of the MFC framework, documentation, and the creation process.

Below we track an MFC MDI app, let's take it or learn.

Create an MDI application using AppWizard, the application I created is Mditest, so that the MFC generates the following classes:

Class names CMDiteStApp derived from the application class of CWINAPP. CMAINFRAME

Deleted in the MDI frame window of CmdiFrameWnd. Cmditestdoc

Delivered in CDocument document class. CChildFrame

Delicate the MDI sub-window class of cmdichildwnd. CmditestView

Delivered in CVIEW document display class.

At runtime, CMAINFRAME, CCHILDFRAME, CMDITESTVIEW window relationships are shown in the table below:

CMAINFRAME

(Menu, Toolbar ...)

Mdiclient

CChildFrame

CmditestView

PDocument = * cmditestdoc (pointer with document)

[Statusbar] where the outermost layer is the top window CMAINFRAME, which contains an MDICLIENT window. CChildFrame is included as a sub-window (can contain multiple), and the CchildFrame is a real document indicating that the window cmditestView is.

We start here:

// cmditestApp initialization

Bool cmditestapp :: initInstance ()

As a derived class for CWINAPP, you usually need to overload the initInstance (), ExitInstance () two functions to complete the initialization and exit of the application. We are now concerned about the part of the document template, window processing in InitInstance, and ignore some of the COMMONCONTROL, OLE initialization section.

The entire initInstance code is as follows:

Bool cmditestapp :: initInstance ()

{

INITCOMMONCONTROLS (); // Clear a large number of annotations and error processing

CWINAPP :: InitInstance ();

Afxoleinit ();

AFXENABLECONTROLCONTAINER ();

SetRegistryKey (_T ("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 connection between documents, frame windows, and views

CMULTIDEMPLATE * PDOCTEMPLATE;

PDOCTemplate = New CMULTIDOCTEMPLATE (IDR_MDITESTTYPE,

Runtime_class (cmditestdoc),

Runtime_class (cchildframe), // Custom MDI subframe

Runtime_class (cmditestView);

IF (! pdoCtemplate)

Return False;

Trace ("Before AddDdoCtemplate / N);

AddDDOCTemplate (pdoctemplate);

// Create the main MDI frame window window

"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 cmdinfo;

Parsecommandline (CMDInfo);

// Scheduted the command specified in the command line. in case

// Return the application with / raserver, / register, / unregserver, or / unregister, returns false.

"Before ProcessShellcommand / N";

IF (! ProcessShellcommand (cmdinfo))

Return False;

"Before PMainframe-> ShowWindow / N");

// The main window is initialized, so it shows it and updates it

PMAINFRAME-> ShowWindow (m_ncmdshow);

Trace ("Before PMainframe-> UpdateWindow / N");

PMAINFRAME-> UPDATEWINDOW ();

Return True;

}

In order to study the entire creation process, I added some trace to track the creation order.

Ignore the beginning of the beginning of the chaos, starting from cmultidoctemplate:

CMULTIDEMPLATE * PDOCTEMPLATE = New CMULTIDOCTEMPLATE (iDR_MditestType,

Runtime_class (cmditestdoc),

Runtime_class (cchildframe), // Custom MDI subframe

Runtime_class (cmditestView);

AddDDOCTemplate (pdoctemplate);

(A little simplified) Here first creates a cMultiDEMPLATE - Document template, the document template includes three runtime information: Document - cmditestdoc, framewnd - cchildframe, view - cmditestView. Then add the newly created document template to the template manager through the AddDDOCTemplate function (we will study the template manager later).

Then create a main frame window CMAINFRAME:

CMAINFRAME * PMAINFRAME = New CMAINFRAME;

IF (! pmaInframe ||! pmaInframe-> loadframe (idR_mainframe))

Return False;

Among them, it is necessary to study the implementation of LoadFrame, and what is done inside. We will study later.

Handling the command line, here the first empty document is created:

CCommandLineInfo cmdinfo;

Parsecommandline (CMDInfo);

// Scheduted the command specified in the command line. Returns false if you use / Regserver, / Register, / UnregServer or / Unregister to launch an application.

IF (! ProcessShellcommand (cmdinfo)) // ß here creates an initial space document

Return False;

We will focus on the research SHELLCOMMAND.

Finally, the main window is displayed:

PMAINFRAME-> ShowWindow; PMAINFRAME-> UpdateWindow ();

At this point, WinApp :: InitInstance () completes its own work.

The branches of three to be studied have left to study them now: 1, cdoctemplate 2, cframeWnd :: LoadFrame 3, CWnd :: ProcessShellcommand

Research CDOCTemplate

Our example is constructed a cMultidEcTemplate, which is derived from CDOCTemplate, so we mainly study CDOCTemplate. Several key properties of CDOCTemplate are as follows:

Cruntimeclass * m_pdocclass; // Class for Creating New Documents

Cruntimeclass * m_pframeclass; // Class for Creating New Frames

Cruntimeclass * m_pviewclass; // Class for Creating New Views

among them:

m_pdocclass represents the document class type, in this example is cmditestdoc m_pframeclass

Indicates the type type type of the frame window that accommodates the VIEW window, this example is CCHildFrame M_PViewClass

Indicates the VIEW visual type type of the display document, this example is cmditestView

We can think that CDOCTemplate is used to describe the relationship between Frame-View-DOC. Of course, it still has a large number of properties, we temporarily ignore it.

I will also see the CDOCTemplate creation document, framework, and the procedure of the process, and study in ProcessShellCommand.

Research LoadFrame

Let us continue to study how CFrameWnd :: LoadFrame is working. The method used is to track entry. . .

Bool cmdiframewnd :: LoadFrame (uint NidResource, DWORD DWDEFAULTSTYLE,

CWnd * pparentWnd, ccreatecontext * pContext)

{

// Call the LoadFrame of the base class cframeWnd, PCONText is creating the main window = NULL

// pParentWnd = NULL

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 our MDiteSt Application's main window CMAINFRAME is derived from cmdiframewnd, so it enters here, refer to the red annotation portion in the code. Continue tracking into cframewnd :: loadFrame.

Bool CFrameWnd :: LoadFrame (uint NidResource, DWord DwdefaultStyle,

CWnd * pparentWnd, ccreateContext * pContext) {

// only do this overce

Assert_Valid_idr (NidResource); // NidResource = 128, IDR_MAINFRAME

AskERT (m_nidhelp == 0 || m_nidhelp == nidResource);

m_nidhelp = nidResource; // id for help context ( HID_BASE_RESOURCE)

CString strfullstring;

IF (strfullstring.loadstring (nidResource)) // = "mditest"

AFXTRACTSUBSTRING (m_strtitle, strfullstring, 0); // get the first substring

Verify (AFXDeferRegisterClass (AFX_WNDFRAMEORVIEW_REG);

// attempt to create the window

// GeticonWndClass will call the Virtual PrecreateWindow function, elsewhere will also be called, thus

/ / Make the subscar precomreateWindow will be called multiple times

LPCTSTR LPSZCLASS = GeticonWndClass (dwdefaultstyle, nidResource);

CString startle = m_STRTITLE;

// Call CFrameWnd :: Create () actually creates a window.

// Note: A number of messages such as WM_CREATE will be sent to CMAINFRAME here. Trigger CMAINFRAME

// oncreate processing, etc.

IF (! Create (Lpszclass, Strtertle, DwdefaultStyle, RectDefault,

PparentWnd, MakeintResource (NidResource), 0L, PCONText))

{

Return False; // Will Self Destruct On Failure Normal

}

// Save The Default Menu Handle, it seems that CMDIFrameWnd has saved once?

Assert (m_hwnd! = Null);

m_hmenudefault = :: getMenu (m_hwnd);

// load accelerator resource

LoadAcceltable (MakeintResource (NidResource);

// WM_InitialUpdate is a message of the MFC invented, see the description thereof.

IF (PContext == Null) // send Initial Update

SendMessageTodescendants (WM_Initialupdate, 0, 0, True, True);

Return True;

}

The following is some 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.wparam

NOT USED (0)

lparam

NOT USED (0)

Returns

NOT USED (0)

Incident, LoadFrame has taken the following: 1. Register a window class (AFXDeferRegisterClass) 2, actually creation window (create) 3, processing menu, shortcut, send a WM_INITIALUPDATE message to all sub-windows. This message will be processed in CView. (For example: Playing a formview on Toolbar, you may receive this message and benefit?)

At this point, CMAINFRAME has been successfully created, the menu is already loaded, the toolbar, and the status line have been created in CMAINFRAME :: OnCreate. Let us then study how the first child window is created, the process and CMAINFRAME :: LoadFrame is not that it is not so straightforward.

Study CWnd :: ProcessShellcommand

The first MDI sub-window is established from this, which is really lacking intuitive. However, MFC is like this, no way.

Bool Cwinapp :: ProcessShellcommand (CCommandlineInfo & rcmdinfo)

{

Bool Bresult = true;

Switch (rcmdinfo.m_nshellcommand)

{

Case CCommandlineInfo :: filenew:

IF (! AfxGetApp () -> oncmdmsg (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 handle many different commands, we ignore other commands, see the FileNew section separately. Note: Actually entered AFXGetApp () -> oncmdmsg (ID_FILE_NEW, 0, NULL, NULL).

AfxGetApp () actually returns the only instance of cmditestApp, which is derived from cwinapp - cwinthread - ccmdtarget - cobject. We didn't overload the oncmdmsg, so we entered the CCMDTARGET on ONCMDMSG processing. For research, we have deleted some code.

Bool ccmdtarget :: ONCMDMSG (uint Nid, int ncode, void * pextra, afx_cmdhandlerinfo * Phandlerinfo)

{

// Here it is deleted some code

// determine the message number and code (Packed INTO NCODE)

Const AFX_MSGMap * PMessageMap;

Const AFX_MSGMAP_ENTRY * LPENTRY;

UINT NMSG = 0;

// Here, some code is deleted, and the NMSG = WM_COMMAND will be processed.

// For simplicity, some assertions are deleted. The following loop is used to find the entry to process this message.

PMessageMap (); pimentbasemap! = null;

PMessageMap = (* pMessagemap-> pfNgetBasemap) ())

{

LpenTry = AFXFindMessagentry (pMessageMap-> LpenTries, NMSG, NCODE, NID);

IF (LpenTry! = NULL)

{

/ / Find the message processing item entry to distribute this message.

Return_AFXDispatchCmdmsg (this, NID, NCODE,

LpenTRY-> PFN, Pextra, LpenTry-> nsig, phandlerinfo;

}

}

Return false; // Did not find it

} The final MFC quickly found an entry, cwinapp :: ONFileNew (void) To process this message. Continue to go to _afxDispatchcmdmsg to see.

AFX_STATIC Bool Afxapi _AFXDispatchCMDMSG (ccmdtarget * 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 AFXSIGCMD_V:

// Normal Command or Control Notification

Assert (cn_command == 0); // CN_COMMAND SAME AS BN_CLICKED

Assert (pextra == null);

(ptarget -> * mmf.pfncmd_v_v) (); // ß actually calls this member function pointing to PTARGET

Break;

// There is also a large number of AFXSIGCMD_XXX below, ignore them.

Default: // illegal

ASSERT (FALSE); RETURN 0; BREAK;

}

Return BRESULT;

}

Among them (PTARGET -> * MMF.PFN_CMD_V_V) () generates calls to cwinapp :: onfilenew (), ptarget = cmditestApp class instance. The call entry is as follows: void cwinapp :: onfilenew ()

{

IF (m_pdocmanager! = NULL)

M_PDOCManager-> onfilenew ();

}

Enter into cdocmanager :: onfilenew ()

Void cdocmanager :: OnfileNew ()

{

IF (m_templatelist.isempty ())

/ / Tip No template and return

CDOCTemplate * ptemplate = (cdoctemplate *) m_templatelist.getHead (); // first

IF (M_TemplateList.getCount ()> 1)

// Pop up a dialog (very ugly) prompt users to select a document template

// In this example, Ptemplate is the template created in cmditestapp :: InitInstance ()

PTEMPLATE-> OpenDocumentFile (NULL);

}

Before entering cmultidockplate :: OpenDocumentfile, I observed the calling stack, the result is as follows:

> Mfc71d.dll! Cdocmanager :: onfilenew () line 852 C

Mfc71d.dll! cwinapp :: onfilenew () line 25 C

mfc71d.dll! _AfxDispatchCmdMsg (CCmdTarget * 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 89 C

Mfc71d.dll! ccmdtarget :: ONCMDMSG (unsigned int NID = 57600, int ncode = 0, void * pextra = 0x00000000, afx_cmdhandlerinfo * phandlerinfo = 0x00000000) line 396 0x27 C

Mfc71d.dll! cwinapp :: ProcessShellcommand (ccommandlineinfo & rcmdinfo = {...}) line 27 0x1e C

MDiteSt.exe! Cmditestapp :: InitInstance () line 101 0xc C

I hope I have not lost :)

CMULTIDOCTEMPLATE :: OpenDocumentfile is a lot of lots, let us choose some.

CDocument * CMULTIDEMPLATE :: OpenDocumentFile (LPCTSTR LPSZPATHNAME,

Bool Bmakevisible)

{

// The following code deletes the verification, assertion part

CDocument * pdocument = createNewDocument (); // Create a document object

CFrameWnd * pframe = createNewFrame (pDocument, null); // Create a frame window if (lpszpathname == null)

{

pDocument-> onnewDocument (); // Initialization Document

}

Else

// Open an existing document

InitialUpdateFrame (pframe, pdocument, bmakevisible);

Return PDocument;

}

Take a look at CreateNewDocument ()

CDocument * cdoctemplate :: CreateNewDocument ()

{

// Default Implementation Constructions One from cruntimeclass

IF (m_pdoccoas == null)

// Error prompt

// cruntimeclass * m_pdocclass -> CreateObject instantiate document class.

// In this example, it is both cmditestdoc.

CDocument * pdocument = (cdocument *) m_pdocclass-> createObject ();

AddDocument (pDocument); // Add to the document list in the template, MultidOcTemplate Save this document

Return PDocument;

}

CMDiteStdoc has the following definitions that can only be created from CRUNTIMECLASS.

Class cmditestdoc: public cdocument

{

protected: // Create only from serialization

CmditestDoc (); // Protected constructor

Declare_DyncReate (cmditestDoc) // supports creation from CRUNTIMECLASS information.

Then, CreateNewFrame is followed.

CframeWnd * CDOCTemplate :: CreateNewFrame (cdocument * pdoc, cframewnd * pother)

{

// Create a frame wire to the specified document

CcreateContext Context; // This createContext is passed to loadframe

Context.m_pcurrentframe = Pother; // = NULL

Context.m_pcurrentdoc = pdoc; // = Document you just created

CONTEXT.M_PNEWVIEWCLASS = m_pviewclass; // Displays the type of view class of this document

Context.m_pnewdoctemplate = this;

IF (m_pframeclass == null)

/ / Tip error and return

// Create a frame window object using CRUNTIMECLASS information, this example is CCHildFrame

CframeWnd * pframe = (cframewnd *) m_pframeclass-> createObject ();

// Here we saw LoadFrame, refer to the frontframe in front

// In this, the View window is also generated. Refer to Trace output.

Pframe-> LoadFrame (m_nidresource,

WS_OVERLAPPEDWINDOW | FWS_ADDTOTILE, // DEFAULT FRAME STYLES

NULL, & context); Return Pframe;

}

LoadFrame After the View window will be created, then go into cmditestdoc :: OnNewDocument, now just an empty function, there is no specific code.

Bool cmditestdoc :: OnNewDocument ()

{

Trace ("cmditestdoc :: OnnewDocument () entry / n");

IF (! cdocument :: onnewdocument ())

Return False;

// Todo: Add a reinitialization code here

// (SDI document will reuse this document)

Return True;

}

Finally, CDOCTemplate :: InitialUpdateFrame, which is mainly to activate the new framework, document, depending on, and look at a headache.

Void cdoctemplate :: InitialUpdateFrame (CFrameWnd * Pframe, CDocument * PDOC,

Bool Bmakevisible)

{

//just delagate to importation in cframewnd

Pframe-> InitialUpdateFrame (PDOC, Bmakevisible);

}

Now, the documentation, frame window, all created, and we win to ProcessShellcommand. Display and update the main window, complete WINAPP :: InitInstance:

// The main window is initialized, so it shows it and updates it

PMAINFRAME-> ShowWindow (m_ncmdshow);

PMAINFRAME-> UPDATEWINDOW ();

Look at this TRACE output, the middle DLL load is removed:

Before CMULTIDOCTEMPLATE

Before adddhemplate

Before New CMAINFRAME

CMAINFRAME :: CMAINFRAME ()

Before pmainframe-> loadingframe

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 () // Document object is created

Cchildframe :: cchildframe () // Subframe window is created

Cchildframe :: PrecreateWindow Entry

Cchildframe :: PrecreateWindow Entry

Cchildframe :: PrecreateWindow Entry

CMDiteStView :: cmditestView () Entry // Create a View window in the onCreate of the subframe window

CmditestView :: PrecreateWindow entry

Cmditstdoc :: OnNewDocument () entry

Before pmainframe-> showwindow

Before pmainframe-> UpdateWindow

// TRACE

CmditestView :: ~ cmditestView ()

CChildframe :: ~ cchildframe ()

Cmditestdoc :: ~ cmditestdoc ()

CMAINFRAME :: ~ cmainframe ()

转载请注明原文地址:https://www.9cbs.com/read-54128.html

New Post(0)