Click to return to the Component Development home page    
Web Workshop  |  Component Development

COM Tutorial Samples


EXESKEL - Win32 EXE Skeleton Application

Tutorial home
Tutorial
Home
Previous Lesson
Previous
Lesson
Lesson List
Lesson
List
Next Lesson
Next
Lesson

Summary

The EXESKEL sample introduces the basic application skeleton that can be used as a point of departure for more complex Win32 applications. It is used as a base for the COM Tutorial series of code samples. Of particular interest in this code sample is the support for initializing and uninitializing the COM Libraries. The general use of APPUTIL to construct this application is also worthy of study.

For functional descriptions and a tutorial code tour of EXESKEL, see the Code Tour section in EXESKEL.HTM. For details on the external user operation of EXESKEL, see both the Usage and Operation sections in EXESKEL.HTM. To read EXESKEL.HTM, run TUTORIAL.EXE in the main tutorial directory and click the EXESKEL lesson in the table of lessons. You can also achieve the same thing by clicking the EXESKEL.HTM file after locating the main tutorial directory in the Windows Explorer.

In general, to set up your system to build and test the code samples in this COM Tutorial series, see TUTORIAL.HTM for details. The accompanying makefile is Microsoft NMAKE-compatible. To create a debug build of EXESKEL, issue the NMAKE command at the command prompt.

Usage

EXESKEL is an .EXE file that you can execute directly from Windows or from the command prompt. No command line arguments are recognized by EXESKEL.

Run the sample

The EXESKEL.EXE executable must be compiled before you can run it. For more details on building the samples, see Building the Code Samples.

If you have already built the EXESKEL sample, you can run EXESKEL.EXE directly from here.

Operation

Menu Selection: File/Exit
Quits EXESKEL.

Menu Selection: Help/EXESKEL Tutorial
Opens the EXESKEL.HTM tutorial file in the Web browser.

Menu Selection: Help/Read Source File
Starts the Windows Notepad and displays a selected EXESKEL source file. This command illustrates how to program the use of the File Open common dialog box.

Menu Selection: Help/About EXESKEL
Displays the About dialog box for this application, a standard part of this series of code samples. The code illustrates how to program the use of the CAboutBox class provided by APPUTIL.LIB.

Code Tour

Files          Description
EXESKEL.TXT    Short sample description.
MAKEFILE       The generic makefile for building the code sample
               application of this tutorial lesson.
EXESKEL.H      The include file for the EXESKEL application.
Contains
               class declarations, function prototypes, and resource
               identifiers.
EXESKEL.CPP    The main implementation file for EXESKEL.EXE. Has
WinMain
               and CMainWindow implementation.
EXESKEL.RC     The application resource definition file.
EXESKEL.ICO    The application icon resource.

EXESKEL is meant to be a generic point of departure for building Win32 C++ applications that support COM/OLE. It is the basic skeleton that is built upon in subsequent code samples in this tutorial series. You can study the code comments to learn more about this C++ application skeleton.

EXESKEL.CPP defines the WinMain entry function for the entire application, which contains the message loop. EXESKEL makes use of many of the utility classes and services provided by APPUTIL. For more details on APPUTIL, study the source code located in the sibling APPUTIL directory and APPUTIL.HTM in the main tutorial directory.

EXESKEL.H exploits APPUTIL's CVirWindow abstract base class (see APPUTIL.H) to derive and implement a CMainWindow class. CMainWindow encapsulates the main window functionality into a convenient C++ object. It handles initialization of new instances of the main window and the message dispatching of the main window procedure. This main window procedure is a method of CMainWindow.

In the context of a COM Tutorial series of code samples, the goal of EXESKEL is to show the initial and final code you need so your application can properly use the COM libraries and implement COM interfaces.

Before the COM libraries can be used, they must be initialized. Since the COM Libraries allocate resources, you must uninitialize the libraries just before exiting the application to release any resources allocated by the libraries and to unload the COM DLL libraries themselves.

For EXESKEL, the code for initializing and uninitializing the COM libraries is found entirely in the WinMain function, located in EXESKEL.CPP. The following code fragment found at the very start of WinMain illustrates how to check for initialization of the COM libraries and to exit gracefully if the libraries cannot be initialized properly.

  // Call to initialize the COM Library. Use the COM SUCCEEDED macro
  // to detect success.
  if (SUCCEEDED(CoInitialize(NULL)))
  {
    // If we succeeded in initializing the COM Library we proceed to
    //   initialize and run the application.
    ...
    ...
  }
  else
  {
    // We failed to Initialize the COM Library. Put up error message
box
    // saying that COM Library couldn't be initialized.  Parent
window
    // is desktop (ie, NULL). Exit the failed application (ie, by
    // returning FALSE to WinMain).
    ErrorBox(hInstance, NULL, IDS_COMINITFAILED);
  }

For Win32 applications, the pvReserved parameter to the CoInitialize call must always be passed as NULL. The parameter isn't used in 32-bit COM, and CoInitialize will return E_INVALIDARG if you pass a non-NULL parameter. The COM SUCCEEDED macro is used to check the CoInitialize return code for success. The hPrevInstance parameter of WinMain is not used because under Win32, it is always NULL. Other techniques must be used if you want to allow only one instance of the application to run. In this case, multiple instances of EXESKEL are permitted. If COM could not be initialized then APPUTIL's ErrorBox function is used to display an error message box.

Once the checks and COM initialization are done, the application continues. Just before it exits, it must call CoUninitialize. Such exits could occur in many places in the program. The final normal program exit usually occurs after the application's message loop is terminated. The following code fragment shows this CoUninitialize call. It's a simple call that takes no parameters.

  // We're exiting this app (either normally or by init failure) so
shut
  // down the COM Library.
  CoUninitialize();

CoInitialize could be called more than once in an application. The first successful call will return NOERROR; subsequent successful calls will return S_FALSE. Each successful nested call to CoInitialize must be matched with a corresponding call to CoUninitialize. CoUninitialize frees resources the COM libraries have allocated and unloads the libraries themselves.

The EXESKEL application calls CoInitialize and CoUnitialize because it uses only COM services in the COM library (currently part of OLE32.DLL). If the application will be exploiting OLE technology--such as OLE automation, compound documents, in-place activation, linking, embedding, and drag-and-drop--the OleInitialize call must be used instead. The OleInitialize function loads the OLE libraries, including the COM library. Thus OleInitialize calls CoInitialize internally, and OleUninitialize calls CoUninitialize internally.

There is stub code in the DoMenu method of EXESKEL.CPP for opening the application's help file. This code is essentially a placeholder. The EXESKEL.CPP code assembles the proper help file name, as in the following fragment from CMainWindow::InitInstance:

  // Build a path to where the help file should be (it should be in
  // the same directory as the .EXE but with the .HTM extension).
  MakeFamilyPath(hInstance, m_szHelpFile, TEXT(HELP_FILE_EXT));

MakeFamilyPath is a utility function in the APPUTIL.LIB library that uses GetModuleFileName to dynamically determine the path to the executing module and then construct a proper "family" variant of that path with the proper file name extension for a particular purpose. In this case, HELP_FILE_EXT (.HTM) is used. Various such extensions are defined in APPUTIL.H for use with the MakeFamilyPath function.

If support for the case IDM_HELP_CONTENTS: menu selection is provided in the EXESKEL.RC file, the following code attempts to find and run WinHelp on that EXESKEL.HLP file (from the DoMenu method in EXESKEL.CPP).

  ...
  case IDM_HELP_CONTENTS:
    // We have some stubbed support here for bringing up the online
    // Help for this application.
    ReadHelp(m_hWnd, m_szHelpFile);
    break;
  ...

EXESKEL does not use an accompanying help file since such information is available in the tutorial files. Thus, the EXESKEL.RC file has the Help Contents menu item commented out.

We saw how this sample series uses an online HTML-based tutorial in the previous READTUT sample. Here is EXESKEL's menu code that invokes such HTML-based tutorial help (again from DoMenu in EXESKEL.CPP).

  ...
  case IDM_HELP_TUTORIAL:
    // Call the APPUTIL utility function, ReadTutorial, to Browse
the HTML
    // tutorial narrative file associated with this tutorial code
sample.
    ReadTutorial(m_hInst, m_hWnd, TEXT(HTML_FILE_EXT));
    break;
  ...

ReadTutorial is called with a non-NULL HTML target specification. In this case, ReadTutorial detects that a .HTM file extension is specified and launches the Web browser on file EXESKEL.HTM which is assumed to be located in the parent directory of the EXESKEL sample directory. ReadTutorial determines the path and name of the module that is executing it and constructs the path and name for an EXESKEL.HTM file in the parent directory.

At the very start of WinMain, the UnicodeOk function call is made to determine if the application was compiled with UNICODE defined and is running on a platform where Win32 string functions are supported for Unicode.

The UnicodeOk function is defined and used only within the EXESKEL.CPP module.

  BOOL UnicodeOk(void)
  {
    BOOL bOk = TRUE;
    TCHAR szUserName[MAX_STRING_LENGTH];
    DWORD dwSize = MAX_STRING_LENGTH;

    if (!GetUserName(szUserName, &dwSize))
      bOk = ERROR_CALL_NOT_IMPLEMENTED == GetLastError() ? FALSE :
TRUE;

    return bOk;
  }

This function uses a representative Unicode-supported Win32 API function call (GetUserName). This call reduces to GetUserNameW when UNICODE is defined and reduces to GetUserNameA when UNICODE is not defined. If a call to GetLastError returns ERROR_CALL_NOT_IMPLEMENTED, the implication is that the platform won't handle other Unicode calls very well either, and UnicodeOK returns FALSE.

Here's the code at the start of WinMain that uses the check:

  // If we were compiled for UNICODE and the platform seems OK with
this,
  // then proceed.  Otherwise, we error, and exit the application.
  if (UnicodeOk())
  {
    ...
    ...
    ...
  }
  else
  {
    // If we were compiled for UNICODE but the platform has problems
with
    // this, then indicate an error and exit the application
immediately.
    CHAR szMsg[MAX_STRING_LENGTH];

    if (LoadStringA(
          hInstance,
          IDS_UNICODEFAIL,
          szMsg,
          MAX_STRING_LENGTH))
    {
      MessageBoxA(
        NULL,
        szMsg,
        ERROR_TITLE_STR,
        MB_OK | MB_ICONEXCLAMATION);
    }
  }

Notice that the xxxA (ie, ANSI) versions of the LoadString and MessageBox Win32 API calls are used explicitly. This is because the macro versions of these calls (LoadString and MessageBox), when compiled with UNICODE defined, will expand to LoadStringW and MessageBoxW. If the platform does not support these xxxW calls (the ones sensitive to Unicode), they will fail with GetLastError() returning ERROR_CALL_NOT_IMPLEMENTED. Many Win32 platforms support Unicode, and more will do so over time. The Unicode (xxxW) versions of the Win32 API are fully implemented under Windows NT.



Back to topBack to top

Did you find this material useful? Gripes? Compliments? Suggestions for other articles? Write us!

© 1999 Microsoft Corporation. All rights reserved. Terms of use.