© 2004 Microsoft Corporation. All rights reserved.

Implementation Tips and Tricks

Changing the Title of a MessageBox

      In the case of a dialog-based application, the title of the message box provided by AfxMessageBox is the application file name (without its extension) in uppercase. To change this behavior, pass the string you want to the CWinApp constructor during the application class construction:
CGdiUsageApp::CGdiUsageApp() : CWinApp("GDI usage")
{
};
      This string is stored by MFC in the m_pszAppName member of CWinApp and, for SDI or MDI applications generated by AppWizard, the AFX_IDS_APP_TITLE string resource is used as a template, but not for a dialog-based application. See APPCORE.CPP and APPINIT.CPP in the MFC source code directory for the implementation details.

Using a True 16x16 Icon in an MFC Dialog

      By default, MFC supports only normal-sized icons. Because of this, the one displayed in the top-left corner of the main dialog is an ugly, stretched version of the 32x32 icon. The CGdiLeakDlg class keeps track of the small icon in its m_hSmallIcon member, which is set in OnInitDialog using LoadImage:
m_hSmallIcon = 
    (HICON)::LoadImage(
    AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), 
    IMAGE_ICON, 
    16, 
    16, 
    0
    );
      Unlike LoadIcon, the icon handle returned by LoadImage must be deleted using DestroyIcon, and the WM_DESTROY message handler is a good candidate to do this.

Saving State Information in .INI Files

      To leave a dialog on top of other windows, you can call SetWindowPos with wndTopMost as the first parameter. If the user has chosen an Always on Top state, it is useful to save this information from session to session. Rather than using the registry, you can use an .INI file. In MFC, by default the GetProfileString and WriteProfileString methods access a file whose name is formed from the name of the application plus an .INI extension, and the file is stored in the Windows directory. In order to avoid having these files in your Windows directory, you can replace the CWinApp::m_pszProfileName member.
// First free the string allocated by MFC at CWinApp 
// startup. 
// The string is allocated before InitInstance is 
// called and released in CWinApp destructor
   free((void*)m_pszProfileName);

// build the file name according to the application 
// file name
   TCHAR szINIFile[_MAX_PATH];
   ::GetModuleFileName(NULL, szINIFile, _MAX_PATH);
   _tcsrchr(szINIFile, _T('.'))[0] = _T('\0');
   _tcscat(szINIFile, _T(".ini"));
   m_pszProfileName = _tcsdup(szINIFile);
      Don't forget to free the string first, then duplicate the new one in order to let CWinApp do the cleanup.

Getting an Executable File Name from a Process ID

      This is not difficult since PSAPI exports GetModuleFilenameEx, which does exactly what needs to be done. In addition to a buffer to store the file name, a process handle is needed. The first time I tried it I got an access denied error! My mistake was to believe that the PROCESS_QUERY_INFORMATION flag was the perfect way to get a process handle from OpenProcess with enough rights to call GetModuleFilenameEx. However, I was wrong: the PROCESS_VM_READ flag must be added to PROCESS_QUERY_INFORMATION to avoid the access denied problem.

Isolating Platform-specific APIs in C++

      To avoid static linking that might force Windows to refuse to load your application because a function (specific to this Windows platform) is not exported by any of the system DLLs, you can isolate platform-specific code into a class that needs to expose a startup method and that wraps the other functions into their own methods. The GetGuiResources function is a good example. If you statically link to it, your application will fail to load on Windows NT 4.0 or Windows 9x since GetGuiResources is only exported by the Windows 2000 version of USER32.DLL.
      Don't hesitate to use LoadLibrary and GetProcAddress to locate the platform-dependent APIs you need. To wrap all these tricky details, I use a class that exposes the corresponding functions. In addition, a method called BindToWindows returns TRUE if the late binding is successful. Call it in your derived InitInstance function and quit if it returns FALSE. After that, everything should work.