C++ HOWTO, Republican Guide to Linux and C++ Essential Questions and Answers
Copyright © Marcel Lambert

C++ Howto Questions and Answers

How to enable MFC edit control to accept drag-and-drop files?

Details

One might suggest the following answer:

  • Set "Accept Files" property to True in Properties Window for the edit control, which is False by default.
  • Add WM_DROPFILES handler to the message map of the dialog that hosts the edit control.

However this seemingly standard routine does not provide DAD capabilities for the edit control. You can additionally set "Accept Files" property for the dialog that hosts the child edit control to enable DAD for the whole dialog area, but this DAD design would mislead the user a little bit since DAD icon would show up not only over the edit control, but over the whole dialog area. The right solution to enable DAD for the edit control only is to add WM_DROPFILES handler to the edit control itself and you need to subclass edit control to handle control messages in own procedure. The code from the MFC_EditWithDAD sample shows how to implement subclassing starting from boilerplate MFC dialog application with edit control added in dialog template in the Resource Editor:

  • Add a class CEditWithDAD derived from CEdit. You can use the code that VS.NET generates when deriving from a standard MFC class:
    // CEdit is subclassed to handle dropped files.
    class CEditWithDAD : public CEdit
    {
        DECLARE_DYNAMIC(CEditWithDAD)
    
    public:
        CEditWithDAD();
        virtual ~CEditWithDAD();
    
    protected:
        afx_msg void OnDropFiles(HDROP hDropInfo); // added manually
        DECLARE_MESSAGE_MAP()
    };
    
  • Add CEditWithDAD class variable, m_ctlEditWithDAD, to the dialog class.
  • Add WM_DROPFILES handler, ON_WM_DROPFILES(), to the message map of the CEditWithDAD and its default function OnDropFiles:
    // Works for single files and for a folder.
    void CEditWithDAD::OnDropFiles(HDROP hDropInfo)
    {
        TCHAR szFFN[MAX_PATH];
    
        // MSDN (second parameter): "If the value of the iFile 
        // parameter is between zero and the total number of files
        // dropped, DragQueryFile copies the file name with the 
        // corresponding value to the buffer pointed to by the 
        // lpszFile parameter."
        DragQueryFile(hDropInfo, 0 /* iFile */, szFFN, 
                                      sizeof(szFFN)/sizeof(TCHAR));
        DragFinish(hDropInfo);
    
        // Update text in the edit control.
        SetWindowText(szFFN);
    }
    
  • Subclass "normal" edit control which was previously positioned in the dialog template in the Resource Editor as:
    BOOL CMFC_EditWithDADDlg::OnInitDialog()
    {
        <...>
        // Subclass edit control.
        m_ctlEditWithDAD.SubclassWindow(
                                     GetDlgItem(IDC_EDIT)->m_hWnd);
        
        return TRUE; 
    }
    

That's all. Now you can DAD (drag-and-drop) files to the edit control to check that the full file name is automatically inserted with the drop. The same code works for folders. Notice that the use of SubclassWindow has the advantage for it allows to use positioning of the edit control in the dialog template.

How to get CHM help file version information?

Details

To start with, there is no standard method to read CHM file version information since it is not supposed to be in CHM file by design. Right-click any CHM to verify that there is no Version tab. There are no options in Microsoft Help Workshop to add version information.

Is there a simple possibility to add version information and read it in Win32 or MFC application? The answer is yes: Microsoft compiler for help files that produces CHM files does not encode file names included into a help project. Hence there is a possibility to add version information to a name of HTM file and read this information from an application.

Microsoft Help Workshop compiles help files into CHM file (compiled help file) that is essentially an ANSI text file. Look what Notepad++ shows:

It shows a plain name of HTM file added to help project. The file name includes version information. Then the method to add read version information is the following:

  • In Microsoft Help Workshop, add _Version_.htm file. The file can be empty and there is no need generally to add it to Table of Contents (HHC file), the sole purpose of it is to keep version information in its file name.
  • In your application read CHM file as you would read any ANSI file, search for "<projectname>_Version_<x.x>.htm" string and parse it to extract version information.

This method is demonstrated in Win32_GetCHMVersion sample application. In this sample the About dialog shows version information in the static field.

As the following code shows it uses ReadFile to get CHM file content into a buffer, and then searches for the version string. The only little complication is that CHM file contains many NUL characters and therefore C-functions that are used to find substrings or characters in a string (like strstr, strchr) can not be used. In this sample the memchr is used to find the first character of the string in raw CHM file.

In general, the steps to employ this method for an application are the following:

  • Add <projectname>_Version_<x.x>.htm file with version information to your existing Help Project file (HHP file) and compile it.
  • Modify the AboutDlg_OnInitDialog function updating the location of CHM file, the search character (the first character in the name of added version information file), and adjusting the size of szBuffer according to the comments below. Update search string and constants in the code.
  • The result is the szVersion string that is shown in the sample in the About dialog. Customize how the result is handled.
BOOL AboutDlg_OnInitDialog(HWND hDlg)
{
    // Help file is supposed to reside in the project root folder 
    // while the executable is in the Release or Debug folder.
    char szCHMFileName[] = "..\\GetCHMVersion.chm"; 
    int  ch = 'G';  // search character
    HANDLE hCHMFile;
    // The buffer should be big enough to hold characters beyond 
    // the "GetCHMVersion_Version_1.0.htm" string in CHM file, 
    // but generally less than CHM file size. 
    char szBuffer[2000]; 
    DWORD dwBytesToRead = sizeof(szBuffer);
    DWORD dwRead;
    LPBYTE  lpCurr = NULL;
    int nSearchDepth;
    int nNextPos;
    LPBYTE  lpStart = NULL;
    char szTarget[100];
    char szVersion[10];
    char* pszVersion = NULL;
    BOOL bFound = FALSE;

    hCHMFile = CreateFile(szCHMFileName, GENERIC_READ, 
        FILE_SHARE_READ, NULL /* security is off */,          
        OPEN_EXISTING /* opens file if exists or fails */,
                      FILE_ATTRIBUTE_NORMAL /* normal use */, NULL);

    if (hCHMFile != INVALID_HANDLE_VALUE)
    {
        ReadFile(hCHMFile, szBuffer /* out */, 
                  dwBytesToRead /* in */, &dwRead /* out */, NULL);

        // Since the szBuffer contains many NUL characters, memchr 
        // is used to search for "GetCHMVersion_Version_<x.x>.htm" 
        // string. The memchr-search is enabled up to the end of 
        // the buffer (the exact position where the search must be 
        // ended is not essential).
        nSearchDepth = sizeof(szBuffer); 
        lpCurr = (LPBYTE)memchr(szBuffer, ch, nSearchDepth);
        // Return FALSE if the search character was not found.
        if (lpCurr == NULL) return FALSE;
        // Calculate the position next to the found character
        // relative to the beginning of the buffer.
        nNextPos = (int)((char*)lpCurr - szBuffer + 1);

        while (nSearchDepth > 0)
        {
            // The new position to start search.
            lpStart = lpCurr + 1;
            // The size of remaining chunk of characters that 
            // were not searched.
            nSearchDepth = nSearchDepth - nNextPos;
            lpCurr = (LPBYTE)memchr(lpStart, ch, nSearchDepth);
            // Return FALSE if the search character was not found.
            if (lpCurr == NULL) return FALSE;
            nNextPos = (int)((char*)lpCurr - (char*)lpStart + 1);

            // Check if found position corresponds to 
            // "GetCHMVersion_Version_<x.x>.htm" string, get the 
            // version, and if not, continue the search. First, 
            // extract the 29 characters that might be 
            // "GetCHMVersion_Version_<x.x>.htm" string (without 
            // NUL since anyway szTarget is NUL-terminated when 
            // initialized).
            strncpy(szTarget, (char*)lpCurr, 29); 
            if (strncmp(szTarget, 
                 "GetCHMVersion_Version_", 22) == 0 /* equal */)
            {
                bFound = TRUE;
                break; 
            }
        } // while cycle

        if (bFound)
        {
            // Get the version string that is in  format.
            pszVersion = szTarget + 22;
            *(pszVersion + 3)  = '\0';
            strcpy(szVersion, pszVersion);

            // Show the version string in the About dialog.
            SendMessage(GetDlgItem(hDlg, 
                IDC_ABOUT_STATIC_VERSION), 
                   WM_SETTEXT, 0 /* not used */, (LPARAM)szVersion);
            return TRUE;
        }

        CloseHandle(hCHMFile); 
    }
    else
    {
        MessageBox(NULL, "CHM Help file was not found.", 
                                 "CHM Version", MB_ICONINFORMATION);
    } // hCHMFile check

    return FALSE; // either CHM file or version string is not found 
}

The sample is limited to <x.x> version strings. However the code that is easy to update to read version strings like 1.28 or 10.124 or 1.15.23.

How to provide context help ("?" style help) for an application?

Details

Have you ever noticed a question mark in the title area of virtually any Windows XP dialog next to close button? The screenshot below is an example from the application as familiar as WordPad. In the WordPad's Print dialog when you click the question mark (the cursor takes immediately a question shape), then click a control (or text) in question - a yellow (typically) popup window brings up the context help.

This is an example of context help in action. In general, the following context help support can be implemented:

  • "?" Help: click with "?"-shaped cursor on the control in question after you click "?" in the title area of a dialog.
  • SHIFT+F1 Help: when control has focus SHIFT+F1 brings up the same context help as for "?" Help.
  • "What's This?" Context Menu Help: when control has focus right-click on the control brings up "What's This?" window. The following click on the window brings up the same context help as for "?" Help and SHIFT+F1 Help. You can check out "What's This?" help with the WordPad application.
  • SHIFT+F1 Help for the Menu Items: when menu item is active SHIFT+F1 bring up the context the menu item. This usually does not work for root menu items and menu items that have subitems. Notably typical Microsoft appications do not always have this type of context help support. For example, SHIFT+F1 Help for menu item is not implemented for Microsoft's WordPad and Notepad applications. On the contrary, Microsoft Word, Excel, Outlook 2000 support this type of help.
What about F1 help and its relation to the context help? The answer is that with native Windows XP and Microsoft Office 2000 applications F1 would typically end up the same way as "?" Help, SHIFT+F1, and "What's This?" help for a control that has focus. "What's the point?" would be fair to ask. Would it be nicer that F1 is responsible for "bigger scale" help by opening up a specific topic from CHM file for an active dialog, and context help ("?" Help, SHIFT+F1, "What's This?" are all essentially making the same thing) is responsible for "smaller scale" help for a particular control?

The following download provides an example of F1 and context help implementation in the MFC application. The application's Help system samples “bigger scale” overview with CHM file invoked with F1 and “smaller scale” hints for the controls and menu items in question invoked with different mehods of context help. At the center of the sample is CContextHelpMappings class developed to facilitate providing context help for typical Win32 and MFC applications. The details of the logic are the following:

  • F1 should always invoke CHM file (MFC_SHIFT_F1_Help.chm) except for the case when cursor is inside the rectangle of the control that has focus. In this case, the context help for the control is shown.
  • The "?" Help, SHIFT+F1, "What's This?" context types of help should bring up the same context help windows based on ID of the control in question.
  • There is some difficulty with intercepting F1 since “?”, SHIFT+F1, F1 are all generate WM_HELP message handled in MFC with OnHelpInfo handler. Therefore the check is made in the beginning of OnHelpInfo if the WM_HELP relates to F1. In the result, F1 help is handled in the separate handler (OnF1Help function of the sample) of the application class albeit all help (F1 and context help) originally handled with MFC's OnHelpInfo handler. The method used in the sample to sort out F1 events figures if the cursor is inside the rectangle of a control that has focus and if not it decides that this a general help request for F1. An alternative method would be to use MFC's PreTranslateMessage to analyze help request.
  • The CHM file does not (although it can in general) handle context help support. The rationale is that the frequently more convenient context help options are: (a) keep context help tightly bundled with an application (user/development perspective), (b) keep context help hard-coded (developer perspective). For example, when CHM file is missing (not yet downloaded, deleted) the context help is still available. Notably hard-coded context help helps to reduce download traffic since CHM file can frequently reach the size of the associated application or bigger. In this case, one can implement the option to download CHM help while not including CHM file into the setup.

Let's suppose an application has many dialogs and windows that require context help support. Where does one handle context help? In general, it is reasonable to handle context help in one location such as application class. By this we avoid duplicating context help functionality in each dialog and save quite a bit of efforts. The following is CContextHelpMappings class that helps to handle all context help in typical Win32 or MFC application. Notice that according to the CContextHelpMappings template each control has a pair of IDs. The m_MapMainDialog array keeps all these ID pairs in one place. The first ID in each pair, is control ID (with "IDC_" prefix) and the second is help ID with "IDH_" prefix. You should add help IDs manually according to the control namings e.g. for IDC_MD_BUTTON1 add IDH_MD_BUTTON1.

#pragma once

class CContextHelpMappings
{
public:
    // Initialize the context help mappings in the constructor.
    CContextHelpMappings()
    {
        // Mappings for the main dialog. 
        m_MapMainDialog[0].nControlID = IDC_MD_BUTTON1;
        m_MapMainDialog[0].nStringHelpID = IDH_MD_BUTTON1;
        m_MapMainDialog[1].nControlID = IDC_MD_BUTTON_HELP;
        m_MapMainDialog[1].nStringHelpID = IDH_MD_BUTTON_HELP; 
        <...>

        // Mappings for the options dialog.
        m_MapOptionsDialog[0].nControlID = IDC_OD_BUTTON_OPTION1;
        m_MapOptionsDialog[0].nStringHelpID = IDH_OD_BUTTON_OPTION1;
        m_MapOptionsDialog[1].nControlID = IDC_OD_BUTTON_OPTION2;
        m_MapOptionsDialog[1].nStringHelpID = IDH_OD_BUTTON_OPTION2; 
        <...>		
    }
		
    // Set the size of arrays. Notice that "const static" helps 
    // initialize outside the constructor, but inside the class and 
    // keeps initialization in the one place. 
    const static short m_nMapSizeMainDialog = 5;
    const static short m_nMapSizeOptionsDialog = 5;

    struct ContextHelpMap
    {
        DWORD nControlID; 
        DWORD nStringHelpID;
    } m_MapMainDialog[m_nMapSizeMainDialog], 
                           m_MapOptionsDialog[m_nMapSizeOptionsDialog];
};

The next are the more elaborative steps to implement context help using CContextHelpMappings class in MFC application (Win32 steps are essentially the same):

  • Add CContextHelpMappings member variable to the application class so that the variable would be accessible from any MFC dialog with theApp reference:
    #include "ContextHelpMappings.h"
    
    class CMFC_SHIFT_F1_HelpApp : public CWinApp
    {
    // Constructor(s) and destructor (constructors).
    public:
        CMFC_SHIFT_F1_HelpApp();
    
    // Access member variables (attributes).
    public:
        CContextHelpMappings m_ContextHelp;
    <...>
    };
    
    extern CMFC_SHIFT_F1_HelpApp theApp;
    
  • Add to the resource.h the values for the new help IDs e.g. for IDC_MD_BUTTON1 (value 123) add IDH_BUTTON1 (value 2123). The value for the help ID is arbitrary as long it is unique. In this sample, the value for help ID is the value of relative control ID plus 2000. This keeps help IDs ordered.
  • Add help strings to the RC file. To keep all strings in one place is one of the basic purposes. You can edit RC with any text editor adding strings like:
    STRINGTABLE 
    BEGIN
        IDH_MD_BUTTON1             "Test button."
        IDH_MD_HELP_BUTTON         "Show CHM help."
        <...>
    END
    
  • In each dialog class where you want ot implement context help add OnHelpMenu handler, which (a) handles all WM_HELP messages such as related to "?" Help, SHIFT+F1, (b) redirects to OnF1Help messages related to F1, (b) process messages redirected from OnContextMenu that handles "What's This?" context help. Since "What's This?" help is originally handled (WM_CONTEXTMENU message) with OnContextMenu handler, you should add it too. The sample shows the implementations of OnHelpMenu and OnContextMenu functions that should be a bit customized for each dialog to reflect CContextHelpMappings's names of variables for context menu IDs mappings in the dialog.
  • Finally, add to the application class OnF1Help handler to handle F1 help so that that each dialog can access the handler.

How to set background color for owner-draw static control?

Details

When you need to provide color pickup option for an application, you may want to show current color within some area without invoking color selection dialog, and show newly selected color within the same area. One option to make this is to use small colored bitmaps and set a bitmap with specific color for static control. Another option, which is more economic (since does not require additional bitmaps), is to paint the background area of static control with specific color. Here is how the result looks like:

On the left is static control, and on the right is button, which invokes standard Windows color selection dialog.

You can examine the Win32 code here. In a nutshell you add SS_OWNERDRAW style to static control, and handle WM_DRAWITEM message in window procedure of static control's owner window (main window in sample application) to paint its background color. In sample application the static control is created with CreateWindow, but you can surely use Visual Studio dialog template, drag-and-drop static control and add SS_OWNERDRAW style to RC file.

Sounds simple? Indeed it is a fairly straightforward method, which has however some pitfalls if you're are not careful about details:

  • You should adhere to fairly standard receipt of painting an area. That is, firstly, you save current device context before painting, and return it to the system when finished with painting. Secondly, you should backup in similar way current system brush object (or more generally GDI objects) used for painting background, and restore system brush when finished with painting. In between, you use device context provided with painting method that is in our case represented by the device context provided with WM_DRAWITEM message to paint an area:
    	
    case WM_DRAWITEM:
            if (wParam == IDC_STATIC_OWNERDRAW) 
            {
                    lpdis = (LPDRAWITEMSTRUCT) lParam; 
                    if (lpdis->CtlID == IDC_STATIC_OWNERDRAW)
                    {
                            // Standard painting procedure is to save   
                            // system DC, brush (SaveDC, SelectObject)   
                            // before using custom, and restore to saved when  
                            // finished with SelectObject, RestoreDC.
                            nSavedDC = SaveDC(lpdis->hDC); 
    
                            hBrush =  CreateSolidBrush(g_clrBkgroundColor); 
                            hOldBrush = (HBRUSH)SelectObject(lpdis->hDC, 
    						hBrush);
                            FillRect(lpdis->hDC, &lpdis->rcItem, hBrush);
                            SelectObject(lpdis->hDC, hOldBrush);
    
                            DeleteObject(hBrush);
                            RestoreDC(lpdis->hDC, nSavedDC);
    
                            return TRUE;
                    }
            }
            break;
    
    Notice that Select function serves two purposes: loads newly created brush and returns system brush, which must be saved for later restore. This cooking receipt for painting shows that when finished with painting, we still need to inform the system that our own work is done by returning TRUE so that the system does not make own painting.
  • Do not presume that if you set owner-draw style for static control with SS_OWNERDRAW style, and other controls do not have this sort of style, WM_DRAWITEM will not be generated while painting client areas for other controls e.g. button control. In fact, try to comment this line in the handler above:
    	
    case WM_DRAWITEM:
            // if (wParam == IDC_STATIC_OWNERDRAW) 
            lpdis = (LPDRAWITEMSTRUCT) lParam; 
            if (lpdis->CtlID == IDC_STATIC_OWNERDRAW)
    
    As the result, the sample application may crash (access violation reading error) when you pickup a color from color dialog or even when invoke About dialog and click OK. When repainting the system sends also WM_DRAWITEM message for button control (you can check out wParam, which is the control ID, to see that) even though the button does not have owner-draw style. For some reason the WM_DRAWITEM message for button is spurious, and DRAWTIMESTRUCTURE members are not defined as seen from the debugging output (VS.NET 2003), which corresponds to handling of WM_DRAWITEM sent with wParam containing button ID:

    The access violation can be easily avoided by sorting out WM_DRAWITEM messages to handle only those messages where wParam equals ID of static control.

  • When you finished with selecting a color from colors dialog and click OK, force application to repaint static control with newly selected color. You can do in different fashions using InvalidateRect, RedrawWindow, SetWindowPos, or sending WM_DRAWITEM to static control explicitly with SendMessage provided that DRAWITEMSTRUCT is properly filled. For example as in the sample:
    RedrawWindow(g_hwndStatic_OwnerDraw, 
        NULL /* invalidate entire client area */, 
             NULL /* invalidate entire client area */, RDW_INVALIDATE); 
    
  • The g_clrBackgroundColor variable that stores color from color selection dialog should be global, not local to WndProc, since WM_DRAWITEM message relative to owner-draw static control can be caused not only due to RedrawWindow when color selection dialog is closed, but also due to main window resize, About dialog close.

The sample is generated with VS.NET 2003 (can be imported to VS.NET 2005 with no changes in source files). You can see changes made to skeleton application generated with Win32 Wizard of VS.NET 2003 by using UI-enabled compare tool such as Beyond Compare 3:

How to animate Win32 window?

Details

You can see how the animation works in sample Win32 application. To start animation choose animation type from Commands menu:
  • Slide left to right
  • Slide right to left
  • Slide top to bottom
  • Slide bottom to top
  • Collapse inwards
  • Collapse outwards
  • Blend (hide)
  • Blend (show).

The sample demonstrates the animation that realized with AnimateWindow Win32 function. The steps to make animation in MFC applications are essentially the same. To make AnimateWindow function operate correctly there are a couple of essential points to notice:

  • All painting relative to AnimateWindow occurs in respond to WM_PRINTCLIENT or WM_PRINT messages. This differs from handling of painting in typical window, which occurs in respond to WM_PAINT message. In fact, when implementing painting caused by AnimateWindow you can safely forget about WM_PAINT message since it is not even sent. You don't need to implement painting in response to both WM_PRINTCLIENT and WM_PRINT, it is enough to realize painting in response to WM_PRINTCLIENT message only (alternatively you can use WM_PRINT only).
  • Making a certain type of animation requires setting relative animation flags as third parameter of AnimateWindow function. In fact, this parameter is frequently combination of flags and it can be quite tedious and troublesome to figure out what combination is required for certain type of animation. As a cooking receipt the following table enlists combination of flags that make possible corresponding animation in sample application:

    Animation TypeAnimation flags
    Slide left to rightAW_SLIDE | AW_HOR_POSITIVE
    Slide right to leftAW_SLIDE | AW_HOR_NEGATIVE
    Slide top to bottomAW_SLIDE | AW_VER_POSITIVE
    Slide bottom to topAW_SLIDE | AW_VER_NEGATIVE
    Collapse inwardsAW_HIDE | AW_CENTER
    Collapse outwardsAW_ACTIVATE | AW_CENTER
    Blend (hide)AW_BLEND
    Blend (show)AW_BLEND | AW_HIDE

  • Though description of AnimateWindow function does not say it specifically, the call to AnimateWindow function should be preceded by functions like ShowWindow or SetWindowPos. Without such call the animation does not work (I have never heard that it is possible to make animation with a single call to AnimateWindow). Anyway such receipt works in sample application.
  • Finally, the window used for blend animation cannot be a child window, and must be a top-level window. This requirement does not relate to other types of animation.

If you use screen resolution other than 1280x1024 the window used for blend animation will be somewhat misadjusted in relation to the application window, but it has nothing to do with animation itself, which should work fine (you may want to update one line of code in provided sample if you prefer another resolution).

How to make URL link in Win32 dialog?

Details

There are frequently occasions when we need to navigate user to some internet resource from desktop application. There are many posts regarding how to do it with MFC or you can use novel SysLink Control, provided that your minimum operation system is Windows XP (comctl32.dll version 6 is required).

What about a situation when we need to have URL link in compact Win32 application? Below I'll describe steps required to implement URL link in Win32 dialog. This solution provides URL link with minimum programming, and enables you to accomplish 3 tasks:
  1. Navigate user to internet location by single click.
  2. Set URL font and color.
  3. Set cursor (usually hand cursor).

This solution does not track if URL was previously visited by setting specific hyperlink color since in most situations you don't need it. To begin you can use boilerplate Win32 application generated with VS.NET 2003 and add static control (IDC_STATIC_LINK) to About dialog to transform it to hyperlink. Importantly, set Notify property to TRUE (default is FALSE) so that static control would send notification messages when clicked or passed over with cursor.

Handling single click for internet navigation

You handle click event to launch IE with specified URL by handling command message in About's dialog procedure (no subclassing is required for this task), and by doing this you'd implement the major and most simple part of the task:

case WM_COMMAND:
        if (LOWORD(wParam) == IDC_STATIC_LINK)
        {  
                ShellExecute(NULL, "open", 
                        "http://netston.tripod.com", 
				0, 0, SW_SHOWNORMAL);		
        }
Note that ShellExecute function requires inclusion of "shellapi.h" header file and addition of shell32.lib library in project settings.

Setting font and color of static control

You can set font (size and underlining) by handling WM_INITDIALOG message in About's dialog procedure with custom SetFont(HWND hDlg) function like this:

void SetFont(HWND hWnd)
{
        HFONT hfnt;
        LOGFONT lf;

        // Specify the font to use (stored in a LOGFONT structure).
        lf.lfWidth = 0;
        lf.lfEscapement = 0;
        lf.lfOrientation = 0;
        lf.lfWeight = FW_DONTCARE;
        lf.lfUnderline = TRUE; // underlining
        lf.lfStrikeOut = FALSE;
        lf.lfCharSet = DEFAULT_CHARSET;
        lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
        lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
        lf.lfQuality = DEFAULT_QUALITY;
        lf.lfPitchAndFamily = FF_DONTCARE;

        _tcsncpy(lf.lfFaceName, _T("Arial"), 32);

        hfnt = CreateFontIndirect(&lf);
        SendMessage(GetDlgItem(hWnd, IDC_STATIC_LINK),  
	                           WM_SETFONT, (WPARAM)hfnt, TRUE);
}
In the same manner, you can set the font color (hyperlink color) by handling WM_CTLCOLORSTATIC message in About's dialog procedure:
if (GetDlgItem (hDlg,IDC_STATIC_LINK) == 
		(HWND)lParam /* handle to static control */)
{
        SetTextColor((HDC)wParam /* handle to display context */, 
						RGB(0, 0, 255));
        // Use TRANSPARENT so that background is not changed while 
        // drawing a text.
        SetBkMode((HDC)wParam /* handle to display context */, 
						TRANSPARENT); 
        // If the dialog box procedure returns FALSE, then default
        //  message handling is performed.
		
        return (BOOL)GetStockObject(HOLLOW_BRUSH); 
}
Up to this point we have blue-colored URL control, which navigates us to given HTTP resource (netston.tripod.com in this sample), but our hyperlink is still missing a specific cursor (usually hand cursor) when mouse passes over the link.

Setting hyperlink cursor

To perform this task a subclassing technique is used. Usually all messages generated by controls of a dialog are handled in a single dialog procedure. You can handle messages generated by specific control in own procedure. While subclassing you set own control procedure with SetWindowLong, which return an address of an older procedure. You can set own static control procedure (StaticSubclassedFunc) by handling WM_INITDIALOG message in About's dialog procedure:
case WM_INITDIALOG:
        g_wpOldWndProc = (WNDPROC)SetWindowLong(
            GetDlgItem (hDlg, IDC_STATIC_LINK), GWL_WNDPROC, 
				(DWORD)StaticSubclassedFunc);
Don't forget to declare g_wpOldWndProc at appplication level (global variable). Finally, when mouse passes over the static control, which is now a "normal" URL hyperlink, the cursor is changed in subclassed function:
LRESULT APIENTRY StaticSubclassedFunc(HWND hWnd, UINT Message, 
				    WPARAM wParam, LONG lParam)
{
        ::SetCursor(LoadCursor(NULL, IDC_HAND));
}
You can download sample application from here. Also you can play with live example that employs just described method by downloading Rational Typist program (free). The URL hyperlink is located on the right side of the toolbar of this program.

How to draw a single divider line in VS.NET's resource editor?

Details

The single divider line is a common feature of typical Windows applications. For example, most of tabs in Word 2003's Tools/Options dialog have simple divider lines like this one in Save tab:

Interestingly, VS.NET's toolbox does not have a control to draw a single line in the resource editor, and even if you'd play with group box control by resizing its height in RC file manually, the resulting line would not look perfect (i.e. setting height to 0 or 1 does not work as you might have desired). You may come to the point that group box control use for this purpose is not the answer, and it should be a simple solution. You are right!

There are two solutions. First, you can add empty static text in resource editor, and then edit resource script manually by adding style WS_EX_STATICEDGE and setting static control's height to 1 (mention also zero before WS_EX_STATICEDGE):

LTEXT "",IDC_STATIC,30,31,63,1,0,WS_EX_STATICEDGE

In the second solution, you also edit RC file manually by adding CONTROL element with SS_ETCHEDHORZ style:

CONTROL "",-1,"Static",SS_ETCHEDHORZ,10,10,62,5

When drop-down control is expanded how to make all its items visible?

Details

When running your application you can see the following picture for the combo box common (and also standard) control:

The problem is that you have populated combo box with many items and you don't see all of them. Where are the missing items? Normally you would expect that combo box looks like:

Further, while working with VS.NET 2003 (or 2005) resource editor you might find it is not very intuitive how to stretch down expanding area of combo box since in resource editor you'd see the following:

There are handles to resize the combo box area itself, but how to resize a drop-down area only? (At the run-time, this area is visible only when the down arrow is pressed.) The answer is that the combo box, unlike other common controls, have 2 sets of handles to be resized: the first one is visible on the picture above, and the second one becomes visible in resource editor when you press down arrow (you can use only handles filled with color for resizing):

Finally, the middle-bottom solid handle enables you to resize drop-down area of the combo box control.

How to make toolbar icons for Win32 (or MFC application)?

Details

If you'd look into Visual Studio generated boilerplate files when you finish with its application wizard for Win32 project, you will not find a resource containing toolbar icons neither for the files generated with VS.NET 2005, 2003, nor with VS 98. In contrast to project created by default with MFC wizard, you need to start from scratch creating toolbar and its icons in Win32 project.

Before all, several bumpy things to note.
  • Toolbar pictures are not separate resources. In fact, all pictures should be located on a single bitmap strip (BMP file).
  • Bitmap strip consists of 16 [width]x15[height] pictures, where sizes mesured in pixels. So if your application has 10 toolbar pictures, you would need to provide 160x15 pixels bitmap resource. Windows is quite untolerating to other bitmap heights. For example, if you use 16x16 pictures your application would likely crash. The use of not default picture sizes requires a bit of additional work.
  • You cannot change toolbar's color depth in Visual Studio image editor. By default, toolbar pictures have 4-bit color depth (16 colors). In contrast, you can change color depth for icon resources, and for the bitmap resources. The use of not default color depths requires some additional work.

    To change icon color depth, you need to create new icon at first (since "device" property is read-only), and then choose image type in Image menu. The color depth for bitmaps is read/write, and can be changed directly in its properties.

    By the way, icon resources in VS.NET 2005/2003 can be 1 (monochrome), 4 (16 colors), 8 (256 colors), 32 (> 16 milions colors) bits depth. You can view, but cannot create 32-bit color icon in Visual Studio.

Up to this point we examined some pitfalls, and now we can return to the main question. One simple solution is to start from default toolbar from MFC application (Toolbar.bmp) by copying it into Win32 application. Here is the default MFC toolbar picture for SDI application:

Once imported into Win32 project, you can edit toolbar and add pictures in toolbar editor like this:

Editing toolbar, and adding new pictures is simple, but there are several tricks worth mentioning for reference:

  • The toolbar's backgroud should be painted with menu color. In color palette position cursor over the grey and right-click. The palette's symbolic rectangle representing backgroung color turns grey:

    Now, erase tool can be used to paint with the grey background color.
  • There is always fresh new area for the next image in the top editor's frame. If you leave it unpainted, this area does not matter i.e. it is not translated finally into RC file. To tell image editor that you want to add a new image, merely begin to paint last button area (new clean button area is automatically added to the right as soon as one pixel is painted).
  • Pressing Delete does not removes the image from the toolbar, you need to drag it off the toolbar area. Dragging is also the way to move a button.
  • Last (not obvious!) trick is about making a separator area on the toolbar. You need to drag a button to the right or left (approximately one half the button width) a make space between buttons. When you save RC file, Visual Studio automatically adds SEPARATOR line in toolbar code. In the similar way, the separator space is removed (guess how).

As we saw editing toolbar as toolbar resource is a bit tricky. There is also an alternative way to create a toolbar and you might find it more convenient. Start with a bitmap resource and then, after you finished with adding pictures, turn it into toolbar resource. It is more straigtforward process, but remember that toolbars are 4-bit resources, and if you created your bitmap as 8-bit (256 colors) resource, and want to convert it into a toolbar (right-click in the any of image editor's panes and choose Toolbar Editor...), you will see a message "Toolbars can only contain 16 colors ..." that will prompt you to automatically decrease color depth to 4-bit. You might have avoided it if you had started your bitmap as 4-bit resource!

How to write own XML parser (Read/Write)?

Details

When your application uses persistense realized with small satellite XML file, which does not use complex XML structures, you might need 2 function only: one for reading XML tag value, and another for writing XML tag value provided that XML tag name is given as parameter. Having in mind that standard XML parser shipped with Windows XP resides in 3 system DLL - msxml4.dll (1.17 MB), msxml4a.dll (43.5 KB), and msxml4r.dll (80.5 KB) - own XML parser might considerably decrease the size of your aplication if you plan to include XML parser into the download.
The simple XML parser is built as Unicode version. This provides compatibility with targeted multilanguage application (built with Unicode), which nevertheless uses ANSI names and values to persist its state. The XML parser included in download has the following interface:

char* XMLReadValue(char* pszTagName /* always ANSI*/);
void XMLWriteValue(char* pszTagName /* always ANSI*/, char* pszTagValue /* always ANSI*/);

Because, the parser has been wrote to provide the very basic and simple XML persistense needs of a small application, it has the following requirements and limitations.

  • Open/closing tags should be within the same line.
  • Only one pair of open/closing tags within the same line.
  • No spaces between tag value and surrounding open/closing tags.
  • XML file is ANSI text file (e.g. Windows-1252 encoding).
  • Build with UNICODE.

Notice that some requirements/limitations are pretty artificial and came from the motive to save programming efforts. You can modify the program to make it more general.

What is the algorithm realized in this functions? XMLReadValue open XML file as byte stream, and simply reads one by one line. In a loop, we check - substr- whether the tag name passed as an input parameter is inside the current line, make simple parsing and return the tag value if found. Notice that I use C++ standard library <string> header since it provides convenient functions for reading file line-by-line (getline) and parsing strings.

XMLReadValue algorithm is slightly more complicated, and it uses CreateFileA/WriteFile for writing.
  1. Read entire XML file, find a line with target line to update (e.g. tag name "OrderBy").
  2. Read into a separate string top part of the XML file (before the targeted line).
  3. Read into a separate string bottom part of the XML file (after the targeted line).
  4. Updated targeted line into a separate string.
  5. Concatanate and write entire XML file as new one (delete file and create new one, ANSI).

Now, when XML parsing algorithm is outlines here is the code.

Notice that I use CreateFile suffixed with "A" (CreateFileA) because our XML file is ANSI-type, and the parser is built as Unicode version (SetFilePointer, WriteFile auto-determine what type of writing to use based on whether CreateFileW or CreateFileA used, and they don't have "W/A" suffixed versions).

How to determine encoding of a text file?

Details

One typical situation when you might need it is when importing dictionary items from a text file into a Win32 or MFC application. Typically, this kind of application is UNICODE-based, and we need to read dictionary items from a text file (i.e. French-English, Japanese-English pairs). What kind of text file is used to keep these items? What hidden symbols at the beginning of file we need to take into account when transforming text strings from this file to UNICODE strings?

For example, UTF-8 encoding has a 3-bytes preamble sometimes called BOM (because UTF-8 has no notion of byte order, this name is a misnomer for UTF-8):

EF BB BF

Thus, if our application uses MultiByteToWideChar to transform UTF-8 string to TCHAR string we should trim left 3 bytes before that. Interestingly, UTF-8 preamble is optional so that UTF-8 file can start with actual dictionary item.

One sideway to determine the encoding of a text file is to open file in Windows Notepad, and choose File/Save As... The encoding combo box shows encoding type (ANSI or UNICODE or UNICODE big endian or UTF-8). Frequently it will work, but what if you need to know for sure if UTF-8 file has a optional 3-bytes preamble? Notepad is no use for this.

In general, Windows has no tools to determine the encoding of UNICODE file. Typically, a Hex Text Viewer that comes with third-party advanced text editors will help to determine it. Let's say one Russian character is written into a text file encoded with UTF-8. Here is snapshot taken with Notepad++ v3.8 editor (choose Plugins/HEX-Editor/View Editor):



Because 3 first bytes are EF, BB, and BF this is indeed UTF-8 file. The 5,6th byte is a single Russian character (2 bytes), and 6,7th bytes are carriage return (CR: 0x0D - VK_RETURN) and line feed (LF: 0x0A) characters. Notepad++ can show hidden characters in other view:



Notice that UTF-8 is a variable-length encoding that encodes ASCII characters with one byte, letters with diacritics, Cyrillic (and Greek, Cyrillic, Armenian, Hebrew, Arabic, Syriac and Thaana) with 2 bytes. Three bytes are used for the rest of Basic Multilingual Plane. Four bytes are used for the rest of characters. In most cases, UTF-8 saves space (almost 2 times if only European languages are used).

Here is the list of BOM used in other typical encodings:

UTF-16 Big Endian: FE FF
UTF-16 Little Endian: FF FE (reversed to BE)
UTF-32 Big Endian: 00 00 FE FF
UTF-32 Little Endian: FF FE 00 00 (and one of the following byte sequences: [ 38 | 39 | 2B | 2F | 38 2D ])

You can experiment with Notepad++ to see these characters. By the way Notepad++ enables you to change UNICODE encoding on the fly!

How to paint an area?

Details

This code paints red rectangle inside Win32 window:

RECT rc;
HBRUSH hbrush;

rc.left = 600; rc.top = 100; rc.right =  700; rc.bottom = 200;

hbrush = CreateSolidBrush(RGB(255,0,0));
HDC hdc = GetDC (hwnd); 
FillRect(hdc, &rc, hbrush);

DeleteObject(hbrush); 
ReleaseDC(hwnd, hdc);

How to delete directory in Win32?

Details

You might be surprised, but Windows platform SDK does not have a function to delete NOT EMPTY directory! Only Win32's API RemoveDirectory function is provided, which deletes directory when it does not contain other directories and files. Therefore this question has at least three parts:
  • How to delete an empty directory in Win32? The answer is to use simply RemoveDirectory function.
  • How to delete not empty directory in Win32 that has files, but does not have subdirectories?
  • How to delete an empty directory in Win32 that has files and subdirectories? The answer is to use the code.

How to share variables between different implementation files in Win32?

Details

One seemingly obvious solution is to declare global variable in shared header file (i.e. stdafx.h) and set its value in one implementation file. Alas, this it not enough: you'd likely face LNK2005 error if you have opted for this solution. The right solution is to use C++ extern keyword:
  • Declare in shared header file (such as stdafx.h) extern global variable:

    #include <string>
    using namespace std;
    extern string g_s;
  • Re-declare global variable in one implementation file and initialize it to some value:

    string g_s;                        // file scope
    g_s = "global variable"; // initialization function

How to handle double click in list view control (or tree view control)?

Details

You've probably noticed that CListCtrl (or CTreeCtrl) controls do not have handlers for double click (or right click) explicitly. To handle double click event insert an entry into the message map of a dialog that host the control and hanle event in OnDoubleClick function:

ON_NOTIFY(NM_DBLCLK, <controlid> OnDoubleClick)

What is the syntax to add/remove style for window, control, property sheet etc?

Details

The syntax is somewhat bizarre, you just need to memorize it:

m_psp.dwFlags |= PSP_HIDEHEADER; // add property page style
m_psp.dwFlags &=~ PSP_HIDEHEADER; // remove property page style

How to enable HTML style help in MFC property pages?

Details

This question might seem trivial, however default property page implementation uses WinHelp, not HTML help (i.e. uses old-style HLP files instead of newer CHM files) in VS 98 and even in VS.NET 2003! In the result, you need to invest some extra effort.

Because CPropertyPage class does not have OnHelp handler you should provide PSN_HELP message handning in Win32 style and take care not to cause malfunctioning of other handlers like OnWizardNext, OnWizardFinish etc if you implemented them and which MFC CPropertyPage class provides. The solution is like:

BOOL CMySSPropPageWelcome::OnNotify(WPARAM wParam, 
                                              LPARAM lParam, LRESULT* pResult)
{
        PSHNOTIFY* pPSHNOTIFY = (PSHNOTIFY*)lParam;

        char szPath[200];
        LPTSTR pszPath;
        SearchPath(NULL, "MySS.chm", NULL, 200, szPath, &pszPath);
    
        switch (pPSHNOTIFY->hdr.code)
        {
        case PSN_HELP:

            ::HtmlHelp(NULL, szPath, HH_DISPLAY_TOPIC, 0);
            return -1; // message handled, no futher actions
            break;
        }

        // Contitue handling notification messages other then PSN_HELP
        return CPropertyPage::OnNotify(wParam, lParam, pResult);
}


LRESULT CMySSPropPageWelcome::OnWizardNext()
{
        // ...
        return CPropertyPage::OnWizardNext();
}
Notice that in MFC you don't need to add PSP_HASHELP flag in property page constructor to show help button. In MFC's CPropertyPage implementation this flag is enabled by default.

How to customize fonts used in dialog e.g. make text of static control bold?

Details

Interestingly, VC++ 6.0 (or 7.0) dialog editor does not have options to setup font properties when showing text. There is only programmatic solution! Suppose a dialog has static control with IDC_WP_STATIC_TITLE as ID. Then just write:

CFont fong;
font.CreatePointFont(12,"Tahoma");
GetDlgItem(IDC_WP_STATIC_TITLE)->SetFont(&font);

Notice that we used convenient CreatePointFont instead of much longer writing:

CFont font;
font.CreateFont(12,                            // nHeight
                0,                             // nWidth
                0,                             // nEscapement
                0,                             // nOrientation
                FW_NORMAL,                     // nWeight
                TRUE,                          // bItalic
                FALSE,                         // bUnderline
                0,                             // cStrikeOut
                ANSI_CHARSET,                  // nCharSet
                OUT_DEFAULT_PRECIS,            // nOutPrecision
                CLIP_DEFAULT_PRECIS,           // nClipPrecision
                DEFAULT_QUALITY,               // nQuality
                DEFAULT_PITCH | FF_SWISS,      // nPitchAndFamily
                "Tahoma");                     // lpszFacename


© 2005-2006, Marcel Lambert

Contact me if you have an interesting project
(software projects, IT consulting & writings, business startups),
ongoing or that you plan to realize, by
lambert1791 at gmail dot com.