Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Verifying Installation Environments


Jun02: Verifying Installation Environments

Chris is the founder of Wave Software, which specializes in the development of Internet and Windows applications. He can be contacted at [email protected].


Deploying an application in the Windows environment can be problematic when you consider how easily the base OS environment can be changed. For instance, how can you be sure that all the required system DLLs are present on the customer's system? Or worse, how can you determine if an environment has been changed by, say, any number of other product installations that may occur, causing your software to break. Whose support line is going to be called? Yours, of course.

In this article, I describe a system I developed that separates the process of verifying the environment on which your program will run from that of the installation process. I built this system primarily to avoid cluttering the installation software and benefit technical support: Customers would now have a diagnostic tool that they could use to reevaluate their environment whenever they are having problems with your software.

My design goals were to provide an extensible architecture for programmers to add support for specific environment tests in the future; implement a basic set of tests for common environment requirements; support external configuration files enabling different product environments to be tested without changing the code; support downstream tools, for example, installers, by recording the level of environment compatibility; and minimize run-time dependencies — the application will typically run directly from a product installation CD-ROM.

I wrote my test program (ECHECKLITE) in Borland's C++ Builder because of the ease with which it lets you reach into the Windows API and because of its design tools that enable the application GUI to be rapidly developed. An alternative could have been to separate the program by putting all the environment testing in a DLL and write the GUI using VisualBasic.

ECHECKLITE is a fully functional environment test program. The complete source code and related files (including executable) are available electronically; see "Resource Center," page 5. The "lite" comes from the fact that I didn't add support for all the different versions of Windows, in particular the detection of the Windows 2000 flavors (Professional, Server, Advanced Server, and Data Centre).

Figure 1 is ECHECKLITE's main window. The name of the application whose requirements are being tested is in the title bar and test information label. Each test will have an incompatibility severity status reported (Unknown, OK, Low, Medium, or High), along with the actual value detected while performing the test.

The GUI also features a Details button that opens an HTML page to provide more information on the system requirements; such as including hyperlinks to appropriate downloads or installation packages. The Save button lets the environment test report be saved to disk (useful for sending to technical support), and the Finish button closes the program.

An About box can be accessed from the window's system menu, where the ECHECKLITE version is shown along with a copyright notice read from the configuration file. ECHECKLITE provides for the environments in Table 1 to be tested. For the types of applications I'm deploying, these tests cover the full range of possibilities.

External Configuration

In my design, I was looking for a way to create a generic environment-testing program that could be driven by external configuration files for specific products. I opted to create the configuration files using the Windows INI file format. The major sections of the file include:

  • Main. The Main section holds a number of global settings; for example, the title of the application whose environment is being tested. The full list of variables is defined in Table 2 and Listing One(a).
  • Platforms. Each of the platforms supported by ECHECKLITE has a section in the configuration file. The presence of the section implies that the platform is supported, its absence means the platform is not supported. Sections that are included contain references to the environment tests that should be performed; see Listing One(b).

  • Tests. Each test referenced by the platform has its own configuration section. Table 3 lists, by test type, the various configuration settings. For clarity the Name setting is not listed; but it is mandatory for all tests and is used in the GUI. For example, to create a test that verifies the presence and assesses the version of Microsoft's Internet Information Server, the test declaration would be written as in Listing One(c).

The Microsoft Data Access Components (MDAC) test is implemented to use the COM API provided to determine the MDAC version; no individual DLL is tested (at least not by ECHECKLITE). However, this does restrict the test to working with MDAC 2.1 and above. Previous versions will have to be tested some other way — probably using the RegistryFile test type.

The FreeDiskSpace test looks at the available space on every local drive until one is found with sufficient space or all have been tested. The actual implementation tries to use GetDiskFreeSpaceEx, but if this is not found (you need at least Windows 95 OSR2 for the extended function) then it drops back to using GetDiskFreeSpace. The main difference is that the latter cannot reliably determine available space on disks with more than 2 GB of free space.

Environment Test Framework

One of my original design goals was to produce a system that could be easily extended. ECHECKLITE has a base class Test (Listing Two). The two key methods are Initialize and Verify. The configuration data for each test is stored as section values in an INI file. The configuration loader reads the values, determines the type of test required, calls on a TestFactory class to create a new test instance, and then passes the remaining values to the Initialize method. This data-driven interface enabled me to keep the signature of the interface the same without limiting the capabilities of derived classes. The Verify method is responsible for executing the logic of the test and determining the status.

The test factory uses a table-driven approach to determine the type of test to instantiate. Each test type is assigned an ID that maps to a static member function on the TestFactory class. It is then a simple matter to traverse the table to find the right factory method given a particular test type; see Listing Three.

Versions

The first version of ECHECKLITE featured a simple means to test version numbers; for example, verifying that a particular version of the Microsoft XML Parser is installed. The algorithm essentially took the component's installed version information and compared it with the data in the configuration file. Based on whether the installed software was older, the same, or newer, the test would yield a fail, succeed, or a succeed with a Medium severity incompatibility, respectively.

Unfortunately the application I was working on at the time didn't function correctly when Internet Explorer 5.01 was installed without a service pack. The 5.0 release was fine; the 5.01 Service Pack 1 release was also fine. After some debate, we chose not to support IE 5.01, so I had to go back and rework the version comparison code. The system included in ECHECKLITE allows for multiple ranges to be specified as shown in this example:

Versions=1.0.0.0-2.0.0.0,4.0.0.0- 4.1.0.0,5.0.0.0-5.0.0.0

With this specification, all versions between 1.0 and 2.0, 4.0 and 4.1, and release 5.0 will be supported. All other versions will cause a failure. This approach lets me work around the 5.01 release of Internet Explorer and increase the flexibility of testing software versions.

The FixedFileInfo class handles the version extraction for a file containing the VERSION resource type. It hides the gritty details of picking apart the structure and has accessor methods to get both the FILE and PRODUCT version strings as Version instances.

System Requirements

ECHECKLITE provides access to a details HTML page that can be used to provide more information on the system requirements and hyperlinks to installation packages. The root directory for the HTML is always <PATH TO ECHECKLITE.EXE> and the value of the DetailsRoot attribute read from the configuration file. The actual name of the HTML file launched when users click the Details button is the name of the configuration file with an .htm suffix.

Downstream Tools

No matter how many High severity incompatibilities show up in ECHECKLITE, some people are always going to run the installation program, regardless of the consequences. Therefore, ECHECKLITE writes the number of Low, Medium, and High severity incompatibilities to a registry key specified in the configuration file. It then becomes a simple matter for downstream installation programs to read this data and abort if necessary.

Detecting the Version of Windows

Being able to check different environments meant being able to detect what OS version ECHECKLITE was running on. The Windows API provides the GetVersionEx function that can be passed one of two structures: OSVERSIONINFO and OSVERSIONINFOEX, as well as extra data about the specific OS flavor that's running. Each structure includes a member that says how big it is — this lets GetVersionEx decide what to do, depending on what you pass it. However, OSVERSIONINFOEX can only be passed to Windows NT with SP6, Windows 2000, or later; older NT 4 and Windows 95 or 98 are not supported. Without OSVERSIONINFOEX, you have to resort to reading cryptic registry entries for more detail.

Debugging Platform Tests

Because I only had Windows 2000 Professional available during development, I added a simple debug interface to the ECHECKLITE program that lets you bypass its platform detection step. In this way I could fool the program into thinking that it was running on a different platform. This was useful to validate that the right environment tests were being performed. For example, ECHECKLITE.EXE W95 prd.cfg leads ECHECKLITE to believe that it is running on Windows 95.

Using Microsoft DLLs With Borland C++ Builder

Borland C++ Builder cannot use Microsoft DLL import libraries because they are written in a different format. I experienced this problem after downloading CPUINF32.DLL (this DLL is used to test the speed of the machine's CPU) from the Intel web site. The solution is to create a compatible import library using the IMPLIB.EXE command-line tool. You do this by running the utility directly against the DLL to produce a compatible import library. You can find more details in the C++ Builder Tools online help package.

Run-Time Package

To use ECHECKLITE in a run-time environment, create your own configuration file (you might want to use PRD.CFG, which is included in the source package as a template), then put the following files together in the same directory either on your installation CD-ROM or as part of the download:

ECHECKLITE.EXE

CPUINF32.DLL

<CONFIGURATION FILE>

Conclusion

Separating the environment tests from the installation program provides three key benefits:

  • Users can verify their environment before starting installation.
  • The installation program is less cluttered with platform-testing code.

  • The program can be used at a later date to help diagnose failures that may have been introduced by an environment change that occurred sometime after the original software was installed.

ECHECKLITE provides good coverage for a range of environment checks that are data driven and can be easily changed to support the specific requirements of individual applications; and because the code is structured, it lets new test types be easily included in the framework.

DDJ

Listing One

(a)

[Main]
Title=Killer App 2.2
Copyright=Copyright (C) 2001 Wave Software Limited.  All Rights Reserved.
DetailsRoot=html
RegistryKey=software\wavesoftware\killerapp\2.2\install

(b)
<pre>[W2K]
Test001=RAM
Test002=CPUSPEED
Test003=FREEDISKSPACE
Test004=NETWORKCARD
Test005=SERVICEPACK
Test006=MDAC
Test007=REGISTRYFILE
Test008=SYSTEMFILE

(c) 
<pre>[REGISTRYFILE]
Name=Microsoft Internet Information Server
Type=7
RootKey=2
Key=software\microsoft\inetstp
Value=InstallPath
Data=inetinfo.exe
Search=
Replace=
Versions=4.2.622.1-4.2.690.1

Back to Article

Listing Two

class Test
{
public:
   enum TEST_STATUS {TS_OK, TS_LOW, TS_MED, TS_HIGH};
   Test();
   virtual ~Test();
public:         // Operations
   virtual bool initialise(const stringmap& data);
   virtual bool verify() = 0;
public:         // Mutators
   void setName(const std::string& name);
   void setDisplayName(const std::string& displayName);
public:         // Accessors
   const std::string& getName();
   const std::string& getDisplayName();
   TEST_STATUS getStatus();
   const std::string& getStatusMsg();
protected:
   void setStatus(TEST_STATUS status);
   void setStatusMsg(const std::string& msg);
private:
   std::string m_Name;
   std::string m_DisplayName;
   TEST_STATUS m_Status;
   std::string m_StatusMsg;
};

Back to Article

Listing Three

   typedef Test* (*TESTCREATEFUNC)(const stringmap& data);
   typedef struct tagTESTENTRY
   {
      int type;
      TESTCREATEFUNC func;
   } TESTENTRY;

   TESTENTRY g_Tests[] =
   {
      { 0, createNull },
      { 1, createRam },
      { 2, createCpuSpeed },
      { 3, createDiskFreespace },
      { 4, createNetworkCard },
      { 5, createServicePack },
      { 6, createMDAC },
      { 7, createRegistryFile },
      { 8, createSystemFile }
   };

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.