C++ Q&A: Browser Detection Revisited, Toolbar I...

We were unable to locate this content in de-de.

Here is the same content in en-us.

Browser Detection Revisited, Toolbar Info, IUnknown with COM and MFC
Paul DiLascia
I
n the January 2001 issue of MSDN® Magazine, Rolf Wenger asked how to find the default browser in order to open an HTML file. I showed him how to find the name of the default browser program by examining the registry key HKCR\htmlfile\shell\open\command. Several readers sent me e-mail to point out that this can be achieved more simply by calling ShellExecute(Ex).
/// As I've shown in many programs...
ShellExecute(0, _T("open"),
   pszMyHTMLFile, 0, 0, SW_SHOWNORMAL);
      Well, put me in the corner and call me a conehead! Obviously that's a better way to open an HTML file. I apologize for being such a geek; I was too caught up in finding the default browser to notice that all Rolf wanted to do was open the file. As if that wasn't bad enough, even if for some reason you still need to know specifically which browser is the default (for example, to have more control launching it than ShellExecute provides), there's a better way than the one I described. As reader Bill Wilder kindly pointed out: just call FindExecutable. This handy function fetches the name of the program associated with any file name.

Q
I'm writing a program to find out information about toolbar buttons in all currently running applications. I found that the class name of the toolbar window is different in different apps. For example, in Windows® Explorer and Spy it's ToolbarWindow32; in Microsoft® Visual C++® it's "Afx:400000:b:1486:10:0"; and in Microsoft Word it's MsoCommandBar. Also, sending messages like TB_BUTTONCOUNT fails in the apps that have toolbar classes other than ToolbarWindow32. Why are there so many toolbars, and is there any general way to get information about all of them? Please give me your valuable suggestion.
SA. Basha Bangalore
India

A
I understand the problem, but alas, I'm afraid I have no par-ticularly valuable suggestion. The multiplicty of toolbar classes is an artifact of the way software systemsâ€"and indeed most systems, not to mention life in generalâ€"evolve. Which is to say, chaotically. It usually goes like this: in the beginning someone has a Great New Ideaâ€"in this case, toolbars. The idea catches on, and soon everybody wants one. Programmers write to magazines like MSDN Magazine (MSJ in those days) asking, "How do I put a toolbar in my app?" Others roll up their sleeves and just do it. The result is diversity. Same feature, many implementations.
      If a software idea is successful, the Redmondtonians eventually assimilate it. (No Borg jokes, please.) In the case of toolbars, the Microsofties introduced a group of "common controls" which included, among others, ToolbarWindow32. MFC encapsulates this with CToolBarCtrl, and so does CToolBarâ€"though in fact the original CToolBar in the first release of MFC was homegrown and did not use ToolbarWindow32!
      Over time, a kind of convergence takes place. Older apps ditch their proprietary implementations in favor of common code; new apps start out with the new thing. In other words, standardization ensues. In this case, more and more apps use ToolbarWindow32 for their toolbars. Life is good.
      But not for long. In the push for ever more UI features, it's always tempting to do something new. The standard controls don't do quite what you want, and you can't customize them sufficiently. Ever since the list and tree controls came out, I've had a steady stream of questions about how to make them do this or that. Sometimes, if the original designers were forward thinking enough, the Standard Thing has some way of extending itselfâ€"like NM_CUSTOMDRAW for common controls. But no matter how sapient the original architects might be, there always comes a day when the original code is too stultifying and it's easier and more satisfying to write your own toolbar, button, menu, whatever.
      As you discovered with Spy++, Visual C++ and Microsoft Office both use their own different toolbar classes. The Office productsâ€"Microsoft Excel, Word, Outlook® and companyâ€"use something called MsoCommandBar. I can imagine the Office folk sitting around the conference table discussing options: "That standard toolbar can't do what we want, so let's write our own." And it's not only Microsoft. A quick survey of my machine reveals that other apps do the same, particularly "big" apps. The more popular a program is, the more money the company makes selling it, the more programmers it can hire, the more likely the programmers are to build and maintain their own libraries that duplicate standard classes.
      Before you lament this distressing situation, remember: it's only distressing if you're trying to write some kind of spylike program that peers inside the toolbars of other apps! If you're a user, you don't care a whit whether your app's toolbar uses ToolbarWindow32 or MsoCommandBar. You only care that the program works and is easy enough to use. If the different toolbar gives you more features you like, all the better! Standardization is good, standardization in code delivers standardization in UIâ€"but too much standardization can sometimes be stifling. As in the Animal Kingdom, diversity promotes innovation.
      So the upshot is, I can't give you any magic solution to peer inside every kind of toolbar. Instead, I would question why you need to do it. The only kinds of apps I can think of that need something like this are accessibility apps (such as apps that improve readability for people with poor vision), and the preferred solution for that is to use the Accessibility API, which provides the hooks you need. (Of course, the big problem there is that apps have to use it firstâ€"but that's another column.)
      If, despite my warnings, you're still determined to "crack" each toolbar, there's nothing you can do but special-case as many toolbar window classes as you can, accepting the fact that you'll never crack them all. You'll have to use spy tools to glean what the window classes and messages are, but it'll be rough going since you can't always even determine which windows are in fact toolbars: apps can use arbitrary class names, and you can't assume just because "Afx:400000:b:mumble..." is a toolbar in Visual C++ that it's always a toolbar in other apps, too! It could be a toolbar in one app and something else in another. MFC assigns class names automatically, using a combination of window style and cursor, background brush, and icon handles. You'll probably have to insert some really grody lines like
IF appname="foo" THEN ToolbarClass = "[insert name here]"
Yuk!
      Most programmers will never write a spy program that needs to know about other apps' windows, but the preceding overly philosophical toolbar discussion is nevertheless relevant for a different reason. As a developer, you're constantly confronted with the choice, "Do I use standard components, or do I build my own thing?" There's always a tradeoff between being the first to have some new GUI feature (toolbars, coolbars, menu buttons, whatever) and waiting until the feature becomes part of the operating system. In most cases, I advise waiting.
      Whatever your app is, you'll always do better by focusing your energies on the app itself; that is, on what the app does. If you're writing a wave editor, make it a good editor. If you have a portfolio program, focus on the stocks and bonds. Make your app fast, make it reliable, make it work. If you do those things, users will love and use your program, and I absolutely positively guarantee you no one will even notice it doesn't have a coolbar or menu buttons, personalized menus or whatever the latest GUI gadget is. The Napster program doesn't have any of these things, it even has a dopey-looking button barâ€"and yet it's used by thousands of people every day! Why?â€"Because what it does is valuable. I tried one of the most popular MP3 players recently, one whose UI is so slick it has glitzy customizable skins. But it's so slow and hogs so much CPU time that I ditched it for an older player with ordinary grey buttons that's much faster and so lightweight my Pentium doesn't even notice it's there. So my advice is: let the Redmondtonians write your GUI for you.
      On the other hand, if you really really really believe your success depends on having a state-of-the art user interface, and everything else works perfectly, and you have the resources to spendâ€"then go for it! You'll find the help and resources you need right here in the pages of MSDN Magazine.

Q
I've been programming COM using MFC for some time. I know how the macros and nested classes work, how the IUnknown interface is handled in these nested classes, but I am still confused about how the IUnknown is handled.
      Suppose CMyClass is a COM server, and that it is derived from CCmdTarget. It implements IMyInterface, so MyClass looks like the following:
class CMyClass: public CCmdTarget
{
    BEGIN_INTERFACE_PART(...)
    STDMETHOD....
    END_INTERFACE_PART
    DECLARE_INTERFACE_MAP
} ;
Since CCmdTarget has no method called QueryInterface, CMyClass doesn't have this method either. My question is this: the client of my COM server always calls QueryInterface; it doesn't call ExternalQueryInterface, nor InternalQueryInterface. So if QueryInterface is not a valid method of CMyClass, how does the magic happen?
Hu Wei

A
Ah, the question of the missing QueryInterface.� You might be surprised to know that many people besides you find this issue perplexing. Never fear; I am here to deperplexify you. An even more potentially confusing feature is that CCmdTarget is itself derived from CObject, and both these classes have virtual functions other than QueryInterfaceâ€"for example, the first virtual function in CObject is GetRuntimeClass and the first virtual function in CCmdTarget is OnCmdMsg. So how can any class derived from CCmdTarget be a COM class when the first three virtual functions of any COM interface are supposed to be IUnknown: AddRef, Release, and QueryInterface?
      The answers to these mystifying questions are actually quite simple, but as usual, they're buried deep within MFC. To understand what's going on, let's take a little trip through MFC-land, beginning when someone calls CoCreateInstance to create an instance of your class.
      The first thing CoCreateInstance does (more or less) is look in the registry to find out which DLL implements your class. For simplicity, let's assume an in-proc server. CoCreateInstance loads your DLL and calls the special function DllGetClassObject.
// DLL function to create a COM object factory
DllGetClassObject(REFCLSID rclsid, // class ID
                  REFIID riid,    // Interface ID
                  LPVOID* ppv)    // pointer to interface returned
      DllGetClassObject doesn't actually get an instance of your class, it gets an instance of a factory (IClassFactory) that can create an instance of your class. Pretty sneaky, eh? MFC provides an implementation of DllGetClassObject that searches all the factories in your DLL for one whose class ID matches the one requested. How does MFC know which factories to search? When you write your class, you use the macros DECLARE_OLECREATE and IMPLEMENT_OLECREATE to declare and implement a class factory (COleObjectFactory) for your class. In particular, IMPLEMENT_OLECREATE declares a static instance of COleObjectFactory; this object adds itself (by invoking the class constructor COleObjectFactory::COleObjectFactory) to a list of factories associated with your module or DLL. The upshot is, given a class ID, DllGetClassObject knows how to find the class factory associated with that class. So far, so good.
      Once CoCreateInstance has a class factory, it calls IClassFactory::CreateInstance. Once again, MFC's COleObjectFactory provides the default implementation.
// vastly simplified
STDMETHODIMP COleObjectFactory::XClassFactory::CreateInstance(...)
{
    METHOD_PROLOGUE_EX(COleObjectFactory,
    ClassFactory)
    •••
    // lots o' stuff omitted

    CCmdTarget* pTarget = pThis->OnCreateObject();
    return pTarget->InternalQueryInterface(&riid, ppvObject);
}
      I've omitted a lot of hairy stuff to highlight the basic idea, which is: create an instance and call InternalQueryInterface. OnCreateObject is a virtual COleObjectFactory function that calls the MFC runtime class (CRuntimeClass) to create an instance of your class:
// also simplified
CCmdTarget* COleObjectFactory::OnCreateObject()
{
    return (CCmdTarget*)m_pRuntimeClass->CreateObject();
}
      Once COleObjectFactory::XClassFactory::CreateInstance has an instance of your class, it calls InternalQueryInterface to get a pointer to your class's IUnknown interface. As I said, the details have been omitted to spare the innocent. For example, in real life MFC calls IClassFactory2::CreateInstanceLic, and checks for all sorts of error conditions, as well as aggregation. The difference between InternalQueryInterface and ExternalQueryInterface is that the external version delegates to the outer IUnknown if there is one, while the internal version does not. But even with aggregation, all roads eventually lead to InternalQueryInterface.
      Are you lost yet? Don't worry, I'm almost finished. At this point the class factory CreateInstance function has an instance of your class, and it's calling CCmdTarget::InternalQueryInterface to get the IUnknown pointer for your class. Where does InternalQueryInterface get a pointer to your class's IUnknown? And where's the elusive QueryInterface?
      There are several steps and functions along the way, but CCmdTarget::InternalQueryInterface eventually calls a function GetInterface, which goes like this:
LPUNKNOWN CCmdTarget::GetInterface(const void* iid)
{
    LPUNKNOWN ptr = NULL;
    if (iid == IID_IUnknown) {
        ptr = // first interface in interface map
    } else {
        ptr = // lookup iid in interface map
    }
    return ptr; // (NULL if not found)
}
      In other words, if the interface requested is IUnknown, GetInterface returns a pointer to the first interface in your interface map; otherwise it searches for one that matches the interface ID requested. The trick is this: since every interface your class provides must implement IUnknown as its base interface, and since moreover every interface must implement it the same way, it doesn't really matter which interface pointer InternalQueryInterface returns; any one will do. The CCmdTarget class itself doesn't have a QueryInterface function; only the nested classes do, the ones that implement each interface. And each interface implements IUnknown.
      Figure 1 shows a typical COM class implementation. The .cpp file shows the same boring IUnknown implementation that you must write for every interface your class supports. Each IUnknown method calls the corresponding ExternalXxx (or the InternalXxx if you want to disallow aggregation) method for the parent (CCmdTarget-derived) class. The implementation is the same for every interface you write. This must always be the case; since all the interfaces are instantiated by the same single object in memory, AddRef and Release must increment and decrement the same physical pointerâ€"regardless of which interface (nested class) the AddRef or Release actually belongs to. Pretty clever, eh?
      That is why, in the case of IUnknown, only InternalQueryInterface can return a pointer to the first interface in your interface map. Any one will do. And, of course, there must be at least one interface in your map because a class that implements only IUnknown is a useless class indeed. In case you're lost, here's the executive summary.
  1. The client calls CoCreateInstance to create an instance of your class.
  2. CoCreateInstance finds and load your DLL, and calls DllGetClassObject.
  3. DllGetClassObject searches your DLL's list of factories for the one with the right class ID, and then returns a pointer to this factory.
  4. CoCreateInstance calls IClassFactory::CreateInstance to create an instance of your app.
  5. COleObjectFactory::XClassFactory::CreateInstance calls CRuntimeClass::CreateInstance to create an instance of your MFC class in memory. CreateInstance then calls InternalQueryInterface to get a pointer to your class's IUnknown interface. InternalQueryInterface in turn calls CCmdTarget::GetInterface.
  6. If the interface requested is any other than IUnknown, GetInterface looks the interface up in your class's interface map. If the interface is IUnknown, GetInterface returns a pointer to the first interface in your class's interface map. This works because every nested implementation of IUnknown must be the same.
  7. All the functions return, return, return and the original caller that started the whole mess receives a pointer to your interface. Presto!
Who said COM was confusing?
      Before I leave this subject, I should point out a couple of useful tips and suggestions. There are several places you can override MFC's default behavior to do your own thing. The first place is in the class factory. There are times when you might want to create your own specialized class factory derived from COleObjectFactory. You can do so if you like; the only trick is hooking your factory up to the object. You can't use DECLARE_OLECREATE and IMPLEMENT_OLECREATE since these macros have COleObjectFactory hardcoded into them. But you can copy the macros, rename them, and change the factory class name to your own.
      In particular, one COleObjectFactory method you might want to override is COleObjectFactory::OnCreateObject. For example, if your COM object is a singleton, you could override OnCreateObject to return the one and only instance of the object. (Don't use a static object, however; you'll probably run into ref count problems because the static instance will get created with a ref count of one and never get its final Release. Instead, use new to allocate your singleton on the heap.)
      Finally, one very useful override is GetInterfaceHook. Remember GetInterface? That's the function that looks the interface up in your interface map, or just returns the first one if IUnknown is requested. It actually looks like this:
LPUNKNOWN CCmdTarget::GetInterface(const void* iid)
{
    // allow general hook first chance
    LPUNKNOWN lpUnk;
    if ((lpUnk = GetInterfaceHook(iid)) != NULL)
        return lpUnk;
    •••
  // as before

}
      Before it does anything, GetInterface calls the virtual function CCmdTarget::GetInterfaceHook. The default CCmdTarget implementation returns NULL, but if you want to implement QueryInterface some special way, all you have to do is override GetInterfaceHook to return something else. In effect, GetInterfaceHook gives you first crack at QueryInterface: if you return a non-NULL interface pointer, MFC will use it. In my COMToys article in the November and December 1999 issues of MSJ, I showed how you can override GetInterfaceHook to achieve a completely different COM implementation, one that uses ATL-style multiple inheritance instead of MFC's nested classes.


Who said COM was COMplicated?
Got a question? Send questions and comments to cppqa@microsoft.com.

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

From the March 2001 issue of MSDN Magazine

Page view tracker