C++ Q&A;: Browser Detection in the Registry, Ch...
Browser Detection in the Registry, Changing Cursors in Windows, Avoiding Resource ID Collision
Paul DiLascia
Q
How do I find the definition of the default browser in the registry? I need the path and the name of the EXE to start a session from my program. My goal is simply to start the default browser so the user can use it as a normal program. I do not need browsing information from the Internet in my own window.
Rolf Wenger

A
As far as I know, there's no registry key or setting anywhere in Windows® that says specifically what the browser is. But then, the full mysteries of the registry are deeper than any individual can probe, even supposed experts. For all I know, there could be a key somewhere, HKCU\System\Mumble\Bletch\Blah\Gak\DefaultBrowser. If you know of such a key, please write in. In the meantime, the quickest way I know to solve your problem is to just look at which program is associated with HTML files. In Windows, HTML files have an extension of .htm or .html, so all you have to do is look in the registry under HKCR/.htm. If you do, you'll find something like:
HKEY_CLASSES_ROOT\.htm="htmlfile" 
And if you follow this to HKCR/htmlfile, you'll find something like:
[HKEY_CLASSES_ROOT\htmlfile\shell\open\command]
@="\"C:\\PROGRA~1\\INTERN~1\\iexplore.exe\" -nohome" 
      In this case, Microsoft® Internet Explorer (iexplore.exe) is the program used to open .htm files. (The -nohome switch tells Internet Explorer not to open your home page.) If the default browser were Netscape, the entry would be something like:
[HKEY_CLASSES_ROOT\htmlfile\shell\open\command]
@="\"C:\\PROGRA~1\\NETSCAPE\\netscape.exe\". 
Got it?

Q
I would like to have a separate cursor on a button in a dialog. How can I set the cursor for one button only?
Rolf Wenger

A
There are two ways to change cursors in Windows: you can supply a global cursor (HCURSOR) as part of the WNDCLASS structure when you register your main window class, or you can set the cursor manually on the fly by handling WM_SETCURSOR. The standard MFC app sets the cursor for you using the first method by automatically registering your main window with an arrow cursor. You can override this behavior just by handling WM_SETCURSOR—in the main or child window.
// handle WM_SETCURSOR in button class 
BOOL CMyButton::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg) 
{
    ::SetCursor(m_hMyCursor);
    return TRUE;
} 
      Whenever the user moves the mouse into your button and the mouse is not captured, Windows sends your button a WM_SETCURSOR message. It passes a window handle—the window over which the mouse points, which in this case is the button itself; a hit test code, one of the HTXXX codes used in WM_NCHITTEST (see Figure 1); and the message ID of the mouse message that triggered the event—for example, WM_MOUSEMOVE. WM_SETCURSOR is your big chance in life to set the cursor. If you do, you should return TRUE to prevent Windows from doing its own processing.
      And what sort of processing would that be? The default window procedure first sends WM_SETCURSOR to the parent window if there is one. If the parent handles WM_SETCURSOR (that is, if it returns TRUE), Windows doesn't do anything more. Message over. Finis. If the parent window doesn't handle WM_SETCURSOR (returns FALSE), Windows gives the child a chance to handle it. If the child also doesn't do anything (returns FALSE), Windows uses the global cursor or, if there isn't one, an arrow.
      What does all this mean for you? It means that when setting the cursor dynamically, you must decide whether to handle WM_SETCURSOR in the child or parent window. Either option could be expedient, depending on the circumstances. Generally it's better to let objects determine their own properties—that is, do it in the child, which in this case would be the button. But that requires deriving a new button class with its own message map and everything else. That's a lot of typing—or clicking, if you're the Class Wizard sort. (Does anyone use the Class Wizard?) If you already have your own button class, then I would say definitely handle WM_SETCURSOR there. But if you don't have your own button class and you're feeling lazy, it's OK to handle WM_SETCURSOR in the dialog. Just don't tell any OO pundits I told you so.

Figure 2 MyCursor and Buttons
Figure 2 MyCursor and Buttons

      I wrote a simple dialog-based app, NoCursor, that illustrates both methods. If you move the mouse over a button (OK or Cancel), the cursor changes to a blue pointing finger (see Figure 2); this feature is implemented by the code in Figure 3 by handling OnSetCursor in the dialog. If, on the other hand, you move the mouse over one of the underlined hyperlinks, the cursor changes to a different pointing finger, the normal hand cursor in Windows (see Figure 4). This feature is implemented in the child class, CStaticLink (see Figure 5). CStaticLink is a general-purpose hyperlink class that appears in my columns frequently. Most of the code in CStaticLink::OnSetCursor deals with getting the proper hand cursor from winhlp32.exe, and is irrelevant as far as setting cursors is concerned—so I excised it. If you're interested in these details, you can download the full source, as always, from the link at the top of this article.

Figure 4 MyCursor and Hyperlinks
Figure 4 MyCursor and Hyperlinks

Q
I've got a library that has some dialogs (and thus some re-source IDs). Now, using this library within a main app, the resource IDs collide with resource IDs in the app. The result is that instead of showing the library's dialog, the app dialog pops up. How can I avoid this? Do I have to set the resource IDs from the library manually?
Hans Zwahlen

A
Alas, this is one of those problems that doesn't have any really satisfying solution, only a few intelligent workarounds. The fundamental problem is that every resource in Windows must belong to some module (EXE or DLL), and resources of a given type within a module can't have the same name or ID. With DLLs, this isn't a problem because your DLL has its very own HINSTANCE handle that distinguishes it from the app; but in a statically linked library, all resources must cohabit the same EXE file like a big extended family. And, as with families, there's always a potential for conflict.
      MFC solves this problem using the 800-pound gorilla approach: "I'm the 800-pound gorilla and you will do what I say." MFC doesn't have any built-in dialogs that I'm aware of, but it has a number of hardwired menu item IDs like ID_FILE_OPEN, which it uses also as resource string IDs for menu prompts. MFC's IDs all fall above 0xE000, and you simply must not use IDs above this value. If there's a conflict, it's your problem.
      That approach may be appropriate for the people who make the operating system, but if you're just a little guy struggling to survive and your customer complains that your IDs conflict with his app, it just won't do to say, "tough." You have to be more accommodating. So what to do?
      One solution I've used is to define all your library's resource IDs as offsets from a base value that defaults to something nice, but which the programmer can override if collisions occur. The values are all defined in a special header file that programmers must include in both the app and the resource file.
// in app's main rc file 
#include "libres.h"  // ID symbols 
#include "libres.rc" // resources 
Here, libres.rc is the resource file with all your library's dialogs and cursors and whatever. The header file, libres.h, defines the IDs used in libres.rc as offsets from a common base ID.
// in libres.h 
#ifndef LIBBASEID 
#define LIBBASEID 2000 // or whatever 
#endif 

#define IDR_FOO  (LIBBASEID+1) 
#define IDR_BAR  (LIBBASEID+2) 
// and so on
•••
      Programmers using your library can easily remap all the resource IDs just by redefining LIBBASEID. For example,
// In app.rc 
#define LIBBASEID 3000 
#include "libres.h" 
#include "libres.rc" 
Now all the IDs start at 3000 instead of 2000. This scheme works well in practice, but it has the possibly onerous drawback of requiring that customers recompile your library to change the IDs. If recompiling the library is unacceptable (perhaps you don't want to give out your source code), there are a couple of other options.
      If a programmer changes LIBBASEID, he only has to recompile your library if it actually uses the symbols IDR_FOO, and so on. That is, only if your library has these symbols hardwired into the source:
BOOL SomeLibFn(...) 
{
    DialogBox(..., IDR_FOO, ...); 
} 
      One way to get rid of the hardwired symbols is convert them to parameters which callers must supply.
BOOL SomeLibFn(..., UINT nDlgID) 
{
    DialogBox(..., nDlgID, ...); 
} 
Now your library doesn't care what values the IDs have, since the app must supply them. Anyone can change LIBBASEID without having—or even being able—to recompile your library, since all IDs are passed in from without. Of course, in this case the programmer writing the app must also edit your .RC file to set the IDs. Not especially elegant.
      A better option is to define your IDs as offsets from a static global instead of a constant #define symbol.
// in header file 
extern UINT LibBaseId; 
#define IDR_FOO (LibBaseId+1); 
#define IDR_BAR (LibBaseId+2); 
// and so on
••• 

// in your main lib module 
UINT LibBaseId = 2000; 
Now if there's a conflict, the application can change LibBaseId during startup (perhaps through a function), and only the app has to be recompiled. Alas, this won't work for .RC files exactly as I've shown it because RC syntax is not full C/C++, only a subset; it doesn't understand extern and UINT. (It has the intellectual capacity of a preprocessor.)
      To make the resource compiler understand what's going on, you need another header file—or, more elegantly—you can use RC_INVOKED to keep everything in one header file.
// in libres.h 
#ifdef RC_INVOKED 
#ifndef LibBaseId 
#define LibBaseId 2000 
#endif 
#else 
extern UINT LibBaseId; 
#endif 

#define IDR_FOO (LibBaseId+1); 
#define IDR_BAR (LibBaseId+2); 
// and so on
••• 
In general, header files included in .RC files can't contain C code, only #define macros.
      Using this new approach, programmers using your library must do two things to remap your resource IDs. First, they must #define LibBaseId to something new before #including libres.h in their main .rc file. Second, they have to initialize LibBaseId to the same value in the application startup code (for example, in CWinApp::InitInstance).
      Finally, there's another, even simpler way to perform conflict resolution on resource IDs: use strings instead of IDs to name your resources.
// in libres.h 
#define IDR_FOO "MyApp_IDR_FOO" 
#define IDR_BAR "MyApp_IDR_BAR"
 ••• 
//etc. 
Using strings is frowned upon because they take space and it's slower to find a string-named resource than an integer-numbered one. If your library has hundreds of resource IDs used for strings or other frequently loaded resources, I recommend sticking with integer IDs and using one of the schemes mentioned earlier. But if all you have is a few dialogs, strings are perfectly fine. No user will ever notice it took five milliseconds longer to load your dialog, and the chances of name collisions are practically nil.
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 January 2001 issue of MSDN Magazine

Page view tracker