COM Tutorial Samples |
Tutorial Home |
Previous Lesson |
Lesson List |
Next Lesson |
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.
For details on setting up your system to build and test the code samples in this COM Tutorial series, see Building the Code Samples. The supplied makefile (MAKEFILE) is Microsoft NMAKE-compatible. To create a debug build, issue the NMAKE command in the Command Prompt window.
For convenient use in Microsoft's Visual Studio, a project file is provided for each sample. To load the project for the EXESKEL sample, you can run Visual Studio at the Command Prompt in the sample's directory as follows:
MSDEV EXESKEL.DSP
You can also simply double-click the EXESKEL.DSP file in the Windows Explorer to load a sample's project into Visual Studio. From within Visual Studio you can then browse the C++ classes of the sample source and generally perform the other edit-compile-debug operations. Note that, as part of the Platform SDK, the compilation of these samples from within Visual Studio requires the proper setting of directory paths in Visual Studio. For more details, see Building the Code Samples.
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.
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.
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.DSP Microsoft Visual Studio Project file.
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 CoUninitialize 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.