COM in the Windows Installer World

Ever wonder how to build an install package? This author shows you how, as he shows you step by step how to built a package with which to install a COM server. This article is from chapter 3 of The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972).

IN THIS CHAPTER, you’ll build a package to install a COM server, but first some background and history.

Let’s All Share

You probably know that a COM object is created by a COM server in response to—and on behalf of—a client request for a unique Classid and Interface ID. Look at the specific case of a COM server implemented as a DLL. When installed, this COM server has a Registry entry in HKCRCLSID{Classid Guid}InprocServer32 that usually contains the path to the server. (I say usually because you’ll see cases later in the book where InprocServer32 isn’t the full path to the DLL.) Consequently, every client that asks for this class causes Windows to go to this unique Registry entry to find the path to load the server. The process whereby these Registry entries are created initially is called self-registration, “self” because the server itself contains the code to write these Registry entries using an exported function called DllRegisterServer. Installation has historically meant that the DLL first is copied to the system, and then its DllRegisterServer entrypoint is called. This is how Regsvr32.exe registers a server, although installation programs generated by proprietary software tools don’t usually run Regsvr32.exe—they typically just load the DLL, then use the GetProcAddress Win32 API call to find the location of DllRegisterServer and call it. (You can also have out-of-process COM servers implemented as executa-bles—EXE files—that have a different installation scheme because they don’t export a DllRegisterServer function, but the general principle here is the same in both cases.)

What happens if multiple products want to use the same COM server? Clearly this is desirable because a COM component is most often used as a shared component. If a product P1 installs the DLL to location A, and another product P2 installs it later to location B, the DllRegisterServer for the later product will clearly result in the InprocServer32 path now pointing to location B. Three things are wrong with this scenario:

  1. The product P2 might have installed an older version of the DLL to location B. Product P1 now fails if it requires new interfaces implemented in the DLL that the registration entries no longer refer to.

  2. If product P1 is uninstalled, it has no clue that someone else is using the COM server, so it happily deletes the registration entries and breaks product P2.

  3. If product P2 is uninstalled, it uninstalls its Registry entries, and the COM server and product P1 are broken.

The way out of these dilemmas consists for the most part in making sure that every client that uses the COM server arranges to install it to a specific unique location on the system. Windows provides some help and a convention by encouraging use of the Registry to count the references to shared DLLs. What you’ll examine here is the reference counting scheme that has historically been used to manage the sharing of common files. I’ll use it to show how sharing schemes need to work.

When an installation program copies a DLL to the system, it looks in HKLMSoftwareMicrosoftWindowsCurrentVersionSharedDLLs for the path where the DLL is going to be installed. If the DLL’s install path is in this Registry location, the counter for that path is incremented. If it isn’t there already, there is usually an option in the installation development tool that causes the path to be entered there with an initial count of one. When the product is uninstalled, the reference count is decremented. When the count becomes zero, this means that the DLL is no longer being used and it can be removed from the system. The behavior you see at this point depends on the uninstall program. Some might ask for confirmation that it’s acceptable to remove the shared DLL that’s no longer in use. The key point is that removal of the actual DLL is also the trigger that causes the registration entries to be removed as well. I should stress that this SharedDLLs scheme is not the primary mechanism that Windows Installer uses to count references to installer components, although it supports the SharedDLLs mechanism.

Note that this reference counting scheme is based on the path to the DLL. If a COM DLL is installed to the wrong location, the reference counting breaks. It does no good to have the COM DLL with a reference count of one in two separate paths in the SharedDLLs entries. Using the preceding example, you’re still in trouble if product P1 has reference counted the DLL’s path in one location and product P2 in another because each believes it’s the only remaining user of the DLL and removes the Registry entries.

It’s worth noting here that you can use this reference counting in any case where a shared file is installed to a common location, not just COM server DLLs.

It isn’t unusual for a company’s shared DLLs to be installed in a common location and reference counted using SharedDLLs so that the last product to be uninstalled can remove the DLL.

The required convention for a shared COM server is that every product agrees on the common installation location for the file. Each product installs the file to that location, incrementing the reference count in the SharedDLLs Registry entry at install time and decrementing at uninstall time. The critical point here is that by counting the references to the file, you’re effectively also counting the references to the registration entries for the server so that the registration entries are not removed until the DLL is removed.

Finally, there is a standard installation convention that a newer version of a versioned file replaces an older version of the same file. This is normal practice for installing versioned files, and it is Windows Installer’s default behavior. The result of putting all these together is that references to the COM server and its Registry entries are counted, and that existing client programs depend on new versions of the COM component installed from other products being a superset of the previous versions.

Anyone that has done COM development should be aware of the concept of the COM contract between client and server, whereby the server promises to preserve a particular interface on a class for all time. The contract states that if the server needs to provide new functionality, a new interface is added to an existing class, and this new interface extends the old interface. New clients ask for the new interface to get the new functionality. Old clients continue to ask for the older interface, which provides their required functionality. If you think about this, the COM contract must behave this way because of the installation constraints on sharing that I just discussed. In other words, a substantial part of the COM programming paradigm is driven by installation requirements. If you thought that installation did not have much impact on software architecture, you should take note of its impact on the COM programming model. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=Installing a COM Server}

In Chapter 1 I talked about the transactional nature of an installation and minimizing the code that runs during an installation. If the registration of a COM server isn’t going to call DllRegisterServer, how does the registration code get written to the Registry? A Windows Installer package does contain tables pertaining to COM registration, the contents of which get written to the Registry at install time. That begs the question of how you get the data into the tables in the first place. The answer to that depends on what tool you use to build your installation package. Some tools can spy on the progress of DllRegisterServer and populate the tables for you. If your tool doesn’t, a helper program is supplied with the book to let you see the Registry entries.

In this case, you’re using VS, and you’ll install a COM DLL that’s supplied with the book. Use VS to start a new Setup and Deployment project, as with the Notepad installation in Chapter 2. This time the only file you’ll be adding to the Application folder is the COM DLL from the COMServer project sample. This COMServer project has been altered so it does not do automatic self-registration of the DLL when it compiles. By default the VS development environment for a COM server generates a call to REGSVR32.EXE to register the server, but this postbuild step has been removed from the project to keep registration data off the system until installation time.

As in Chapter 2’s Notepad installation, you can create a new Setup and Deployment project and get to the point where you’re adding files to the target system. If you add COMServer.dll and look at its properties, you’ll see the screen in Figure 3-1.


Figure 3-1.  COM server properties

Under the Register property, VS has set the default to vsdrfCOMSelfReg, the setting to self-register the DLL by calling DllRegisterServer. Aside from wondering why this should be the default for a Windows Installer project, how did it know this is a COM server? VS looked at the version resources for the DLL and found an OLESelfRegister setting. This is a standard marker for self-registering servers. But in the choices for the Register property, there is another choice: the vsdrfCOM value. Selecting this causes VS to extract the COM registration entries when you build the setup project, and it populates the appropriate tables. After building the package, run Orca on it and look at the Class table that contains the COM class information (see Figure 3-2).


Figure 3-2.  The Class table

As you can see, a CLSID column contains a CLSID that the COM server supplies. The Context column shows it to be an InprocServer32 COM server (as opposed to LocalServer32 for an out-of-process server). The default ProgId for the class is there, and so is the Windows Installer component that this COM server is associated with. (At the risk of stating the obvious, this extraction process that VS performed still uses the DllRegisterServer call and monitors what happens. This means that the DLL itself requires its dependent DLLs to load when you build the Setup project, so they need to be present on the development machine. You can verify this behavior by putting a MessageBox call in the DllRegisterServer code. You’ll see that it gets called during VS’s build process.)

Note the SharedLegacyFile property that appers in Figure 3-1. This setting maps to the SharedDLLs reference counting in the Registry through the msidbComponentAttributesSharedDllRefCount in the Component table of the package. Setting this bit forces creation of a SharedDLLs entry in the Registry, but if you don’t set it and Windows Installer finds an existing entry, Windows Installer increments the existing count for the SharedDLLs entry. You can always set this property to True for an installation. It does no harm, and the file won’t get removed accidentally if some other non-Windows Installer setup installs the file to the same location and uses the SharedDLLs references.

Although this Orca view in Figure 3-2 is truncated, there is in fact no direct reference to the DLL you’re installing—this linkage is indirect, through the Component entry in the Class table. The Component table for this component has a KeyPath entry, and this KeyPath entry is in turn a key that refers to the File table entry for the server. This is another example of the Windows Installer design principle described in Chapter 2, where the Shortcut table has a Component entry with a KeyPath entry referring to a target file.

There is more to COM registration than just the CLSID entries. Figure 3-3 shows the entries for the COM ProgID, showing the version-independent ProgID and the specific versioned ProgID. The version-independent one has a parent linkage to the versioned entry.


Figure 3-3.  The ProgId table

There is also a TypeLib table, which contains the basic type library entry. Again, the Component_ column is a key to the Component table, where the KeyPath column refers to the File table entry for the type library. At install time, Windows Installer registers the type library (the same functionality as the Win32 RegisterTypeLib API). Remember that in this case the type library is embedded in the resources section of the code file. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=Type Libraries}

In the context of type libraries, it’s interesting to take a look at the Registry table with Orca (see Figure 3-4).


Figure 3-4.  The Registry table

The table itself is straightforward: Each entry has a Root to identify the Registry hive, the name of a Registry key, then Name and Value pairs for the contents of the key. The Name column has some values that determine how to create the data (such as +, meaning create the key if it’s absent), as well as actual data names. Notice that this table contains some of the COM registration entries that don’t have entries in the Class, TypeLib, or ProgId tables. For example, the COM ThreadingModel value is stored in this table. Now if you’re familiar with COM registration entries, you probably know that type library registration creates Interface entries in the Registry, so you might wonder why there are entries for COM interfaces here in the Registry table when the type library registration will create them. In fact, you can delete these Interface entries from the Registry table using Orca and install the edited package, and those HKCRInterface entries are indeed still created. The probable explanation is that this is simply a consequence of a VS mechanism that collects all the registration entries without any attempt to filter them, and any that don’t fit into the COM tables are put in the Registry table. There is no harm in having the explicit Interface entries installed from the Registry table as well as through type library registration from the TypeLib table.

If you have a stand-alone type library as a TLB file that you want to install and register, VS will do that for you. If you add the file to the project, you’ll see that VS automatically sets the Register property of the file to vsdrfCOM. If you build the package and look at the Registry table with Orca you’ll see that all the appropriate Interface entries have been extracted. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=InprocServer32 Entries and Repair}

After you install this COM server, if you run a Registry editor and take a look at the CLSID entries you might be somewhat alarmed. In the InprocServer32 key entry there is an InprocServer32 data item, a REG_MULTI_SZ apparently consisting of garbage (see Figure 3-5).


Figure 3-5.  The repair descriptor

Occasionally people see this and worry that the installation has corrupted the Registry, but in fact this data—this descriptor—is related to repair. More on this later.

COM Server Dependencies

A COM server might have dependencies on other COM servers or DLLs, so clearly you need to install them somewhere that the COM server can find them. They should be installed in the same folder as the COM server. You might find documentation or advice that refers to installing dependent DLLs into the application folder. This usually means the folder where the client program is installed, but in this context “application folder” means the COM server’s folder. This makes a lot of sense. In an object-oriented component world, client programs should be oblivious to the implementation details of the server components, and no client program wants to be in the business of installing a COM server’s dependencies in its application folder.

Windows Installer Sharing

I started this chapter by describing the original Windows sharing scheme based on the SharedDLLs Registry data. Windows Installer sometimes uses this mechanism because it must handle the cases where a shared DLL has already been installed (or will be installed later) by some non-Windows Installer method. By default, Windows Installer increments the SharedDLLs reference count for a path if there is an existing entry for it in the Registry. If your product installation requires the Installer to create a new SharedDLLs entry, the msidbComponentAttributesSharedDllRefCount bit must be set in the Attributes data in the Component table. To reiterate: not the File table, the Component table, and as you’ve seen before, this SharedDLLs setting applies to the KeyPath of the component.

The Windows Installer sharing mechanism works at the component level. Each component has a GUID, and Windows Installer counts the uses of this GUID. However, just like the SharedDLLs mechanism, this reference counting is meaningful only when it counts multiple references to a single location. If product P1 installs a component to its own private application folder, and then product P2 installs the same component GUID to a different private application folder, no sharing is effectively going on. Product P1 reference counts the component to its private folder and product P2 does the same. The key point here is that reference counting has no practical effect unless the same physical location of an installer component is shared by multiple separate products. In other words, the behavior of the installer’s sharing mechanism is essentially the same as the SharedDLLs mechanism discussed earlier. Instead of a Registry mechanism where each product install increments a count on a path to a file, the installer uses a GUID to reference count each use of the component. However, it is meaningful for sharing only when the component is installed to the same location by different products.

There’s some simplification in that description, because the key path of a component doesn’t have to be a file—it can be Registry data. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=Merge Modules and Sharing}

Although I’ve mentioned Windows Installer components a lot, VS’s IDE doesn’t expose the idea of components. When you build a package it creates a component for each file, but you don’t get a component view into the package being built. However, because sharing is something that happens at the component level, developers need some way to build shareable components instead of simply replicating all the settings for a shared component over and over again into a collection of packages. The package of reusable components that is designed to be shared by multiple products is called a Merge Module. A Merge Module is practically identical to an MSI package, and you can open Merge Modules and edit them with Orca. However, they have an MSM file extension and usually contain just installer components—they have no user interface if they are designed to contain only shared files. A Merge Module is designed to be merged into an installer package at build time as a subpackage of predefined installation components, and once a Merge Module has been merged into an actual installation database package it loses its separate identity. The contents of the module’s database tables, such as the File table, Class table, ProgId table, and so on are merged so that the resulting package has single copies of all the tables.

You just built an installer package to install a COM server, but the proper way to install the COM server so it can be shared properly would be to create a Merge Module and then add it into any other product installation packages that require it.

Building a Merge Module

When you start VS’s New Project wizard and choose Setup and Deployment Projects, one of the choices there is for a Merge Module project. If you create a Merge Module project for the COM server using the New Project wizard, you’ll find that the process of creating a Merge Module is nearly identical to the way in which you created the setup project earlier on. If you look at Figure 3-6 you’ll see that the DLL is being installed into the Common Files folder under a specific folder. This makes sure that all users of this COM server install it to a specific location where they can share it.


Figure 3-6.  Merge Module destination

You’re also choosing that location because it becomes the standard installation folder for all client setups that include this Merge Module. The other choice of Module Retargetable Folder, as its name implies, is a means whereby client setups get to choose the installation folder for the Merge Module files. You don’t want that choice for your sharable COM server. If you build this project and then look at the File, Component, Class, ProgId, and Registry tables you’ll see that they contain the same registration information as the package you built before.

A Merge Module is of no use unless you can merge it into an installation. If you open up that Notepad installation project, you can incorporate a Merge Module in a couple of ways. If you select the Project name in the Solution Explorer, the right-click menu has an Add choice that goes on to offer a Merge Module choice. Alternatively, the Project menu also offers an Add to allow a Merge Module to be selected. Once you add it, it shows up in the Solution Explorer with the other files. After you’ve built the new Notepad installation package that now incorporates the Merge Module for the COM server, you can open it with Orca and find little trace of the original Merge Module. For example, the Orca view of the File table now contains the DLL from the Merge Module (see Figure 3-7).


Figure 3-7.  File table with COM server

Note that the File column entry for the COM server is different from the other files; the File column for COMServer.dll has a two-part name, where the second part after the period does not exist for the files that were not in a Merge Module. The issue here is that the item in the File column must be unique to the File table. A tool that generates the File table directly, such as VS, can guarantee that each entry it generates is unique to that package. However, when a File table is being merged from an external Merge Module there is no such guarantee of unique File column names. To make them unique, VS appends the package code GUID of the Merge Module that is the source of this File table entry.

Because a Merge Module is literally merged into a containing database package, it’s important to realize that there is no sense in which the Merge Module contents are separately installed. The contents of the Merge Module— one or more Windows Installer components—become an integral part of the final package and are installed on the target system in the same way as other components that were created directly in the package. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=Relative Paths and Side-by-Side COM Components}

Side-by-side COM components are COM components that are installed without an absolute path to the server. You might have noticed that there was a vsdrfCOMRelativePath choice in the VS Register options for COMServer.dll. Perhaps you’re thinking that this is the way to install a side-by-side COM server, because a relative path is one of the requirements for a side-by-side installation. However, there is a bit more than that going on here. If you were to use this choice, install the resulting package, and look at the registration entries, you’d see that the InprocServer32 key doesn’t contain the absolute path to the server. It does indeed have just the name of the DLL: COMServer.dll. Here’s an interesting test to try: In the COMServerInst project, a test VB script (CALLCOM.VBS) displays a property from the COM object with a couple of lines of code:

set obj = CreateObject (“COMServer.MyClass”)
msgbox (obj.SomeString(1) )

If you install the COM server using that vsdrfCOMRelativePath property, it doesn’t matter what folder you run this script from. You should find that it works every time, despite the thought that this simply shouldn’t be working because the InprocServer32 key entry points only to a relative path, not the complete path to the DLL. What’s happening here is that Windows Installer is getting involved at the point that the DLL needs locating. I’ve mentioned that Windows Installer keeps track of each installer component that gets installed on the system. A component has a KeyPath, located in the Component table in the package, so Windows Installer knows the installed location of every component on the system. This means Windows Installer can locate the complete path to the COM server. Knowing the product code GUID and the installer component GUID for the COM server, Windows Installer can locate the file by locating the component’s KeyPath. But how does it know these installer GUIDs? They’re encoded into that InprocServer32 data item, the descriptor that looks like a corrupted entry. You can show that this is the case by deleting that InprocServer32 descriptor entry from the Registry, after which the preceding script no longer works. Right-clicking the package, the MSI file, and choosing Repair restores the missing entry, which causes the script to start working again.

A Detour: Locating Components

In Chapter 2 you built a VBScript that listed all the files in an installer package. You can extend this script to do what Windows Installer just did: Locate the component on the system by getting the component’s KeyPath. This is part of the listcomponents.vbs file supplied with the COMServerInst project. Error-handling code and opening the database package are removed from these fragments:

Sub ListComponents
Dim view, record, componid, componpath, componversion, fullmsg
fullmsg = “Product ” & ProductGuid & vbcrlf
Set view = database.OpenView(“SELECT `ComponentId` FROM `Component`”)
view.Execute : CheckError
Do
 
Set record = view.Fetch : CheckError
  If record Is Nothing Then Exit Do
  componid = record.StringData(1)
  componpath=””
  componpath = installer.ComponentPath (ProductGuid,   
  componid)
  if componpath <> “” then
    
componversion = installer.fileversion (componpath,
    false)
    fullmsg = fullmsg & componpath & ” ” & componversion  
    & vbcrlf
  end if
Loop
msgbox fullmsg
End Sub

The beginning of the preceding code should look familiar from Chapter 2. This is a SQL-type query on the Component table to return ComponentID, which is the actual GUID for this installer component. Given the ProductCode GUID and the component GUID, the ComponentPath property of the installer object returns the absolute path to the component’s KeyPath. The ComponentPath property requires the ProductCode GUID as well as the com-ponent’s GUID. That’s because the same component might have been installed to any number of different locations by different products, so the owning product needs to be specified. Note that the installer object also has a FileVersion property that retrieves the version from the path to the file. If you wanted to find out the installation location of each component of an installation package, this is the general way to do so. Beware that a KeyPath can also be a Registry key, and write your code accordingly; in these cases the ComponentPath returned starts with a numeric representation of the Registry hive (such as “02” for HKLM).

The point of this exploration of relative paths was to show that Windows Installer is integrated into COM in a rather interesting way. However, to use actual side-by-side COM components you need to go a bit further. 

This chapter is from The Definitive Guide to Windows Installer by Phil Wilson. (Apress, 2004, ISBN: 1590592972 ). Check it out at your favorite bookstore today. Buy this book now.

{mospagebreak title=Back to Side by Side}

The idea of side-by-side COM is simply that each client installs its own private copy of a COM server in its own application folder with two requirements in the DLL case that

  1. The InProcserver32 path to the COM server is a relative one.

  2. The application folder contains a clue that the client program requires side-by-side sharing. This clue is in the form of a file with .LOCAL appended to the name of the client program executable.

To implement side-by-side COM with an installer package, you need to look at the IsolatedComponent table. The table itself is uncomplicated; a column called Component_Shared requires the installer Component table row that identifies the COM DLL, and a column called Component_Application that contains the Component table row that identifies the client program executable. In both cases, the KeyPath entry of the component identifies the file. However, there’s more to it than just setting these tables in the installation packages:

  1. There is clearly no sharing in the side-by-side scenario, so each installation uses its own private copy of the COM server and shouldn’t use a common Merge Module. It’s not that you cannot use a common shared Merge Module here, but if you do, the COM server will have the same installer component GUID associated with multiple products. So if the idea is truly to keep each COM server separate, then keep them separated in every way, including not sharing the same installer component GUID.

  2. The sharing schemes described earlier (the installer scheme and the SharedDLLs scheme) both have the notion that reference counts to an installation path protect the registration information for the COM server. When an uninstall occurs, these reference counts are decremented, and the last product using the COM server removes the registration entry. There is no such sharing scheme in a side-by-side installation, so an uninstall of a product using side-by-side sharing will potentially remove the COM registration entries unless some other mechanism is used to count the number of products using the COM server. The MSDN article “Implementing Side-by-Side Component Sharing in Applications” (in the Setup section) recommends a Registry item under the InprocServer32 key that is a reference count, incremented at install time and decremented at uninstall time, such that when decremented to zero the registration entries are removed.
  3. Everyone must agree to the sharing scheme. For example, if the  COM server is already installed on the system and the installation program adds an absolute path to the server, you can’t install your copy of the COM server in a side-by-side way.

With these caveats in mind, if you’re going to implement your own side-by-side installation, take a look at the IsolatedComponent table and use it to install the COM server in a side-by-side configuration. You can use the sample projects associated with this chapter. As well as the COMServer project that builds this COM server, a COMClient project builds a C++ executable that calls a method in the COM server. To perform side-by-side installations, you need two setup projects that each install the COMClient program and the COMServer DLL to their own private application folders. These two sample setup projects are named COMClientServerInst and COMClientServerInstAgain. Both were built from scratch and have the same general idea (see Figure 3-8).


Figure 3-8.  COM server with relative path

The client executable and the COM DLL are both being installed to the Application Folder, and the COM DLL is using the vsdrfCOMRelativePath choice for Register. VS has no explicit support for side-by-side COM components, which means you need to edit the IsolatedComponent table with Orca. Although the IsolatedComponent table requires Component values, it’s easiest to get those from the File table.

In Figure 3-9 the installer component for the COM DLL and the component for the client executable are in the Component_ column. The Component_ value for the server needs copying to the Component_Shared column of the IsolatedComponent table, and the Component_ value for the client needs copying to the
Component_Application column, so you end up with something like Figure 3-10. Follow this process of modifying the IsolatedComponent table with Orca wherever you are using side-by-side installation for a COM server. In the sample code with this chapter, this means modifying the COMServerInst and the COMServerInstAgain package files.


Figure 3-9.  The File table with COM client and server


Figure 3-10.  The IsolatedComponent table

If you install one of these packages, you’ll see that Windows Installer has helped out by creating the required .LOCAL file in each application folder, and the Registry entry for the class has the relative path that you’d expect from using the vsdrfCOMRelativePath choice. Installing both of the packages— COMClientServerInst and
COMClientServerInstAgain—also installs shortcuts to their respective client programs. If you use both of the shortcuts, the client programs each display a MessageBox. You can now use a diagnostic program to see what DLLs are loaded into each COMClient.exe (I used the Process Explorer from the Sysinternals Web site at http://www.sysinternals.co). You should see that each separate COMClient.exe is indeed using the COMServer.dll file from its application folder.

Now here’s something cool. If you install a single COM server that has the vsdrfCOMRelativePath option for registration and then uninstall it, you can go to the Registry and notice that the uninstall doesn’t delete all the registration entries for HKCRCLSID{cccxClass GUID}. It leaves some of the entries behind, including the relative path in the InprocServer32 key. Normally you’d consider that to be a bug, but think about the result. If you install both of the sample side-by-side setups and then uninstall one of them, that uninstall should delete all the registration info because that’s what you’d expect from an uninstall. But it doesn’t. What Windows Installer does at install and uninstall is just alter the contents of the InprocServer32 data descriptor that contains an encoding for installer product and component GUIDs. Windows Installer is effectively making side-by-side COM work by keeping those Registry entries present, and altering the content of the InprocServer32 descriptor as other products use the same COM class registration. The cool part about this behavior is the effect it has on other COM clients—those that don’t have their own side-by-side installation. If you run the Callcom.vbs script, the script continues to work when two products are installed, each with its own private side-by-side copy of the COM server. If you uninstall one of these products, that script still continues to work. That’s because, as you saw before when you ran this script, Windows Installer uses the InprocServer32 descriptor to get the installer component’s KeyPath and find the absolute path to the COM server.

In effect Windows Installer can give you all the available options for sharing side-by-side COM servers without having to write any code to do reference counting on the HKCRCLSID{Class GUID} key (as in point 2 in the preceding numbered list), which you’d need to do if you didn’t use Windows Installer. You get side-by-side sharing, you don’t need to build your own reference counting, and Windows Installer finds the latest version of the COM server for other non-side-by-side clients. It does this by finding the path to the server from the Inprocserver32 descriptor and the component KeyPath. When you uninstall the final side-by-side product, the InprocServer32 descriptor disappears as well, so there is no longer any path information anywhere.

Summary

You’ve looked at sharing, specifically in the COM context, and drilled down into some of the Installer’s interesting behavior. You saw that Windows Installer uses the descriptor associated with a COM server to locate the actual file, and you used a VBScript to locate an installed file in the same way. Because that descriptor can locate Installer components, it is the mechanism that Windows Installer uses to determine the health of the component and whether it needs repair. As you might guess, the repair that occurs when the target file of a shortcut is missing (see Chapter 2) is triggered by a similar descriptor associated with an advertised shortcut.

[gp-comments width="770" linklove="off" ]