This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.

MSDN Magazine

FileType Icon Detector App, Custom Context Menus, Unreferenced Variables and String Conversions

Paul DiLascia

Download the code for this article: CQA1100.exe (56KB)

Q In your C++ Q&A column in the November 1999 issue of MSJ, you explained how to extract the icon from an executable. How can I find the icon associated with a specific document file type?

Peter Diehl
Germany

A Good question. The answer depends on whether you want the icon for a specific file or a general file type. If you have a particular file in mind, say C:\MyFiles\SomeFile.zzp, then you can call ExtractAssociatedIcon to get the icon associated with it. But this function only works for an actual file that exists on disk (or somewhere accessible).
      If you want to find the icon associated with a file type in general, you have to read the registry. For example, to find out what icon is used for .bmp files, you look at the registry entry HKEY_CLASSES_ROOT\.bmp, which on my machine has the value "bitmapFile". You then have to look in HKEY_CLASSES_ROOT\bitmapFile, at the key DefaultIcon. On my machine, the value is:

  HKEY_CLASSES_ROOT\bitmapFile\DefaultIcon =
"C:\PROGRA~1\ACCESS~1\MSPAINT.EXE,1"

 

      So the icon for .bmp files is the first icon in C:\Program Files\Accessories\MSPAINT.EXE. In general, HKCR\.foo has a value like foofile, and HKCR\foofile\DefaultIcon specifies the icon.
      But waitâ€"there's more! Sometimes the entry for .foo is something like "SuperApp.Document.5", meaning version 5 of SuperApp documents, and HKCR\SuperApp.Document.5 doesn't have a DefaultIcon, but it has a key CLSID = {long-ugly-hex-guid}. That's your cue that foofiles are COM objects with class ID {same-long-ugly-hex-guid}. So then you have to peer in HKCR\CLSID\{same-long-ugly-hex-guid}\DefaultIcon to get the icon. Sheeshâ€"what a pain! But even that isn't the whole story because foofiles could have a custom icon handler, implemented as a shell extension. You have to check for this too, load the icon handler, call IShellIcon or IExtractIcon, andâ€"hey, this is getting ridiculous!
      Fortunately, there is a better way (and I only ran through all that boring registry stuff so you'd appreciate it): SHGetFileInfo. This handy-dandy function gets all kinds of poop on a file. It has a clever option, SHGFI_USEFILEATTRIBUTES, that lets you pass a spurious file name, such as mumble.txt. SHGetFileInfo gets the info you want even if the file doesn't exist.

    SHFILEINFO shfi;
memset(&shfi,0,sizeof(shfi));
SHGetFileInfo("foo.bmp",
FILE_ATTRIBUTE_NORMAL,
&shfi, sizeof(shfi),
SHGFI_ICON|SHGFI_USEFILEATTRIBUTES);

 

      To show how it works, I wrote a little program, FileType (see Figure 1), that displays a dialog with an edit control where you can type an extension like txt or jpg and see the associated icon. The implementation is straightforward: when the user types a new extension, Windows® sends an EN_UPDATE notification; CMyDialog calls SHGetFileInfo to get the icon, then shows it in a static icon control. For details, you can download the source code from the link at the top of this article.

Figure 1 FileType In Action
Figure 1 FileType In Action

Q Why isn't a WM_INITMENUPOPUP message generated when you right-click an edit control?

Fraganestis Anestis

A I can't tell you why there isn't one, but I can confirm it's true and show you how to work around it. I've run into this problem myself on occasion; it usually comes up when you want to subclass an edit control to add your own items to the standard context menu (see Figure 2), or adjust the state of the standard edit items.

Figure 2 MTS
Figure 2 MTS

      Normally you'd write a WM_INITMENUPOPUP handler for your edit control, add your items, and be done with it. But as you discovered, edit controls don't send WM_INITMENUPOPUP. The edit control must be calling TrackPopupMenu with a null HWND handle and/or TPM_NONOTIFY, which tells the menu not to send notifications. It's possibleâ€"and again I'm only guessingâ€"that the authors were trying to improve performance by reducing message traffic. It's hard to remember, but Windows 1.0 and 2.0 ran on machines with 640KB of memory and an 8MHz processor! (Did edit controls actually have a context menu in those days? Does anyone remember?)
      In any case, suppose you want to add your own menu items to the edit control context menu. How do you do it? Alas, you have no choice but to reinvent the wheel. Look on the bright side: it's easy. I wrote a little class, CEditMenuHandler, that does all the work. All you have to do is use it. To show how, I modified the edit control in the FileType program from the previous question by adding three "hardwired" file types to the context menu (see Figure 3).

Figure 3 Menu
Figure 3 Menu

      To use CEditMenuHandler, you have to do three things. (Well, maybe four if you count designing the menu itself.) First, instantiate the handler.

  class CMyEdit : public CEdit {
protected:
CEditMenuHandler m_editMenuHandler;
virtual void PreSubclassWindow();
};

 

      Next, you have to install it. You can do it in OnCreate, but if your edit control lives in a dialog, PreSubclassWindow is the place since you don't normally create dialog controls; you subclass them instead. When you install the handler, you give it the ID of your context menu:

  void CMyEdit::PreSubclassWindow()
{
//IDR EDITMENU is my context menu
m_editMenuHandler.Install(this, IDR_EDITMENU);
}

 

      So far, so good. Now the handler is installed and you're ready to call the only two functions you need to use. OnUpdateEditCommand updates the menu items; OnEditCommand handles the commands.

   // in CMyEdit message map
ON_COMMAND_RANGE(ID_EDIT_FIRST,
ID_EDIT_LAST, OnEditCommand)
ON_UPDATE_COMMAND_UI_RANGE(ID_EDIT_FIRST,
ID_EDIT_LAST, OnUpdateEditCommand)

void CMyEdit::OnUpdateEditCommand(CCmdUI* pCmdUI)
{
m_editMenuHandler.OnUpdateEditCommand(pCmdUI));
}

void CMyEdit::OnEditCommand(UINT nID)
{
m_editMenuHandler.OnEditCommand(nID);
}

 

      CEditMenuHandler does all the work. It handles Cut, Copy, Paste, and other actions. It enables/disables the menu items appropriately, depending on whether text is selected or there's something to paste, and so on. The handler functions return TRUE if the command was handled; otherwise they return FALSE so you can handle other edit commands yourself if you want. For example, CMyEdit has separate handlers for the TXT, BMP, and JPG commands.

  // in CMyEdit message map
ON_COMMAND(ID_FILETYPE_TXT, OnFiletypeTXT)

void CMyEdit::OnFiletypeTXT()
{
SetWindowText(_T("txt"));
SetSel(0,-1);
}

 

CMyEdit passes edit commands to CEditMenuHandler and handles the rest itself. Ditto for updating the menu items. Everything works smooth as satin.
      Of course, there's a fair amount of code to make it soâ€"in fact, it may seem like too much when you think that all CEditMenuHandler does is reinvent Windows! Oh well, that's life. But CEditMenuHandler makes good use of preexisting code. To wit, a class called CPopupMenuInitHandler, which I described in the COMToys article in the November 1999 issue of MSJ. CPopupMenuInitHandler has nothing to do with edit menus per se; it lets you use MFC's CCmdUI menu update mechanism to update the context menu for any window. MFC's wonderful menu update mechanism is implemented entirely within CFrameWnd, so only frame windows can use it. If you have some other kind of windowâ€"an edit control, for exampleâ€"MFC doesn't handle WM_INITMENUPOPUP and do all the good CCmdUI stuff. What a bummer! But CPopupMenuInitHandler works with any CWnd object. It relies on yet another class, CSubclassWnd, which lets you subclass any CWnd object.
      CPopupMenuInitHandler intercepts WM_INITMENUPOPUP on behalf of your window, and then does the MFC menu update thing. All you do is instantiate the CPopupMenuInitHandler and install it. Then you can add ON_UPDATE_COMMAND_UI handlers to update your window's context menuâ€"just like for frame windows. Pretty cool. For more details, see the COMToys article mentioned previously or the source code itself.
      Once you have CPopupMenuInitHandler, CEditMenuHandler is a piece o' pie. Figure 4 shows the code. To make life as pleasant as possible (you have lots to worry about, I know), I threw in a WM_CONTEXTMENU handler, so all you have to do is supply a menu ID and CEditMenuHandler displays it when the user right-clicks, as you can see in Figure 3.
      The rest is perfectly straightforward. OnUpdateEditCommand updates the menu items appropriately. Here's an example:

  // in CEditMenuHandler::OnUpdateEditCommand
switch (nID) {
case ID_EDIT_PASTE:
pCmdUI->Enable(::IsClipboardFormatAvailable(CF_TEXT));
•••

 

That is, CEditMenuHandler enables the Paste command when there's some text to paste. Note that CEditMenuHandler expects you to use the standard MFC IDs, ID_EDIT_CUT, ID_EDIT_COPY, and so on in your menu. To handle the commands themselves, CEditMenuHandler calls CEdit::Cut, CEdit::Copy, and friends, which just send WM_CUT, WM_COPY, and so on to the edit control. CEDIT::Cut, Copy, and Paste really belong in CWnd since any window can implement themâ€"but in practice, the only Windows controls that do implement them are edit controls and comboboxes (which contain an edit control).

Q Here's a (maybe dumb) question about macros: I had a look at the ATL string conversion macros, including W2A, and there's something I don't understand. In order to use them, you have to put USES_CONVERSION at the beginning of the function to initialize some local variables. Fine, but when you look at that macro, it produces something like:

  // from atlconv.h
#define USES_CONVERSION \
int _convert; _convert; \
UINT _acp = GetACP(); _acp; \
LPCWSTR _lpw; _lpw; \
LPCSTR _lpa; _lpa

 

      My question is why do they use "int x; x;"â€"the declaration followed by the variable? What am I missing?

Nicolas Tenoutasse

A Your question isn't dumbâ€"other readers may find the prob-lem perplexing, too. The simple answer is: to suppress compiler warning messages. If you write

  int x;
  

 

alone, and never use x, the compiler will complain "unreferenced local variable: x" if you have warnings turned to the max. So in order to avoid nuisance warnings, USES_CONVERSION references the variables it declares.

  int x; // declare
x; // use it

 

      In the old days before C++, programmers sometimes did a similar thing with function arguments in C to avoid "unreferenced formal parameter" or whatever esoteric gobbledygook the compiler used to phrase its gripe.

  void MyFunc(int x, char y)
{
x;
y;
•••
}

 

      Now, of course, you can achieve the same thing more elegantly by writing:

  // parameter x not used
void MyFunc(int /* x */)
{
•••
}

 

That is, you declare the parameter, but don't give it a name. Alas, you can't do that with a local variable; you have to explicitly reference it. Doing so won't add any instructions to your code. At worst it may add a few extra bytes to your stack frame (to reserve space for x). Smart compilers might not even bother, realizing x is never "really" usedâ€"though inquiring minds may wonder: if the compiler is smart enough to know you never use x, why does it complain? Answer: because fastidious programmers (and programming ability is correlated with anal-retentiveness) use the warnings to remind themselves of variables that become obsolete when they delete portions of their code. The warnings were especially useful in the C days, when you had to declare your variables at the top of every function, away from the code that uses them.
      So much for your question. But while I'm on the subject, let's stop to ponder the following question: why implement some-thing like USES_CONVERSION in the first place? That is, why have a macro like W2A & Co. that requires yet another macro to declare its variables; why not just declare the variables in W2A?

  #define W2A(x) \
int _convert; _convert; \
••• etc

 

      Well, obviously this won't work because if you use W2A twice, you get a duplicate variable. OK, so why not put the whole macro in squiggle braces to create a new scope?

  #define W2A(x) Q{ \
int _convert; _convert; \
••• \
}

 

That solves the naming conflict, but now you can't write

  DoSomething(W2A(pwstr));
  

 

There's no way to return a value from a code block, so now you can't pass W2A in a function call. Pretty useless. Well then, how about an inline function?

  inline LPCSTR W2A(LPWSTR w) {
int_convert;
•••
}

 

This solves the scope problemâ€"any variables W2A needs can be declared inside the function, in their own scope, without requiring another macro. It also provides a way to return a value so you can use W2A(x) in a function call or assignment. But this approach won't work either because the real reason for W2A and the other macros is more complicated.
      Whenever you do a Unicode conversion, you can't convert your strings in place, you have to allocate a temporary string to receive the converted bytes. Typically you allocate a string by calling new.

  int len = MultiByteToWideChar(..., 
mystr, NULL, 0); // get length
LPWSTR p = new WCHAR[nLen]; // allocate
MultiByteToWideChar(...,p,len); // convert
SomeCOMFunction(p); // use it
delete [] p; // destroy

 

      This code is not only boring, it's inefficient: you have to call MultiByteToWideChar twice (once to calculate the length, again to actually convert), and you have to allocate p from the heap, which is slow. You can solve the first problem by allocating 2*len bytes, where len is the length of the ASCII stringâ€"but what about the second problem?
      If you look at how A2W expands, it goes something like this:

  // simplified
#define A2W(s) \
_len = 2*strlen(s);
AfxA2WHelper((LPWSTR)alloca(_len);

 

AfxA2WHelper is a helper function that calls MultiByteToWideChar. A2W uses the 2*len trick to avoid calling MultiByteToWideChar twice. But the really clever thingâ€"the whole point of A2W and the other conversion macrosâ€"is this: they don't call new to allocate a temp string. Instead, they call allocaâ€"which allocates bytes from the stack, not the heap. This is very fast since all the compiler has to do is increment the stack pointer. No function call, no manipulating memory blocks. It also avoids memory fragmentation, and there's no need to call delete because the memory is automatically freed when control leaves the scope from which alloca was called. And that's precisely why A2W can't be an inline function; if it were, the temp string created by alloca would be destroyed before it returned, and you'd end up calling SomeCOMFunction (to use the example) with a deleted string.
      A2W must call alloca from the same scope from which it's invokedâ€"so A2W must be a macro, not a function; and therefore it needs another macro, USES_CONVERSION, to declare _len and, in real life, some other variables I've omitted for simplicity. It all makes sense when you examine it carefully, and anyone attempting to write a set of macros like W2A that allocate memory from the stack would end up with something more or less the same.
      By the way, you can use alloca any time you want to grab some temporary memory quickly. It's quite common to see code like:

  char* p = new char[len];
DoSomething(p);
delete [] p;

 

This sequence can be replaced more efficiently with:

  char *p = (char*)alloca(len);
DoSomething(p);
// no need to delete p!

 

      Of course, you might see a nasty message box if you try to grab more bytes than your stack has. There are a few other restrictions, tooâ€"read the documentation for details.
      The moral of this story is: whenever you find something strange in MFC or ATL or anywhere, it's a good idea to explore. You'll probably discover something useful.

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 https://www.dilascia.com.

From the November 2000 issue of MSDN Magazine.