Click to Rate and Give Feedback
Related Articles

The Parallel Patterns Library allows you to more easily take advantage of parallelism. See what this and other Visual C++ 2010 features are in store.

Kenny Kerr

MSDN Magazine February 2009

...

Read more!

This month's column explains how to use Windows HTTP Services, or WinHTTP, the new, powerful API for implementing HTTP clients.

Kenny Kerr

MSDN Magazine August 2008

...

Read more!

Maria Blees introduces WinUnit, a handy new unit testing tool for native C++ projects.

Maria Blees

MSDN Magazine February 2008

...

Read more!

James McCaffrey shows you how to get started with UI test automation using the new Microsoft UI Automation library.

Dr. James McCaffrey

MSDN Magazine February 2008

...

Read more!

This month begins the design of the actual mouse class for EEK!

Stanley B. Lippman

MSDN Magazine December 2007

...

Read more!

Also by this Author

Paul DiLascia

MSDN Magazine October 2005

...

Read more!

This month DLL problems, context menus, MFC strings to managed C++, and more.

Paul DiLascia

MSDN Magazine October 2006

...

Read more!

Paul DiLascia

MSDN Magazine January 2006

...

Read more!

Paul DiLascia

MSDN Magazine August 2005

...

Read more!

By now you're probably used to reaching into the .NET Framework using Managed Extensions with your C++ code. But what do you do if you have legacy apps that use older versions of the compiler, or if you want to avoid some of the CLR requirements? Well, you can wrap Framework classes in a native way so you can use them in any C++/MFC app without /clr. Paul DiLascia shows you how.

Paul DiLascia

MSDN Magazine April 2005

...

Read more!

Popular Articles

C# allows developers to embed XML comments into their source files-a useful facility, especially when more than one programmer is working on the same code. The C# parser can expand these XML tags to provide additional information and export them to an external document for further processing. This article shows how to use XML comments and explains the relevant tags. The author demonstrates how to set up your project to export your XML comments into convenient documentation for the benefit of other developers. He also shows how to use comments ...

Read more!

Paul DiLascia

MSDN Magazine August 2002

...

Read more!

James Avery does it again with his popular list of developer tools. This time he covers the best Visual Studio add-ins available today that you can download for free.

James Avery

MSDN Magazine December 2005

...

Read more!

Jason Clark

MSDN Magazine July 2003

...

Read more!

Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

Kenny Kerr

MSDN Magazine May 2008

...

Read more!

C++ Q&A;: Disabling Context Menus, Sending Commands to Doc Objects
MSDN Magazine
Disabling Context Menus, Sending Commands to Doc Objects
Paul DiLascia
Download the code for this article: CQA0109.exe (1,040KB)
Browse the code for this article at Code Center: AboutHtml and DOCTIME

Q
I liked your implementation of CHtml-Ctrl in a dialog. It is exactly what I wanted my app to do. But there's one thing I want to know. Is there a way I can disable the popup menu that results when the user right-clicks on the HTML page? I don't want the user to right-click and view the source of the HTML file I display in my app. I tried overriding WM_CONTEXTMENU in my CHtmlCtrl-derived window, but it doesn't work.
Aamir

I would like to suppress the right button context menu and the popup menu of the CHtmlView or CHtmlCtrl classes. I have tried handling WM_RBUTTONDOWN, but that does not work. Please help.
Gerhard Koell
Austria

A
I'm glad you both liked my CHtmlCtrl class from the January 2000 issue of Microsoft Systems Journal. For readers who didn't catch it, CHtmlCtrl is a class that converts CHtmlView into a control you can use in any window. I used it to write AboutHtml, a program that implements an HTML About dialog. But as Aamir points out, I overlooked something: if you right-click in the dialog box, you get the standard browser context menu (see Figure 1), which is probably not what you want. Well, put a cone over my head and call me Dunce!

Figure 1 Unwanted Context Menu
Figure 1 Unwanted Context Menu

      Fortunately, the problem is trivial to fix. You don't even have to write any code! Just add a line in your HTML page:
<BODY oncontextmenu="return false">
This tells the browser: don't display a context menu. You could also write
oncontextmenu="ShowMyMenu(); return false"
where ShowMyMenu is a JavaScript procedure that displays your own custom menu. I wrote a new program, AboutHtml1, that uses oncontextmenu; you can download it from the link at the top of this article.
      But since this is a C++ column, not a JavaScript column, let me show you another, more complicated way to accomplish the same thing using C++. Why not? The official C++ way would be to implement IDocHostUIHandler (see the docs for more info), but that's a lot of work. Your idea of handling WM_CONTEXTMENU or WM_RBUTTONDOWN in CHtmlCtrl is on the mark, and certainly the normal Windows way to do things. The problem is, the CHtmlCtrl window isn't the real input window. There are more windows here than meet the eye. To see how many, just open up your trusty Spy++ tool and take a look. As Figure 2 shows, the browser window is three parent/child levels above the actual input window.
Dialog
 AfxFrameOrView42d      // CHtmlCtrl
  Shell Embedding
   Shell DocObject View
    Internet Explorer_Server
      It's the Internet Explorer_Server window that receives input, and that's the window you have to subclass if you want to trap WM_CONTEXTMENU. In MFC, this means you have to get the HWND and call SubclassWindow. Keep in mind, of course, that this is highly irregular, grody, and definitely verboten as far as the Redmondtonians are concerned. Nevertheless, I wrote yet another version of the original program, AboutHtml2, that does it.

Figure 2 Parent/Child Relationships in Spy++
Figure 2 Parent/Child Relationships in Spy++

      There are many ways to get the mysterious Internet Explorer_Server HWND. FindWindow doesn't work since it only gets top-level windows. Since the server window is the great-grandchild of the browser, with no siblings at any level, the following algorithm works.
static HWND GetLastChild(HWND hwndParent)
{
   HWND hwnd = hwndParent;
   while (TRUE) {
      HWND hwndChild = ::GetWindow(hwnd, GW_CHILD);
      if (hwndChild==NULL)
         return hwnd;
      hwnd = hwndChild;
   }
   return NULL;
}
      This function assumes a single-child-only descendant chain like the one in the browser window—that is, each parent has exactly one child—and gets the "last" (youngest?) child window, which in this case is the Internet Explorer_Server window. Once you have the HWND, all you have to do is write a new MFC class to subclass it.
class CMyIEWnd : public CWnd {
public:
   afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos) { }
   DECLARE_MESSAGE_MAP();
};
This class overrides WM_CONTEXTMENU to do nothing: OnContextMenu is an empty function that returns without displaying a menu or calling the base class (CWnd). To use CMyIEWnd, I added an instance in CMyHtmlCtrl:
class CMyHtmlCtrl : public CHtmlCtrl {
protected:
   CMyIEWnd m_myIEWnd;
};
      All that's needed to hook everything up is to call SubclassWindow. But where? Or rather, when? The most convenient time is after the browser has loaded your page.
void CMyHtmlCtrl::OnNavigateComplete2(LPCTSTR strURL)
{
   if (!m_myIEWnd.m_hWnd) {
      HWND hwnd = GetLastChild(m_hWnd);
      m_myIEWnd.SubclassWindow(hwnd);
   }
}
      So now it goes like this: when the user invokes the About dialog, the dialog creates the CHtmlCtrl window to open the document; after the browser has opened the document, it sends a notification which MFC directs to OnNavigateComplete2. CMyHtmlCtrl::OnNavigateComplete2 calls GetLastChild to obtain the "real" input window and subclasses it. Now all messages destined for Internet Explorer_Server go through CMyIEWnd, including WM_CONTEXTMENU. Be warned, however: the Internet Explorer HWND can change, so if you do this for something more than About dialogs, you must make provisions to unsubclass and resubclass the HWND.
      There are two important things to note about this technique. First, it's powerful since now that you've subclassed the "real" Internet Explorer window, you can do almost anything you want. Second, it's really, really, really bad, and likely to get you in trouble if you aren't careful. Once you take over the Explorer window like this, all bets are off. Didn't your mother tell you that you're not supposed to subvert the browser? You're supposed to customize it through the official interfaces (IDocHostUIHandler)! But there's nothing like breaking the taboos to make your blood flow.

Q
I'm writing a Windows® 2000 performance-monitor-like application in which I have a doc object and several views. The doc object is responsible for collecting data regularly, and notifies the associated views that render the data in different formats. In order for the doc to regularly collect data, it needs a time event. However, the doc is not a window-based object, so it cannot have such a time event. I'm weighing three possible solutions:
  1. Create a timer in one of the views. When the event happens, it tells the doc object to get data.
  2. Create a separate thread in/for each doc, so that the thread can create time events regularly.
  3. Create a timer in CMainFrame, and call doc(s) function from it.
      I don't like any of these options. Can you please give me a better solution to this problem?
Lei Hu
Australia

A
Of the options you list, putting the timer in the view is a bad idea because then you'd have one timer for each view, and you should think of timers as a relatively finite resource. (This was especially true in the old days, less so now.) Creating separate threads is overkill for such a simple thing as a timer. Threads are sure to make your life complicated and unhappy. That leaves option number three: create a timer in the main frame, and call the docs from there. Let me show you how to do this cleanly, and then I'll show you another option you haven't considered.
      I assume the reason you don't like the third option is that it requires calling the documents from your main frame, which is kind of ugly. (Why should the frame know anything about the documents?) But there's a clean way to do it. Instead of calling CMyDoc::DoTimerThing directly, you can convert the WM_TIMER message to a WM_COMMAND with some ID like ID_APPTIMER, and broadcast this command in the normal way so that documents can handle it using ON_COMMAND. Documents can't handle all windows messages, but they can handle WM_COMMAND. In fact, this is one of the main innovations of the MFC command-routing architecture, that it lets non-window objects handle commands. So it would appear all you have to do is the following:
CMainFrame::OnTimer(...)
{
   SendMessage(WM_COMMAND, ID_APPTIMER);
}
      That is, when your main frame gets a timer click, have it send an ID_APPTIMER command to itself. MFC will route the command through the system, and any object that has an ON_COMMAND handler for ID_APPTIMER can then process the event. You can use ON_COMMAND_EX so multiple objects can handle the same event.
      This works, except for one problem. MFC only routes commands to the active view/document. If other documents are open but not active, they won't get the WM_COMMAND message. You can fix this by modifying your app so it broadcasts commands to inactive documents too, but then ordinary commands like File | Save would get routed to all the documents—oops! Only the timer command should go to all documents. How do you do that? How do you send a WM_COMMAND to all documents?
      The way MFC sends commands to non-window objects like documents is through the virtual function CCmdTarget::OnCmdMsg. When your window gets a WM_COMMAND message, it goes through lots of CWnd code and virtual functions. Eventually, control arrives at CWnd::OnCommand, which calls OnCmdMsg.
// in CWnd::OnCommand
OnCmdMsg(nID, CN_COMMAND, NULL, NULL);
Here, nID is the command ID and CN_COMMAND is a code that tells OnCmdMsg that this is a command event—as opposed to, say, an update UI event (in which case the code would be CN_UPDATE_COMMAND_UI). The extra parameters aren't used for CN_COMMAND.
      So the upshot is: if you have a pointer to a document and you want to send that document a command, all you have to do is call
pDoc->OnCmdMsg(nID, CN_COMMAND, NULL, NULL);
This is totally general in the sense that whoever makes this call doesn't have to know anything about the document, except that it is a document. In fact, pDoc needn't be a document at all; it could point to any CCmdTarget-derived object. In effect, OnCmdMsg is the function that CWnd uses to convert WM_COMMAND (for windows) to CN_COMMAND (for command targets).
      Knowing this, you can now solve your problem. If you put the timer in your main frame (CMainFrame), you can write a handler that passes the WM_TIMER event to all the docs like so:
CMainFrame::OnTimer(...)
{
   while (pDoc = /* each document */) {
      pDoc->OnCmdMsg(ID_APPTIMER, 
         CN_COMMAND, NULL, NULL);
   }
}
      How do you loop through all the documents? Figure 3 shows the boilerplate code. Since I can never remember that stuff, nor how MFC's wacky POSITIONs work, I long ago wrote a little class for iterating documents, called—what else—CDocIterator. I have less trouble remembering how CDocIterator works.
for (CDocIterator it; it.doc(); it++) {
   it.doc()->DoSomething();
}
      Figure 4 shows my CDocIterator class. The constructor initializes a CPtrList with all the documents, using the code from Figure 3; the other functions navigate this list. Ho-hum. I wrote a little program, DocTimer1, that puts everything together. CMainFrame::OnCreate sets a one-second timer, and CMainFrame::OnTimer uses CDocIterator to send ID_APPTIMER to all the docs.
for (CDocIterator it; it.doc(); it++) {
   it.doc()->OnCmdMsg(ID_APPTIMER, 
      CN_COMMAND, NULL, NULL);
}
      The documents handle ID_APPTIMER the normal way, using ON_COMMAND. Notice that the _EX version isn't required because CMainFrame::OnTimer ignores the return code from OnCmdMsg, passing the command to each doc regardless of whether it was handled or not. The handler CMyDoc::OnAppTimer increments a counter and updates the view. In real life, you'd do whatever you have to do.
      Figure 5 shows DocTimer1 running with a bunch of documents open. You'll have to take my word for it that all the documents update themselves every second—or download the source and compile and run it yourself.

Figure 5 DocTimer1
Figure 5 DocTimer1

      If you really don't like your main frame calling the documents and managing the timer, there is yet another way, perhaps the cleanest way to implement a timer of this sort. Namely, you can use a timer proc instead of WM_TIMER. Normally when you set a timer, you supply an HWND (or implicit CWnd for CWnd::SetTimer) that identifies the window that should receive WM_TIMER messages. But you can pass NULL as the HWND and instead supply a procedure that Windows should call.
void WINAPI MyTimerProc(HWND hwnd, 
  UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
   •••
}
Now if you call
SetTimer(NULL, 0, 1000, MyTimerProc);
Windows will call your timer proc directly instead of sending WM_TIMER. (You still need a main window, however, or at least a Get/DispatchMessage loop—that's where Windows checks the timer.) You can use the timer proc to remove CMainFrame entirely from the picture. You could even implement your doc timer feature entirely within your document class; all you need is a timer proc and a few static globals. Figure 6 shows a document class that implements its own timer using the previously described CDocIterator to iterate all the documents. Of course, if you implement everything in your doc class, the timer proc could call CMyDoc::DoSomething directly, instead of converting the timer pop to a command event.
      There's one more twist on the timer proc approach, and this is my favorite. Conceptually, the timer is a global service that any object might want to use. You should be able to implement some kind of timer object you can just plop into your app, and then different objects could "listen" to it. How could you implement such a beastie?
      It's easy. All you have to do is modify the document-centric approach just outlined in two ways. First, put the timer proc and SetTimer/KillTimer calls in a separate CAppTimer class that manages the timer; and second, instead of broadcasting ID_APPTIMER to all documents, broadcast it to an arbitrary list of command targets. All you need is a way for objects to add and remove themselves from the list.
      DocTime2 implements this approach. DocTime2 uses two new classes: CCmdTargetList (see Figure 7) and CAppTimer (see Figure 8). CCmdTargetList is a generic list o' command targets with functions that an object can call to register and unregister (add and remove) themselves from the list, and a SendCommand function that broadcasts a command ID to every object on the list. CAppTimer is derived from CCmdTargetList, since the timer is, among other things, a list of command targets. CAppTimer manages the timer and provides a single global instance, theTimer, with an Init function the app must call to set the timer.
// from CMyApp::InitInstance
theTimer.Init(1000, ID_APPTIMER);
This tells CAppTimer to create a 1000 millisecond (1 second) timer and use ID_APPTIMER as the command ID. Each document that wants to receive timer notifications now simply registers itself with the timer when the document is constructed, and unregisters when the document is destroyed.
CMyDoc::CMyDoc()
{
   theTimer.Register(this);
}
CMyDoc::~CMyDoc()
{
   theTimer.Unregister(this);
}
      What could be simpler? The great thing about this approach is that it encapsulates the timer in a single class. All you have to do is plop it into your project, call Init, and then let each object decide for itself whether or not it cares to listen to the clock. Documents, views, frames—any command target—can register for timer pops, and then receive notification through the normal ON_COMMAND mechanism. Once again, there's no need to use ON_COMMAND_EX because CCmdTargetList::SendCommand ignores the return code from OnCmdMsg. This makes sense: no object should be able to prevent other objects from receiving notifications. No matter what, the beat goes on.

Send 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 askpd@pobox.com or http://www.dilascia.com.

From the September 2001 issue of MSDN Magazine.


Page view tracker