Http://www.evget.com/articles/evget_820.html
Use resources in DLL (1)
Yuwei-reproduced (14:02:00 ). net Control Development ActiveX/COM development clx/VCL development Java component development VC/mfc control use Experience Discuss DHTML/script control development basic database programming specification unit test reusable software technical theory it
Use resources in DLL (1)
The most common problem about DLL is how to use the dialog box in DLL. This is a common question about how to use resources in DLL. Here we analyze and solve this problem from the Win32 DLL and mfc dll aspects.
1. Win32 DLL
Using the dialog box in Win32 DLL is very simple. You only need to add the dialog box resources in your DLL, And you can set the required control on the dialog box. Then, use the dialogbox or createdialog functions (or other functions with the same function) to create a dialog box and define your own dialog box callback function to process the messages received in the dialog box. The following is an example of how to use the dialog box in Win32 DLL. You can follow these steps:
1) in the VC menu, choose File> New to create a Win32 dynamic-Link Library project named usedlg. Next, select a simple DLL project.
2) in the VC menu, insert-> resource to add a dialog resource with ID idd_dlg_show. Remove the cancel button on the dialog and retain only the OK button. Add a dialog box with ID idd_aboutbox and its caption is about. Save the resource and name the resource file usedlg. RC. Add Resource. h and usedlg. RC to the project.
3) include resource. h In usedlg. app and add the following code:
Hinstance hinst = NULL;
Hwnd hwnddlg = NULL;
Bool callback dlgproc (hwnd hdlg, uint message,
Wparam, lparam );
Bool callback aboutproc (hwnd hdlg, uint message,
Wparam, lparam );
Extern "C" _ declspec (dllexport) void showdlg ();
Bool apientry dllmain (handle hmodule,
DWORD ul_reason_for_call,
Lpvoid lpreserved
)
{
Switch (ul_reason_for_call)
{
Case dll_process_attach:
Hinst = (hinstance) hmodule;
Case dll_process_detach:
Break;
}
Return true;
}
Extern "C" _ declspec (dllexport) void showdlg ()
{
Hwnddlg = createdialog (hinst, makeintresource (idd_dlg_show ),
Null, (dlgproc );
Showwindow (hwnddlg, sw_show );
}
Bool callback dlgproc (hwnd hdlg, uint message,
Wparam, lparam)
{
Switch (Message)
{
Case wm_initdialog:
Return true;
Case wm_command:
If (loword (wparam) = idok)
Dialogbox (hinst, makeintresource (idd_aboutbox ),
Hdlg, (dlgproc) aboutproc );
Return true;
Case wm_close:
Destroywindow (hdlg );
Hwnddlg = NULL;
Return true;
}
Return false;
}
Bool callback aboutproc (hwnd hdlg, uint message,
Wparam, lparam)
{
Switch (Message)
{
Case wm_close:
Enddialog (hdlg, null );
Hwnddlg = NULL;
Return true;
}
Return false;
}
4) compile and generate usedlg. dll and usedlg. Lib.
Next, create an application that calls this dll. The procedure is as follows:
1) in the VC menu, choose File> New to create an MFC Appwizard (exe) project named use. Next, select dialog based and click Finish.
2) Add a button on the Main Dialog Box and double-click this button. The add member function dialog box is displayed. Click OK to enter the void cusedlg: onbutton1 () function. Add a function call in the function: showdlg ();.
3) Add the following code next to the # include statement:
Extern "C" _ declspec (dllexport) void showdlg ();
# Pragma comment (Lib, "Debug/usedlg ")
4) copy the usedlg. dll and usedlg. Lib files generated in the preceding usedlg project to the DEBUG directory of the Use Project.
5. compile and generate use.exe.
Run use.exe and click the button1 button. A non-Modal Dialog Box named dialog is displayed. Click the button above to bring up the modal dialog box about. Run successfully.
Let's review the process of using the dialog box in Win32 DLL.
In DLL, we define two dialog box resources: idd_dlg_show and idd_aboutbox, and export the showdlg function. In the showdlg function, use the createdialog function to create the idd_dlg_show non-modal dialog box and specify the callback function dlgproc in the dialog box. In dlgproc, wm_initdialog, wm_command, and wm_close messages are processed to respond to user actions on the dialog box. When processing the button action, use the dialogbox function to create the idd_aboutbox modal dialog box, specify its callback function as aboutproc, and process the corresponding message in aboutproc.
In EXE, we use the implicit link method to call the DLL, and use the showdlg function exported in the DLL to call the dialog box in the DLL.
Using the dialog box in Win32 DLL is so simple. Let's take a look at how to use the dialog box in mfc dll.
2. MFC DLL
The dialog box used in mfc dll is not as simple as that in Win32 DLL, mainly because there is a module state problem in the MFC program, that is, the problem of duplicate resources. (The term module here refers to an executable program, or a DLL (or a group of DLL) whose operations do not depend on the rest of the application but use the shared copy of the MFC Runtime Library ). The mfc dll we created is a typical instance of this module .)
In each module (exe or DLL), there is a global state data. MFC relies on this global state data to distinguish different modules for proper operations. This type of data includes Windows instance handle (used to load resources), pointer to the current cwinapp and cwinthread object of the application, Ole module reference count, maintain mappings between Windows Object handles and corresponding MFC object instances. However, when an application uses multiple modules, the status data of each module is not within the application scope. On the contrary, each module has its own private copy of the MFC status data. The global status data is called the status of the MFC module.
The module status data is contained in the structure and can always be used by a pointer to the structure. When the code is executed into a module, only the module is in the "current" or "valid" status, MFC can correctly distinguish this module and perform the correct operations.
For example, an MFC application can use the following code to load strings from a resource file:
Cstring STR;
Str. loadstring (ids_mystring );
This code is very convenient, but it masks the fact that ids_mystring in this program may not be a unique identifier. A program can load multiple DLL files. Some DLL files may also use the ids_mystring identifier to define a resource. How does MFC know which resource to load? MFC uses the current module status to find the resource handle. If the current module is not the correct module we want to use, it will produce incorrect calls or errors.
According to the Link Method of the MFC library, an mfc dll has two methods to use the MFC Library: static link to the mfc dll and dynamic link to the mfc dll. Next we will follow these two types of mfc dll to introduce how to switch the current module status to use resources correctly in the mfc dll.
1. dll statically linked to MFC
When the rule DLL and the MFC library are statically linked to the MFC Library, the MFC Library cannot be shared at this time, so the MFC always uses the module status of the DLL it is linked. In this way, the status of the management module does not exist. However, the disadvantage of using this method is that the DLL program will become larger and duplicate code will be left in the program. The example below demonstrates this. In this example, follow these steps:
1) in the VC menu, choose File> New to create a project named dllstatic MFC Appwizard. Next, select regular DLL with MFC statically linked.
2) Add a dialog box resource in the project. Its ID is idd_aboutbox. In resource. H, change the idd_aboutbox value to 100.
3) define the following functions in dllstatic. cpp:
Void showdlg ()
{
Cdialog DLG (idd_aboutbox );
DLG. domodal ();
}
4) add a line: showdlg to the exports statement in the dllstatic. Def file to export the showdlg function.
5) compile and generate dllstatic. dll and dllstatic. Lib.
Continue to use the use project in the previous section, copy the previously generated dllstatic. dll and dllstatic. Lib files to the DEBUG directory of the project, and
Extern "C" _ declspec (dllexport) void showdlg ();
# Pragma comment (Lib, "Debug/usedlg ")
The two rows are changed:
Void showdlg ();
# Pragma comment (Lib, "Debug/dllstatic ")
Compile and run use.exe. Click the button to see the modal dialog box in dllstatic.
When the button above the DLL is displayed, the modal dialog box in the DLL is displayed. Note: When you use a rule DLL that is statically linked to the MFC, the status of the management module does not exist.
2. DLL dynamically linked to MFC
Let's take a look at an example before discussing the module status of DLL dynamically linked to MFC. In this example, follow these steps:
1) in the VC menu, choose File> New to create a project named dllshared MFC Appwizard. Next, select regular DLL using shared mfc dll.
2) Add a dialog box resource in the project. Its ID is idd_aboutbox. In resource. H, change the idd_aboutbox value to 100.
3) define the following functions in dllshared. cpp:
Void showdlg ()
{
Cdialog DLG (idd_aboutbox );
DLG. domodal ();
}
4) add a line: showdlg to the exports statement in the dllshared. Def file to export the showdlg function.
5) compile and generate dllshared. dll and dllshared. Lib.
Continue to use the above use project, copy the previously generated dllshared. dll and dllshared. Lib files to the DEBUG directory of the project, and
Extern "C" _ declspec (dllexport) void showdlg ();
# Pragma comment (Lib, "Debug/dllstatic ")
The two rows are changed:
Void showdlg ();
# Pragma comment (Lib, "Debug/dllshared ")
Compile and run use.exe. Click the button. What do you see this time? Right, no error. This is the dialog box about use.exe. Changing the DLL type in the above example to the MFC extension DLL (using shared mfc dll) also causes the same problem.
Why is the above problem? This is because when the MFC shared library is used, by default, MFC uses the resource handle of the main application to load the resource template. Find the corresponding dialog box template. Because the resource ID of the dialog box defined in DLL is the same as the resource ID of the dialog box defined in the main application, MFC displays the dialog box in the main application. If the two are different, MFC considers that the dialog box resource defined in the DLL does not exist, and DLG. domodal returns 0, that is, nothing is displayed.
So how can we solve the above problems? The solution is to switch the module status when appropriate to ensure that the module with the current status is the module we need to use the correct resources. MFC provides the following functions and macros to complete these tasks:
Afxgetstaticmodulestate: This is a function. Its prototype is:
Afx_module_state * afxapi afxgetstaticmodulestate ();
This function constructs the instance pmodulestate of the afx_module_state class on the stack and assigns a value to it to return it. In the constructor of the afx_module_state class, this class obtains the pointer pointing to the current module status and stores it in the member variable. Then, it sets the pmodulestate to the new valid module status. In its destructor, this class restores the pointer stored in its member variables to the status of the previous stored module.
Afx_manage_state: This is a macro. Its prototype is:
Afx_manage_state (afx_module_state * pmodulestate)
This macro is used to set the pmodulestate (pointer to the afx_module_state structure containing the global data of the module, that is, the module State) to the current real-time space (the remainder of the immediate containing scope). When the space containing the macro is removed, the status of the previous valid module is automatically restored.
Afxgetresourcehandle: the prototype of this function is:
Hinstance afxgetresourcehandle ();
This function returns a handle to the module that saves the hinstance type and the resources loaded by the application by default.
Afxsetresourcehandle: the prototype of this function is:
Void afxsetresourcehandle (hinstance hinstresource );
This function sets the module represented by hinstresource as a module with the current status.
By using the above four functions or macros, You can correctly switch the module status in the DLL dynamically linked to the MFC. Next we will introduce how to use the above four functions or macros by modifying the example with the above problem. Let's take a look at the regular DLL using shared mfc dll type:
Add the following statement before the first statement of the showdlg function in step 3 of the preceding example (to ensure that the statement is in the first line of function implementation ):
Afx_manage_state (afxgetstaticmodulestate ());
Then, recompile and generate dllshared. dll and dllshared. Lib, and copy the two files to the DEBUG directory of the Use Project. The dialog box about logging is displayed.
Through the above explanation, I believe you already know the role of this statement. After such a sentence is added to the first line of the showdlg function, the MFC Library automatically switches the status of the current module each time the DLL application is called to use the function, this ensures the correctness of resource reading.
Afx_manage_state (afxgetstaticmodulestate (); automatically switches the status of the current module. You can also manually switch the status of the current module by using afxgetresourcehandle and afxsetresourcehandle. The usage is as follows:
Add the following statement before the first statement of the showdlg function in step 3 of the preceding example (to ensure that the statement is in the first line of function implementation ):
Hinstance save_hinstance = afxgetresourcehandle ();
Afxsetresourcehandle (theapp. m_hinstance );
After the dialog box is successfully called, that is, DLG. domodal ();, add:
Afxsetresourcehandle (save_hinstance );
After entering the showdlg function, this method obtains and saves the handle of the Current Status Module through afxgetresourcehandle. Then obtain the DLL module handle theapp. m_hinstance (of course, you can also use the getmodulehandle function to obtain the DLL module handle), and use the afxsetresourcehandle function to set it to the current state. Finally, after the call dialog box is successful, use the restore afxsetresourcehandle resource handle to restore the current module status.
This is troublesome, but one advantage is that the resource handle can be restored immediately after the resource usage task is completed. The afx_manage_state (afxgetstaticmodulestate (); method can restore the resource handle only after the function space ends. We recommend that you restore the resource handle whenever possible because the executable file must be re-painted with a toolbar. Otherwise, you may encounter many problems. For example, if the user moves the DLL dialog box and the resource handle is still the DLL resource, the program will crash. The best time to restore the handle is when the dialog box responds to the wm_initdialog message, because the template of the dialog box has been read.
For mfc dll of the MFC extension DLL (using shared mfc dll) type, the method for switching the status of the current module is similar to that used by the regular DLL using shared mfc dll type, here, we will not give an example. The difference between the two is as follows:
When afx_manage_state (afxgetstaticmodulestate () is used in the MFC extension DLL, the following error occurs:
Mfcs42d. Lib (dllmodul. OBJ): Error lnk2005: _ prawdllmain already defined in dllextend. OBJ
Mfcs42d. Lib (dllmodul. OBJ): Error lnk2005: _ dllmain @ 12 already defined in dllextend. OBJ
Mfcs42d. Lib (dllmodul. OBJ): Error lnk2005: _ prawdllmain already defined in dllextend. OBJ
Therefore, you must change afx_manage_state (afxgetstaticmodulestate () to afx_manage_state (afxgetappmodulestate () in the MFC extension DLL to switch the current module status correctly.
The methods for using afxgetresourcehandle and afxsetresourcehandle In the MFC extension DLL are the same as those used in the regular DLL using shared mfc dll type mfc dll. In addition, the DLL module handle can be obtained through the hmodule Member of the dlgextentdll structure provided by MFC. That is, use the afxsetresourcehandle (dlgextentdll. hmodule); statement.
Of course, for DLL dynamically linked to MFC, you can also use the afxgetresourcehandle and afxsetresourcehandle functions in the MFC application that calls the DLL to switch the current status module. The handle of the DLL module can be obtained using the getmodulehandle function. I will not go into details here.