In the previous article, we introduced non-mfc dll and the MFC rule DLL. Now we will analyze in detail the last type of DLL-MFC extension DLL.
Introduction 6.1
The similarities between the MFC extension DLL and the MFC rule DLL are that the MFC class library can be used inside the two DLL types. The difference between the MFC extension DLL and the ApplicationProgram. MFC extension DLL refers to the extension of MFC. Its main function is to generate reusable classes from existing MFC library classes. The MFC extension DLL uses the MFC Dynamic Link Library version. Therefore, only the MFC executable files (application programs or rule DLL) generated using the shared MFC version can use the MFC extension DLL.
As mentioned above, the MFC rule DLL is automatically added to a cwinapp object by the MFC wizard, while the MFC extension DLL does not include this object, but is automatically added to the dllmain function. For MFC extension DLL, developers must add initialization and termination in the DLL dllmain function.Code.
From the following table, we can see that the three DLL methods are different for the dllmain entry function:
DLL type |
Entry Function |
Non-MFC DLL |
Programmers provide the dllmain Function |
MFC rule DLL |
Initinstance and exitinstance of the cwinapp object |
MFC extension DLL |
The mfc dll wizard generates the dllmain function. |
for the MFC extension DLL, the system automatically adds the macros shown in the following table to the project. These macros provide convenience for compiling DLL and applications. Macros such as afx_ext_class, afx_ext_api, and afx_ext_data have different definitions in DLL and application, depending on whether the _ afxext macro is defined. This allows the use of a unified macro In the DLL and Application to express the different meanings of output and input. In DLL, it indicates the output (because _ afxext is defined, it is usually specified in the identifier parameter of the compiler); in the application, it indicates the input (_ afxext is not defined ).
macro |
definition |
afx_class_import |
__ declspec (dllexport) |
afx_api_import |
__ declspec (dllexport) |
afx_data_import |
__ declspec (dllexport) |
afx_class_export |
__ declspec (dllexport) |
afx_api_export |
__ declspec (dllexport) |
afx_data_export |
__ declspec (dllexport) |
afx_ext_class |
# ifdef _ afxext afx_class_export # else afx_class_import |
afx_ext_api |
# ifdef _ afxext afx_api_export # else afx_api_import |
afx_ext_data |
# ifdef _ afxext afx_data_export # else afx_data_import |
6.2 export an MFC derived class using MFC extension DLL
In this example, we will generate an MFC extension DLL project named "extdll", and export a dialog box class in this DLL, which is derived from the MFC class cdialog.
When you use the MFC Wizard to generate an MFC extension DLL, the system automatically adds the following code:
static afx_extension_module extdlldll = {null, null}; extern "C" int apientry dllmain (hinstance, DWORD dwreason, lpvoid lpreserved) {< br> // remove this if you use lpreserved unreferenced_parameter (lpreserved ); // Description: lpreserved is a parameter reserved by the system. For implicit links, it is a non-zero value, the explicit link value is zero If (dwreason = dll_process_attach) {< br> trace0 ("extdll. DLL initializing! \ N "); // extension DLL one-time initialization If (! Afxinitextensionmodule (extdlldll, hinstance) return 0; // insert this DLL into the resource chain New cdynlinklibrary (extdlldll ); }< br> else if (dwreason = dll_process_detach) {< br> trace0 ("extdll. DLL terminating! \ N "); // terminate the library before Destructors are called afxtermextensionmodule (extdlldll); }< br> return 1; // OK } |
This code is obscure and we need to interpret it:
(1) The above code completes the initialization and termination of the MFC extension DLL;
(2) The cdynlinklibrary object created during initialization enables the MFC extension DLL to export the cruntimeclass object or resources in the DLL to the application;
(3) The cruntimeclass structure of the afxinitextensionmodule function capture module and the object Factory (coleobjectfactory object) used when the cdynlinklibrary object is created );
(4) The afxtermextensionmodule function enables MFC to clear the extension DLL when each process is separated from the extension DLL (when the process exits or when afxfreelibrary is used to uninstall the DLL;
(5) The first statement static afx_extension_module extdlldll = {null, null} defines a static Global Object of the afx_extension_module class. The definition of afx_extension_module is as follows:
Struct afx_extension_module { Bool binitialized; Hmodule; Hmodule hresource; Cruntimeclass * pfirstsharedclass; Coleobjectfactory * pfirstsharedfactory; }; |
With the definition of afx_extension_module, we can better understand (2), (3), and (4) points.
Add a dialog box 15 in the resource editor and use the MFC class Wizard to add a corresponding cextdialog class. The system automatically adds the extdialog. h and extdialog. cpp header files.
Figure 15 dialog box in MFC extension DLL |
Modify the cextdialog class declaration in extdialog. h:
Class afx_ext_class cextdialog: Public cdialog { Public: Cextdialog (cwnd * pparent = NULL ); Enum {IDD = idd_dll_dialog }; Protected: Virtual void dodataexchange (cdataexchange * PDX ); Declare_message_map () }; |
The major change is that we add the "afx_ext_class" macro in the class afx_ext_class cextdialog statement to export the cextdialog class in the DLL.
6.3 loading of MFC extension DLL
6.3.1 implicit Loading
We add a loadextdlldlg project in the workspace where project 6.2 is located to demonstrate how to load the MFC extension DLL. Add a dialog box shown in 16 to the loadextdlldlg Project, which contains a "Call DLL" button.
Figure 16 dialog box in the MFC extension DLL call Project |
In the dialog box corresponding to Figure 16, add the class implementation file header:
// Loadextdlldlg. cpp: implementation file //# Include ".. \ extdialog. H" # Pragma comment (Lib, "extdll. lib ") The message processing function of the Click Event of the "Call DLL" button is: Void cloadextdlldlg: ondllcallbutton () { Cextdialog extdialog; Extdialog. domodal (); } |
When we click "Call DLL", the dialog box 15 is displayed.
To provide users with implicit loading (the MFC extension DLL is generally implicitly loaded for specific reasons, see the following section), the MFC extension DLL must provide three files:
(1) describes the header file of the extension class in DLL;
(2) The. Lib file corresponding to the dynamic link library;
(3) dynamically link the library. dll file itself.
With these three files, application developers can make full use of the MFC extension DLL.
6.3.2 display Loading
Show that the MFC extension DLL should be loaded using the global function afxloadlibrary rather than the loadlibrary in Win32 API. Afxloadlibrary finally calls the loadlibrary API, but the thread synchronization is performed before the call.
The function prototype of afxloadlibrary is exactly the same as that of loadlibrary:
Hinstance afxapi afxloadlibrary (lpctstr lpszmodulename ); |
Correspondingly, the MFC application should use afxfreelibrary instead of freelibrary to uninstall the MFC extension DLL. The function prototype of afxfreelibrary is exactly the same as that of freelibrary, which is:
Bool afxapi afxfreelibrary (hinstance hinstlib ); |
If we change the "Call DLL" button in the above example and click the event message processing function:
Void cloadextdlldlg: ondllcallbutton () { Hinstance hdll = afxloadlibrary ("extdll. dll "); If (null = hdll) { Afxmessagebox ("dynamic loading of MFC extension DLL failed "); Return; }Cextdialog extdialog; Extdialog. domodal (); Afxfreelibrary (hdll ); } |
The Link error occurs in the project:
Loadextdlldlg. OBJ: Error lnk2001: unresolved external symbol "_ declspec (dllimport) Public: Virtual _ thiscall cextdialog ::~ Cextdialog (void) "(_ imp _?? 1cextdialog UAE @ xz) Loadextdlldlg. OBJ: Error lnk2001: unresolved external symbol "_ declspec (dllimport) Public: _ thiscall cextdialog: cextdialog (class cwnd *)" (_ imp _?? 0cextdialog Qae @ pavcwnd @ Z) |
The error message "cextdialog constructor and destructor cannot be found! Yes. For the MFC extension DLL derived from the MFC class, when we want to use the derived class defined in the DLL in the application, we should not use the dynamic DLL loading method.
6.4 MFC extension DLL loading MFC extension DLL
We can use the MFC extension DLL again in the MFC extension DLL. However, the definition of the afx_ext_class, afx_ext_api, and afx_ext_data macro in the two DLL is output, which causes problems during the call.
We will see the Link error in the DLL that calls the MFC extension DLL:
Error lnk2001: unresolved external symbol .......... |
Therefore, in the MFC extension DLL that calls the MFC extension DLL, You need to temporarily redefine the afx_ext_class value before including the header file of the called DLL. The following example shows how to implement it:
// Temporarily change the macro meaning. "output" is "input" # UNDEF afx_ext_class # UNDEF afx_ext_api # UNDEF afx_ext_data # Define afx_ext_class afx_class_import # Define afx_ext_api afx_api_import # Define afx_ext_data afx_data_import // Contains the header file of the called MFC extension DLL # Include "calleddll. H" // Restore macro meaning is output # UNDEF afx_ext_class # UNDEF afx_ext_api # UNDEF afx_ext_data # Define afx_ext_class afx_class_export # Define afx_ext_api afx_api_export # Define afx_ext_data afx_data_export |
6.5 MFC extension DLL export functions and variables
The methods for exporting functions and variables using MFC extension DLL are also very simple. Below is a simple example.
We add the Gobal. h and Global. cpp files to the MFC extension DLL project generated by the MFC Wizard:
// Global. h: MFC extension DLL export variable and function declaration Extern "C" { Int afx_ext_data total; // export the variable Int afx_ext_api add (int x, int y); // export the Function } // Global. cpp: MFC extension DLL export variables and function definitions # Include "stdafx. H" # Include "Global. H" Extern "C" int total; Int add (int x, int y) { Total = x + y; Return total; } |
Compile a simple console program to call this MFC extension DLL:
# Include <iostream. h> # Include <afxver _. h>// Afx_ext_data and afx_ext_api macro are defined in the afxver _. h header file. # Pragma comment (Lib, "extdll. lib ") # Include "... \ global. H" Int main (INT argc, char * argv []) { Cout <add (2, 3) <Endl; Cout <total; Return 0; } |
Run the program. On the console, you can see:
5
5
In addition, when you create an MFC extension DLL under Visual C ++, the mfc dll wizard automatically generates the. Def file. Therefore, apart from using the afx_ext_data and afx_ext_api macros to export functions and variables, defining the export in the. Def file is also a good way. In comparison, exporting classes in the. Def file is troublesome. Generally, you need to obtain all the member functions of the class from the. map file generated by the project, the identifiers changed by the C ++ compiler, and export these "strange" identifiers in The. Def file. Therefore, the MFC extension DLL uses the afx_ext_class macro to directly declare the export class.
6.6 application of MFC extension DLL
The examples of the MFC extension DLL mentioned in the above sections are only to illustrate some problems and do not actually reflect the connotation of the "MFC extension, for example, the Class 6.2 derived from cdialog does not have stronger functions than cdialog. The actual connotation of the MFC extension DLL is that although the class provided by it is derived from the MFC class, it provides more powerful functions and richer interfaces than the MFC class. Let's take a look at a specific example (Click here to download this project ).
We know that the cstatic class corresponding to the static control does not have the interface for setting the background and text color, which makes it impossible for us to flexibly modify the Color Style of the static control in the dialog box or other user interfaces, therefore, we need a cstatic derived class cmulticolorstatic that provides the setbackcolor and settextcolor interfaces.
The declaration of this class is as follows:
Class afx_ext_class cmulticolorstatic: Public cstatic { // ConstructionPublic: Cmulticolorstatic (); Virtual ~ Cmulticolorstatic (); // Attributes Protected: Cstring m_strcaption; Colorref m_backcolor; Colorref m_textcolor; // Operations Public: Void settextcolor (colorref textcolor ); Void setbackcolor (colorref backcolor ); Void setcaption (cstring strcaption ); // Generated message map Functions Protected: Afx_msg void onpaint (); Declare_message_map () }; |
In the implementation file of this class, we need to provide the wm_paint message processing function for it (this is because the color settings depend on the wm_paint message ):
Begin_message_map (cmulticolorstatic, cstatic) // {Afx_msg_map (cmulticolorstatic) On_wm_paint () // defines the wm_paint message processing function for this class //} Afx_msg_map End_message_map () |
The following are important member functions in this class:
// Add the "set text color" interface to the cmulticolorstatic class void cmulticolorstatic: settextcolor (colorref textcolor) {< br> m_textcolor = textcolor; // set the text color } // Add the "Set background color" interface for the cmulticolorstatic class void cmulticolorstatic :: setbackcolor (colorref backcolor) {< br> m_backcolor = backcolor; // set the background color } // Add the "set title" interface for the cmulticolorstatic class void cmulticolorstatic :: setcaption (cstring strcaption) {< br> m_strcaption = strcaption; } // redraw static, the color and title settings depend on this function void cmulticolorstatic: onpaint () {< br> cpaintdc (this ); // device context for painting crect rect; getclientrect (& rect); DC. setbkcolor (m_backcolor); DC. setbkmode (transparent); cfont * pfont = getparent ()-> getfont (); // obtain the font of the parent form. cfont * poldfont; poldfont = dc. selectObject (pfont); // select the font of the parent form DC. settextcolor (m_textcolor); // set the text color DC. drawtext (m_strcaption, & rect, dt_center); // The text is in the center of static DC. selectObject (poldfont); } |
To verify the cmulticolorstatic class, we create a dialog box-based application that contains a dialog box 17. The dialog box contains a static control and three buttons. The three buttons can respectively set the static control to "red", "blue", and "green ".
Figure 17 demonstration of extended cstatic class calls |
The following describes how to compile the class corresponding to this dialog box.
The Declaration of the dialog box class containing such static is as follows:
# include ".. \ multicolorstatic. H " # pragma comment (Lib," colorstatic. lib ") // ccalldlldlg dialog class ccalldlldlg: Public cdialog {< br> public: ccalldlldlg (cwnd * pparent = NULL); // standard constructor Enum {IDD = idd_calldll_dialog}; cmulticolorstatic m_colorstatic; // include a cmulticolorstatic instance protected: virtual void dodataexchange (cdataexchange * PDX); // DDX/DDV support hicon m_hicon; // generated message map functions // {afx_msg (ccalldlldlg) virtual bool oninitdialog (); afx_msg void onsyscommand (uint NID, lparam); afx_msg void onpaint (); afx_msg hcursor onquerydragicon (); afx_msg void onredbutton (); afx_msg void onbluebutton (); afx_msg void ongreenbutton (); //} afx_msg declare_message_map () }; |
The main member functions related to cmulticolorstatic in this class are as follows:
Void ccalldlldlg: dodataexchange (cdataexchange * PDX) { Cdialog: dodataexchange (PDX ); // {Afx_data_map (ccalldlldlg) Ddx_control (PDX, idc_color_static, m_colorstatic ); // Associate m_colorstatic with the idc_color_static Control //} Afx_data_map } Bool ccalldlldlg: oninitdialog () { ... // Todo: add extra initialization here // Display the initial static control M_colorstatic.setcaption ("Black at first "); M_colorstatic.settextcolor (RGB (0, 0, 0 )); Return true; // return true unless you set the focus to a control } // Set the text color of the static control to red. Void ccalldlldlg: onredbutton () { M_colorstatic.setcaption ("changed to red "); M_colorstatic.settextcolor (RGB (255, 0, 0 )); Invalidate (true); // cause wm_paint to be sent } // Set the text color of the static control to blue. Void ccalldlldlg: onbluebutton () { M_colorstatic.setcaption ("changed to Blue "); M_colorstatic.settextcolor (RGB (0, 0,255 )); Invalidate (true); // cause wm_paint to be sent } // Set the text color of the static control to green. Void ccalldlldlg: ongreenbutton () { M_colorstatic.setcaption ("changed to green "); M_colorstatic.settextcolor (RGB (0,255, 0 )); Invalidate (true); // cause wm_paint to be sent } |
Now, we have explained all types of dynamic link libraries, that is, non-mfc dll, MFC rule DLL, and MFC extension DLL. In the next section, we will provide three DLL engineering instances to share with readers the application scope and usage of the DLL.