Viewing icon files is a simple but useful task, and a prototype for more elaborate utilities.
The Internet offers a wealth of icon files for those who want to spruce up the appearance of their applications. Unfortunately, when there are a lot of icons to choose from, opening each one individually within a viewer (such as a resource editor) becomes tedious and time consuming. So I created an application that would allow me to traverse a directory list and view the contents of the icon files interactively on an area of the application's window. This article describes the general technique; due to space limitations I do not show the source code here, but it is available on the CUJ ftp site (see p. 3 for details on downloading).
Implementation
The File Open common dialog box provides all the functionality needed for this application (drive and directory list manipulation). Since the common dialog boxes are customizable, an area of the dialog box can be used to display the contents of the icon files while traversing the directory list. The steps required to customize the common dialog boxes are detailed in the Windows 3.1 SDK help file. The source files for the common dialog box resource templates can be found in the \MSVC\SAMPLES\COMMDLG directory ending with a .DLG extension. The supplied resource templates do not identify the dialog box control ids; without these ids the resource templates are functionally useless. Fortunately, the SDK SPY utility comes in handy for obtaining the common dialog box control ids. Note that existing controls must not be deleted from the resource templates. The common dialog box routines will not function unless all the original controls are present on the dialog box. If a feature of the common dialog box is not required, simply disable and hide the control. I've added two controls to the resource script, iconview.rc: a group box with the title "Icon," and a frame control (IDC_GRAPHIC_RESOURCE) that will display the contents of the icon files. I have disabled and removed the visible, tab stop, and default flags from the Open button, since they are not needed for this application. I've also changed the name of the Cancel button to "Close" and set the READ ONLY flag on the filename edit box control (control id 1152).
The application code, in iconview.c, consists of a WinMain function and a File Open hook procedure. The hook procedure performs the bulk of the work. Some items worth mentioning about WinMain: I use the undocumented function [1] SwitchToThisWindow to check for a previous instance of the application. SwitchToThisWindow will restore and activate the previous instance of the application. I then set the OPENFILENAME structure flags necessary to use the customized dialog box. These flags are OFN_ENABLETEMPLATE and OFN_ENABLEHOOK. I also identify the hook procedure (FileOpenHookProc) that is called when messages are received by the customized dialog box. I include executable files in the OPENFILENAME filter because this application will also display icons in executable files.
The hook procedure, FileOpenHookProc, is basically just a switch statement, as typically found in simple window procedures. The hook procedure processes the messages WM_INITDIALOG, WM_DESTROY, WM_PAINT, and WM_COMMAND. The hook procedure receives all messages and notifications except WM_INITDIALOG before the dialog box procedure in COMMDLG.DLL.
The core functionality is in the handling of the LBN_SELCHANGE list box notification message, which Windows embeds in a WM_COMMAND message. This section of code determines which file is currently selected, and gets a handle to the associated icon through the Windows API function ExtractIcon:
case WM_COMMAND: switch(LOWORD(wParam)) { case 1120: if (HIWORD(lParam) == LBN_SELCHANGE) { /* Get the current file selection */ dwIndex = SendDlgItemMessage( hwndDlg,LOWORD(wParam), LB_GETCURSEL,0,0L); if (dwIndex != LB_ERR) { /* Select the string */ SendDlgItemMessage( hwndDlg,LOWORD(wParam), LB_GETTEXT, (WPARAM) dwIndex, (LPARAM) (LPCSTR) szBuffer); /* Update the graphic display */ InvalidateRect( hGraphicWnd,NULL,TRUE); UpdateWindow(hGraphicWnd); /* Get handle to icon window device context */ hdc = GetDC(hGraphicWnd); /* Free previous icon if any */ if (hExtractedIcon) DestroyIcon(hExtractedIcon); /* Extract new icon, draw on obtained device context */ hExtractedIcon = ExtractIcon(hInst,szBuffer,0); DrawIcon( hdc,nIconX,nIconY, hExtractedIcon); /* Free the device context */ ReleaseDC(hGraphicWnd,hdc); } } break; }The third parameter to ExtractIcon is the index number of the icon to be extracted. Since the file may be an executable file, it may contain multiple icons, so I specify a zero index (first icon) as a parameter.
The hook procedure always returns a FALSE value after processing a message, thus giving the original dialog box procedure a chance to process the message as well.
Caveats
The File Open dialog box has undesirable default behaviors associated with certain actions such as pressing the return key or double-clicking on a filename. In some instances pressing return causes the dialog box to close. Also, when a user selects a file from a File Open dialog box, pressing return or double-clicking a filename will trigger a click on the OK button. The application still responds to these actions even though I've disabled and hidden the OK button. This default behavior could be prevented by subclassing the dialog box controls and processing return key and mouse double-clicks.
The code provided with this article could easily be extended to display other graphics file formats such as bitmap files. Obviously a little more code is necessary, but the core functionality is already present. o
Reference
[1] Andrew Schulman, David Maxey, and Matt Pietrek. Undocumented Windows: A Programmer's Guide to Reserved Microsoft Windows API Functions (Addison-Wesley, 1992).
Pat Paternostro is a Senior Associate for Tri-Com Consulting Group located in Glastonbury, CT. Tri-Com provides programming services for a wide variety of development tasks. You can reach Pat at ppaternostro@tricomgroup.com.