|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThis article describes some of the problems that you may encounter while trying to handle ActiveX events in an MFC application. Although there are many articles available that show how to sink ActiveX events, few mention the details of properly maintaining the MFC state. Maintaining the MFC state is critical if you want to use calls like Although the concept MFC state is an issue whenever you make calls or callbacks between different modules, this article focuses specifically on the case of ActiveX events. BackgroundThis article assumes that you are familiar with ActiveX events and the fundamental concepts required to connect to an event source. To review this topic, see the article "VC Clients for VB ActiveX DLLs". In addition, this article is only relevant if you are developing MFC clients for ActiveX event sources that use MFC internally. MFC StateMFC uses three different types of state information.
This article is only concerned with the first one, module state. Although the current module state is maintained on a per thread basis, this article will focus on the single threaded case. For an in depth discussion of all the MFC states, see the book "MFC Internals, Inside the Microsoft Foundation Class Architecture" by George Shepherd and Scot Wingo. Crossing a module boundary occurs when code execution calls into another dll, ActiveX control, or even when an ActiveX control calls back into the application. The latter occurs when an ActiveX event is fired. Whenever an MFC module boundary is crossed, the MFC framework must be notified so that the module state is kept up to date. Otherwise, any of the state specific calls would refer to the state of the prior module. The figure below demonstrates the concept.
In this simple example, the MFC application (MainApp) makes a call into an MFC ActiveX control. When this happens, the current module state must be changed. Next, the ActiveX control fires an event that MainApp has connected to. Once again, when the transition is made back into MainApp, the current module state must be changed. MFC uses the concept of module state to keep track of things such as the current For example, a common trick that many MFC programmers use is to retrieve the current // // Example code that counts on MFC state to get the current WinApp object // CMainApp *pApp = (CMainApp *) AfxGetApp(); pApp->DoSomething(); This code depends on the MFC state. Internally, Another case that depends on the MFC state is when you attempt to load a resource. When you load a resource by ID, you usually want to load it from the current module's resources. An example of this is the How to Manage the MFC StateWhenever a call is made that crosses an MFC module boundary, you can use the When using Visual Studio, in most cases when you create a module (dll, ActiveX control or component) that supports MFC, the Note: there are some exceptions to the state management rules which make this topic even more confusing. Regular dlls that statically link to MFC and MFC extension dll's should not use ActiveX EventsFinally we reach the topic of interest. As mentioned in the previous section, we must make sure that the MFC state is properly maintained whenever we cross over a module boundary. When an ActiveX event is fired, this is exactly what happens. Therefore, we must be sure to maintain the MFC state in the client (the ActiveX event sink). There are several ways to sink ActiveX events. In the article that I mentioned earlier, "VC Clients for VB ActiveX DLLs", the author presents two different approaches.
MFC ActiveX Event SinkThis approach involves creating a The beauty of this approach is that you don't need to do anything to manage the MFC state! The MFC base class You can use the demo program included with this article to prove this to yourself. See the ATL ActiveX Event SinkThis approach involves creating an Unlike the MFC approach, in this case you are responsible for managing the MFC state yourself. To do this, you should make use of the For example, a typical event sink function for an object constructed in the main application should start with the following code. // // Make sure the MFC state is correct. // AFX_MANAGE_STATE(AfxGetAppModuleState()) // // The rest of your code... // As mentioned earlier, when this function returns, the MFC state will be automatically restored. The demo program illustrates the use of this technique in the The Demo ProgramA demo project is included with this article to allow you to step through the code with a debugger and understand the MFC state management. The demo program consists of two projects, a main MFC program, and an ATL ActiveX control. The MFC main program is implemented as a simple dialog application. An example event sink class is provided for both the MFC and ATL methods described above. These classes are named To fire events the ActiveX control implements an interface - The The event sink objects are instantiated and connected in the main program's You can play with the code to understand how the MFC state is changing between function calls. To run the main program, you must first register the control (control.dll) using Regsvr32. Alternatively, if you rebuild the project the control will register itself during the build process. In addition to the two event sinks that were manually created, I also hooked into the control's event using the control's container site code. This was accomplished using the MFC ClassWizard. I added this simply to illustrate the simplest case. In this case the MFC state management is taken care of for you by the control container site classes. ConclusionThis article has highlighted some of the issues that you may run into when using an MFC client to sink events from other MFC controls and components. My first experience with this issue came after releasing a 1.0 version of an MFC application that successfully connected to and handled events from several other (non-MFC) components. There were no problems with the event sink code and everything functioned exactly as expected. Then came version 1.1. We added our first MFC component. Immediately after adding the new component, there were assertions popping up in existing code that was previously working flawlessly. After quite a bit of research I found that our event sinks were suffering from MFC state problems. Hopefully this article will help you to avoid the same problem. History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||