Mfc dll resource module handle switching

Source: Internet
Author: User

When I used to write the mfc dll, I always saw a prompt in the automatically generated code framework. I need to add the prompt at the beginning of each output function.
Afx_manage_state (afxgetstaticmodulestate ()). I have never understood the meaning of this, nor have I done it, and the Code also works.
Well, it seems like a nonsense.

In a recent project, you need to use MFC In the DLL to generate the interface, so that once the resources are placed in different dynamic libraries
When threads are mixed together, things become abnormal and complex, and the previous knowledge of MFC is insufficient and sufficient. The program unexpectedly crashes, the inexplicable assert, and the resources cannot be loaded.
What? Every time, I always try to add afx_manage_state (afxgetstaticmodulestate () at the beginning of each thread,
Or use afxsetresourcehandler () in some places, and then the problem is solved, but I don't quite understand what is going on. I always feel this solution is disturbing.
It seems that the problem will pop up again in the next second.

The day before yesterday, this problem was finally solved to the extreme. It took me several hours to try and it was impossible to succeed, I swear that I will never use MFC again. Just like many movies, things have finally been solved. This time I decided not to forget it. I must understand it clearly.

Here, I encountered a problem: how to make the interface code in the DLL use the DLL resource, and how to load the dialog box with the IE control in the working thread?

I asked my colleagues how they switched DLL resources? Afx_manage_state (afxgetstaticmodulestate () is their answer. It is as simple as Microsoft's recommendation! Let's take a look at what the code has done?

# Define afx_manage_state (p) afx_maintain_state2 _ ctlstate (P );

Afx_maintain_state2: afx_maintain_state2 (afx_module_state * pnewstate)
{
M_pthreadstate = _ afxthreadstate;
M_pprevmodulestate = m_pthreadstate-> m_pmodulestate;
M_pthreadstate-> m_pmodulestate = pnewstate;
}

_ Afxwin_inline afx_maintain_state2 ::~ Afx_maintain_state2 ()
{M_pthreadstate-> m_pmodulestate = m_pprevmodulestate ;}

Originally, it was to define a local object and use its constructor and destructor to switch the state at the function entry and exit. I guess afxgetstaticmodulestate () obtain the DLL state of the current Code.

Sure enough, please see

Static _ afx_dll_module_state afxmodulestate;

Afx_module_state * afxapi afxgetstaticmodulestate ()
{
Afx_module_state * pmodulestate = & afxmodulestate;
Return pmodulestate;
}

Class _ afx_dll_module_state: Public afx_module_state

// Afx_module_state (global data for a module)
Class afx_module_state: Public cnotrackobject
{
...
Cwinapp * m_pcurrentwinapp;
Hinstance m_hcurrentinstancehandle;
Hinstance m_hcurrentresourcehandle;
Lpctstr m_lpszcurrentappname;
Byte m_bdll; // true if module is a DLL, false if it is an EXE

...
Coccmanager * m_poccmanager;
...

We have to say that MFC has piled up a lot of data here, which is very complicated and has a very bad structure.
}

Afxmodulestate is a static member of the DLL. It can naturally be accessed by the code in the same DLL, but when will it be initialized?

Extern "C"
Bool winapi dllmain (hinstance, DWORD dwreason, lpvoid/* lpreserved */)
{
...

Afxwininit (hinstance, null, _ T (""), 0 );
...
}

Bool afxapi afxwininit (hinstance, hinstance hprevinstance,
Lptstr lpcmdline, int ncmdshow)
{
Assert (hprevinstance = NULL );

// Handle critical errors and avoid windows message boxes
Seterrormode (0) |
Sem_failcriticalerrors | sem_noopenfileerrorbox );

// Set resource handles
Afx_module_state * pmodulestate = afxgetmodulestate ();
Pmodulestate-> m_hcurrentinstancehandle = hinstance;
Pmodulestate-> m_hcurrentresourcehandle = hinstance;

...

}

Originally in the DLL entry function, this structure was initialized with the DLL's hinstance.

At this time, we still do not understand, why do we need to switch resources? What is the _ afxthreadstate that we started earlier? It seems to be related to thread. What is it?

Thread_local (_ afx_thread_state, _ afxthreadstate)

# Define thread_local (class_name, ident_name )/
Afx_datadef cthreadlocal <class_name> ident_name;

Template <class type>
Class cthreadlocal: Public cthreadlocalobject

Again
Tracking down and finding that the code is getting more and more difficult, but the basic function is to access the private data of the thread of the current line of code. The so-called private data of threads means that different threads execute the same generation
The resulting data may be different. This reminds me that many of the MFC handles are stored in the Global Map and placed in the private data zone of the thread. Therefore, it is very important to pass the MFC object across threads.
Insecure. But why does MFC do this? So far, I still cannot understand this problem.

Or back to the initial code, how is resource switching done?

Int cdialog: domodal ()
{
...

Hinstance hinst = afxgetresourcehandle ();
If (m_lpsztemplatename! = NULL)
{
Hinst = afxfindresourcehandle (m_lpsztemplatename, rt_dialog );
Hrsrc hresource =: findresource (hinst, m_lpsztemplatename, rt_dialog );
Hdialogtemplate = loadresource (hinst, hresource );
...
}

_ Afxwin_inline hinstance afxapi afxgetresourcehandle ()
{Assert (afxcurrentresourcehandle! = NULL );
Return afxcurrentresourcehandle ;}

# Define afxcurrentresourcehandle afxgetmodulestate ()-> m_hcurrentresourcehandle

Afx_module_state * afxapi afxgetmodulestate ()
{
_ Afx_thread_state * pstate = _ afxthreadstate;
Afx_module_state * presult;
If (pstate-> m_pmodulestate! = NULL)
{
// Thread state's module State serves as override
Presult = pstate-> m_pmodulestate;
}
Else
{
// Otherwise, use global app state
Presult = _ afxbasemodulestate. getdata ();
}
Assert (presult! = NULL );
Return presult;
}

Original
To load Resources in the dialog box of MFC is to load resources by obtaining the resourcehandler saved by the modulestate corresponding to the current thread. Therefore, the code in the DLL must
In the function entry, replace the modulestate of the current execution thread with the state of the DLL to load the DLL resources! At this time, I suddenly understood why
It depends on the private data of the thread to save the modulestate. It is actually passed! -- This is because the cdialog is stored in another DLL, for example
Mfc40.dll, If you connect to the MFC Library in shared mode. The cdialog subclass compiled by the user is not placed in the same DLL of cdialog. How do they transmit this
What about the resource handle? Two solutions: 1. Use parameters for transmission. 2. It is stored in a public place. The former needs to add parameters, which is very troublesome. Does the Win32 API seem to be implemented in this way? After
You need to determine where this public place is located? This reminds us of the creation of a Public dynamic library? Provided by the main program? Let's say that there is a container concept in J2EE (COM + also seems to have, no
Know how. Net Works), and all components are stored in the container. At this time, we can imagine storing the data in the container. In any case, the implementation of MFC is placed in the private data zone of the thread, without the need for public
The master program is not required for a dynamic library! It thinks that a good solution is perfect, but it triggers a series of problems, especially those who do not understand it.

Off
In resource loading, the problem seems to have been solved, but there is still a little bit of trouble, that is, the dll I implemented is not output with a common output function, but an output class, I don't want to add member functions in every class.
Add afx_manage_state (afxgetstaticmodulestate ()). What should we do? Now that we know how resource switching works, we add two
Output Functions, which correspond to the construction and destructor of afx_maintain_state2 respectively. You can call them before and after using the class. Or, they are placed in the class constructor and destructor respectively. Again
Or, it is declared as a member variable. In any case, you need to ensure that resource switching must be correctly nested and cannot be cross-called across different DLL files.

Good
Now the resources in the DLL can be called correctly, but when the dialog contains the IE control, we still fail. Why? I know that for ActiveX Control
File, dialog needs to do some special processing, afxenablecontrolcontainer (), I also know, to use Com, you need
Coinitialize (), but I never thought it would take two times to get ie out, but in the end this is the case. The strange thing is that if it is not in the working thread, it is not required at all.
Coinitialize () to load the IE control.

Process_local (coccmanager, _ afxoccmanager)

Void afx_cdecl afxenablecontrolcontainer (coccmanager * poccmanager)
{
If (poccmanager = NULL)
Afxoccmanager = _ afxoccmanager. getdata ();
Else
Afxoccmanager = poccmanager;
}

# Define afxoccmanager afxgetmodulestate ()-> m_poccmanager

This
It seems that this _ afxoccmanager should belong to the whole process, and there is only one process, that is, the DLL that defines it. However, you need to set this object (or create a custom
(Note that this attribute is included in the previous afx_module_state), that is,
Afxenablecontrolcontainer (), so that the specific modulestate has the occmanager information! However, please note that
In the target DLL, you must switch the resource correctly before proceeding, as shown below:

Afx_manage_state (afxgetstaticmodulestate ());
Coinitialize (null );
Afxenablecontrolcontainer ();

At this point, the problem that has plagued me for a long time has finally become clearer.

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.