C++ Q&A

Updating Views in MFC, Spying on Events in the .NET Framework

Paul DiLascia

Code download available at:CQA0405.exe(198 KB)

Q I want to update all the child windows on one timer event which is in CMainFrame in my MDI app. The views are displaying many graphs. I am able to update only the active window with the following line of code:

GetActiveWindow()->GetActiveView()->GetDocument()

Is there any way that I can get all the child windows or all documents from the CMDIFrame class?

Q I want to update all the child windows on one timer event which is in CMainFrame in my MDI app. The views are displaying many graphs. I am able to update only the active window with the following line of code:

GetActiveWindow()->GetActiveView()->GetDocument()

Is there any way that I can get all the child windows or all documents from the CMDIFrame class?

Makarand

A Your situation is not uncommon. Many programs that collect real-time data need to update their screens periodically. Even if your program doesn't collect data in real time, you still have to update your views any time the user does something to change the document. Fundamental to MFC's doc/view model (and all object/view models) is the idea that data and presentation are separate. Users—or real-world events—change the underlying object, data, or document; the changes are then propagated to the presentation via some sort of update-the-view-now event.

A Your situation is not uncommon. Many programs that collect real-time data need to update their screens periodically. Even if your program doesn't collect data in real time, you still have to update your views any time the user does something to change the document. Fundamental to MFC's doc/view model (and all object/view models) is the idea that data and presentation are separate. Users—or real-world events—change the underlying object, data, or document; the changes are then propagated to the presentation via some sort of update-the-view-now event.

If you have several views on the same document, MFC already has a mechanism to update all the views in one fell swoop. The function to call is CDocument::UpdateAllViews, which calls CView::OnUpdate for each view open on the document. You can pass your own app-specific "hint" describing what kind of update operation to perform. For example, if you know that only the title of the document has changed, you could define an enum value CHANGED_TITLE and send it as the hint code. If your document has graphics and text you can define enums CHANGED_TEXT and CHANGED_GRAPHICS. The purpose of these hint codes is to improve performance. By giving the view a hint as to what has changed, it can more intelligently repaint only those areas of the screen that actually need repainting, potentially avoiding lengthy paint operations or screen flicker.

UpdateAllViews updates all the views attached to a document, but how do you update all documents? There's no UpdateAllDocuments function in MFC, so you have to enumerate all the docs yourself. This requires a nested traversal of document templates and documents, as shown here:

for (/* each CDocTemplate in app */) { for (/* each CDocument in CDocTemplate */) { // do something } }

Since enumerating the docs is so useful, I wrote a little class, CDocEnumerator, that hides all the MFC mechanics of templates and POSITIONs. I actually wrote such a class way back in September 1995—but hey, that was almost 10 years ago! Figure 1 shows the code. CDocEnumerator makes it easy to enumerate all the open documents in your application:

CDocEnumerator it; CDocument* pdoc; while ((pdoc=it.Next())!=NULL) { // do something }

Figure 1 CDocEnumerator

DocEnum.h

// CDocEnumerator — Enumerates all the documents in an MFC application. // To use: // // CDocEnumerator it; // CDocument* pdoc; // while ((pdoc=it.Next())!=NULL) { // // do something // } // class CDocEnumerator { private: CPtrArray m_doclist; // array of all docs int m_iPos; // current position in array public: CDocEnumerator(); CDocument* Next(); void Reset() { m_iPos=-1; } };

DocEnum.cpp

// CDocEnumerator Enumerates all the documents in an application. #include "StdAfx.h" #include "DocEnum.h" ////////////////// // ctor loads all doc pointers into private array. // CDocEnumerator::CDocEnumerator() { CWinApp* pApp = AfxGetApp(); POSITION pos = pApp->GetFirstDocTemplatePosition(); while (pos!=NULL) { CDocTemplate* pDocTemplate = pApp->GetNextDocTemplate(pos); POSITION pos2 = pDocTemplate->GetFirstDocPosition(); while (pos2!=NULL) { m_doclist.Add(pDocTemplate->GetNextDoc(pos2)); } } Reset(); } ////////////////// // Fetch next document. // CDocument* CDocEnumerator::Next() { if (++m_iPos < m_doclist.GetCount()) { return (CDocument*)m_doclist[m_iPos]; } return NULL; }

What could be easier? To show how it all works in practice, I wrote a little program, UpdView, that simulates your real-time data collection program. Each UpdView document counts the seconds since it was opened. Figure 2 shows UpdView in action. If you download, build, and run UpdView, you can see that each view updates every second to show how long its document has been open. In Figure 2, the document named file2.dat has two views, both of which display the same underlying document. Each document maintains its own time-since-opened value (data); the view merely displays it (presentation). As in your own program, UpdView works by setting a timer in the main frame. The timer handler uses CDocEnumerator to tell each document to collect more data, as shown here:

void CMainFrame::OnTimer(UINT_PTR nIDEvent) { CDocEnumerator it; CDocument* pdoc; while ((pdoc=it.Next())!=NULL) { ((CMyDoc*)pdoc)->CollectMoreData(); } }

Figure 2 UpdView in Action

Figure 2** UpdView in Action **

CMyDoc::CollectMoreData increments a simple counter. In a real program, CollectMoreData would fetch recent stock quotes, download the latest images from Mars, or read the temperature in Bill Gates's bathtub. Whatever. The important thing is that after collecting the data, CMyDoc tells its views to update themselves:

void CMyDoc::CollectMoreData() { iData++; // time waits for no man... UpdateAllViews(NULL, 0, NULL); }

Now MFC calls each view's OnUpdate method, which calls Invalidate/UpdateWindow to repaint the view. Since UpdView is so simple, there's no need for hints. In a real program, you might pass hint info to help the view repaint its window more efficiently.

Q I'm writing an app in C# using the Microsoft® .NET Framework. I understand how events work, but sometimes it's hard to see which events get raised when. When I'm programming in C++, I can always run Spy++ to see when windows messages get sent. Even without Spy++, I can write a window procedure that displays the message as TRACE output. For example:

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { TRACE(_T("Message: %04x, wp=%04x, lp=%04x\n"), msg, wp, lp); ••• }

Q I'm writing an app in C# using the Microsoft® .NET Framework. I understand how events work, but sometimes it's hard to see which events get raised when. When I'm programming in C++, I can always run Spy++ to see when windows messages get sent. Even without Spy++, I can write a window procedure that displays the message as TRACE output. For example:

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { TRACE(_T("Message: %04x, wp=%04x, lp=%04x\n"), msg, wp, lp); ••• }

I tried to do something like this for events in C#, but I couldn't figure it out because there's no central event handler. Is there some way to see the events, or is there a third-party Spy tool for .NET?

Francine Wiggins

A I'm not aware of any event spy tool for the .NET Framework, but it's not too hard to write some code that spies on events. Notice I said not too hard—because it turns out it's a bit harder than the WndProc example for messages.

A I'm not aware of any event spy tool for the .NET Framework, but it's not too hard to write some code that spies on events. Notice I said not too hard—because it turns out it's a bit harder than the WndProc example for messages.

Just to make sure everyone understands, let me underscore the difference between window messages and Framework events. In Windows®, the operating system communicates with your program by sending messages to your window procedure (WndProc) when something happens. It's easy to spy on messages because every message goes through a single callback: namely, the window procedure. This is true whether you're programming in C/C++ or the .NET Framework. In .NET, you can spy on window messages by overriding Control.WndProc, the same as in your C example. But while window messages are conceptually a kind of event (and many programmers call them events), and while many Framework events correspond to window messages (for example, OnClick is the same as WM_COMMAND/BN_CLICKED), Framework events use a different mechanism entirely, one based on delegates instead of a universal callback procedure. Whereas every window message goes through the same WndProc, every Framework event has its own delegate. So how can you spy on events when there's no central handler? How do you even know which events exist to be handled?

Finding the events is relatively easy. One of the most powerful features of the .NET Framework is reflection, which is a fancy way to say that .NET is self-conscious. For example, to find all the public events the Form class can raise, you can write:

EventInfo[] allEvents = typeof(Form).GetEvents();

This fetches an array of EventInfo objects, one for each event defined in the Form class. Each EventInfo object contains information describing the event. EventInfo.Name is the name of the event (for example, Click or Closing). EventInfo.EventHandlerType is the Type of the handler (delegate) required to handle the event. You can even call EventInfo.AddEventHandler to add another handler. This might lead you to suspect that one way to spy on events would be to write a generic handler and hook it up to every event in the class you want to spy on. This approach is on the right track, but the details are somewhat complex. How do you write the generic handler? What's its signature? The obvious answer would be:

// generic event handler—for any event public void OnAnyEvent(Object sender, EventArgs e);

However, not all handlers use EventArgs. For example, the Form.Closing event requires a handler with CancelEventArgs:

void OnClosing(Object sender, CancelEventArgs e);

CancelEventArgs is, of course, derived from EventArgs, so it's perfectly legal to pass CancelEventArgs to a method that expects EventArgs—but delegates in version 1.1 of .NET don't work that way. When you add an event handler with += or EventInfo.AddEventHandler, you must supply a delegate whose type exactly matches the event handler type. This means you can't use a single generic handler for all events. At minimum, you'll need one handler for each event delegate signature.

But it's even worse than that. Even if you could use a generic handler, how will the handler know which event was fired? The whole point of a spy tool is to report which event fired. If nothing else, the spy tool should display the name of the event. But the Framework doesn't pass information about which event was fired because this information is implicit in the handler itself. You know it's the Fooble event because your OnFooble handler was called. If you use the same handler for multiple events, you lose the ability to distinguish them. So the only way to spy on events is to write a different handler for each event. This would seem to present an insurmountable problem: how can you possibly write the code when you have no idea until run time which object you're expected to spy on and what events it exposes?

By writing the code itself at run time, of course! Reflection doesn't only mean you can query objects in the system; it also means you can create them. There are several ways to generate code in the .NET Framework. System.CodeDom provides a high-level, language-independent code model you can use to create code objects such as assemblies, modules, classes, and methods, then emit them in your favorite language using classes like Microsoft.CSharp.CSharpCodeProvider or Microsoft.VisualBasic.VBCodeProvider. Another way to generate code is to explicitly write out the code in a language of your choice to a file or into a StringBuilder and use the Process class to invoke the appropriate command-line compiler (such as C# compiler csc.exe). In fact, the friendly Redmondtonians assure me "this is done all over the Framework." For example, XmlSerializer uses csc.exe to compile dynamically generated C# that is optimized for serializing and deserializing specific types.

The Code Document Object Model (CodeDOM) is designed for code generators that need to deal with code abstractly in order to emit it in multiple languages from a single internal representation and optionally compile it. Running a compiler like csc.exe entails a performance hit, which may nevertheless be justified by repeatedly invoking the code—the same way you might compile a regular expression (RegEx) you plan to use frequently. But there's still another way to generate code on the fly: you can use System.Reflection.Emit.ILGenerator to generate low-level Microsoft intermediate language (MSIL) instructions directly. It's definitely not for the fainthearted, but it's quick and efficient and occasionally just the ticket to pull some swift software trick—like writing an event spy.

To show how, I wrote a class, EventSpy, that reports every event raised by some target object, the spyee. I also wrote a program called SpyTest that shows how to use it. (Both EventSpy and SpyTest are available for download from the MSDN® Magazine Web site). Using EventSpy is easy: just instantiate and add a handler for the SpyEvent event, like so:

// spy on myself spy = new EventSpy("MySpy", this); spy.SpyEvent += new SpyEventHandler(OnEventSpy);

EventSpy raises a SpyEvent event every time the target object (the spyee) raises an event of any kind. When this happens, SpyEventArgs.EventName is the name of the event and SpyEventArgs.EventArgs holds the original event args. What you do with the event is up to you. SpyTest reports the event to the diagnostic stream:

// in main form private void OnEventSpy(Object sender, SpyEventArgs e) { Trace.WriteLine(String.Format("{0}: On{1}: {2}", sender.GetType(), e.EventName, e.EventArgs)); }

Figure 3 shows a typical run. Go ahead, download the code, and try it yourself—it really works! EventSpy also has a DumpEvents function that lists all the events your target class exposes.

Figure 3 SpyTrace

Figure 3** SpyTrace **

If all you want to do is use EventSpy to spy on events, you can quit reading and go directly to the MSDN Magazine Web site to download the source. For those masochists among you who may be tempted to implement something like EventSpy on your own (good luck!), I offer a brief overview of how EventSpy works.

The upshot of the event discussion is that in order to spy on events, you have to dynamically generate a class that looks something like Figure 4. Before you create the class, you have to create an assembly and module for it. System.Reflection.Emit has the AssemblyBuilder, ModuleBuilder, TypeBuilder, FieldBuilder, ConstructorBuilder, and MethodBuilder classes to build everything you need. Here's how to create an assembly on the fly:

AssemblyName an = new AssemblyName(); an.Name = "EventSpyHelper"; AssemblyBuilder asm = AppDomain.CurrentDomain .DefineDynamicAssembly(an,AssemblyBuilderAccess.Run);

Figure 4 EvSpyImpl

////////////////// // This is what the dynamic helper class would look like if it were // written in C# // public class EvSpyImpl : Object { private EventSpy spy; public EvSpyImpl(EventSpy s) { spy = s; } public void OnEventFoo(object o, EventFooArgs e) { spy.ReportEvent("EventFoo", o, e); } public void OnEventBar(object o, EventBarArgs e) { spy.ReportEvent("EventBar", o, e); } ••• // ditto for all events in target object type. }

Creating the module is even simpler. Creating the class, fields, and methods is likewise mostly a straightforward matter of giving each thing a name and perhaps some flags and calling the appropriate builder method. The hardest part comes when it's time to generate the actual code—in this case, the constructor and event handlers for each event. The key is a class called ILGenerator. Here's how EventSpy writes the event handlers in Figure 4:

// create a new method OnEventXxx MethodBuilder mthd = helperClass.DefineMethod(...); // generate its code ILGenerator il = mthd.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld,fld); il.Emit(OpCodes.Ldstr,ev.Name); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Callvirt, miReportEvent); il.Emit(OpCodes.Ret);

Each call to ILGenerator.Emit emits a single MSIL instruction. But how did I know which instructions to emit? Do you really think I know how to program in MSIL? Of course not! You don't have to speak MSIL to emit code. All you have to do is write the code you want in C# (or any other .NET-targeted language), compile it, and use ILDASM to inspect the generated MSIL. This is what I did; Figure 5 shows part of the ILDASM dump for the code in Figure 4. As you can see, the MSIL is the same as what appears in the previous snippet.

Figure 5 ILDASM Dump

Figure 5** ILDASM Dump **

Once you know the trick to discovering the right MSIL, the rest is fairly straightforward, albeit rather bug-prone: any little mistake you make is bound to send your system spiraling into psychosis with a death message that does little to suggest where your error lies. Like I said, MSIL is not for the fainthearted. At least nobody's life is hanging in the balance. Your job, maybe.

EventSpy is fairly primitive. You can't turn spying on or off, you can only spy on instance (as opposed to static) events and you have to create a new EventSpy instance for every object you want to spy on. Well, what do you expect from a free download? But EventSpy gets the job done when you're debugging, and I've used it successfully a number of times to see what's happening in the event stream. I leave it to you to add more features. My main mission is to tip you off about System.Reflection.Emit and dynamic code generation. Figure 6 summarizes the different ways to generate code dynamically in .NET. To learn more, check out the documentation or the article "Bring the Power of Templates to Your .NET Applications with the CodeDOM Namespace" by Adam J. Steinert. Happy programming!

Figure 6 Generating Code Dynamically in .NET

Technique Description
CodeDOM For code generators that need to deal with code abstractly, then emit high-level source code in multiple languages. Very high-level.
csc.exe, vbc.exe You can invoke the C# compiler for C# code that you generate dynamically. Entails a small performance hit. Use when your code is too complex for manually written MSIL.
ILGenerator Use to generate MSIL instructions on the fly. Most efficient, but also most low-level, error-prone, and difficult to debug. Use when the code you want to generate is very simple.

Send your questions and comments for Paul to  cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at https://www.dilascia.com.