It's late but C89.5's Electrobox is still kicking so I thought I'd sit down and write about one of the topics at the root of my blog "thought tree". This thought tree is a text file I've been editing with notepad that captures random questions that I see go by (usually through email at work) whose answers deserve to be written down. The interesting thing is that many answers to seemingly innocuous questions require a bit of depth of understanding. For example, Siew Moi (pronounced "Sue May" in American and Australian English for those of you who haven't been formally introduced) asked for more detail about Merge Modules. I thought, sure Merge Modules are a great thing to talk about (and I happen to know a little bit about them) but the first thing I'd want to explain is why Merge Modules were created. To understand why Merge Modules were created you need to know about the Component Rules. To understand the Component Rules, I probably need to explain what Components are with respect to the Windows Installer. Fortunately, Components are one of the "root thoughts" of my tree so let's go dig into those and we'll take another step down the path to more advanced topics.
Not too long ago, I talked about resources and how they were the building blocks of a program. At that time, I also indicated that most programs of consequence are made up of a significant number of resources. For example, Microsoft Office Professional 2003 is composed of over 20,000 resources. Granted Office is a suite of several programs that are rather intricate themselves but even smaller line of business applications can have 50 or a 100 resources.
To help organize all those resources most installation technologies provide some sort of abstraction to group resources together. The Windows Installer calls its abstraction a Component, as if the word "component" wasn't overloaded enough in the world of computing. Thus it is important to note that these Windows Installer Components have nothing to do with the Component Object Model (COM) or any other definition of component you may have heard somewhere else. I wish I could say that the System Definition Model (the stuff Microsoft pays me to work on most days) didn't add another definition for component into the mix, but we do. Anyway, my point is that a Windows Installer Component, which I will refer to as just a Component from here out, is an entity all to itself with its own semantics independent from everything else that might share the name "component". If we're clear on that, I'll continue defining the Windows Installer Component.
In the Windows installer, Components are not only a way of collecting a set of resources to help with the organization; Components are the "atomic units of setup" in the Windows Installer. Let me come back to that very key statement after providing a few more facts. Components are identified by a globally unique identifier or GUID (pronounced "goo id" if you're from Office or "gwid" [sounds a lot like squid] if you're from VisualStudio). You'll find I often refer to the GUID of a Component as the ComponentId. Now since the ComponentId is globally unique it is possible to walk up to any machine and ask it, "Hey, do you have Component X installed?" This ability is key for more advanced tricks we can play with the Windows Installer that I'll cover in a future blog entry. See, I told you this Component stuff was the "root" of a lot of other ideas. <grin/>
Another important facet of Components is that they are tied to a single directory (a resource). This means that if any files (also resources) are contained in a Component they must all install to the same directory. Not all install engines have this restriction but requiring a Component to install all of its files to a single directory does simplify installation a little bit. This is especially true if you consider how the Windows Installer handles Components' KeyPaths.
The KeyPath for a Component is a single resource that the Windows Installer uses to determine if a Component "exists" on a machine. The KeyPath may be the directory of the Component, or a file contained in the Component, or a registry key contained in the Component, or an ODBC data source contained in the Component. Note that there can only be one KeyPath for a Component. If your Component has two files and three registry keys you have to pick one of those five resources or even the directory of the Component as the KeyPath. There are a lot of other implications to consider when picking the KeyPath of a Component but let me get back to my statement about Components being the "atomic units of setup".
When the Windows Installer decides it is going to install a Component it installs all of the resources in the Component together. There is one exception to that statement. When installing files that have versions, if a file already exists on the machine and that existing file has a higher version than the file in our Component the Windows Installer will skip over our Component's file. The Installer does this because one of the rules of setup is that files with higher versions should provide all of the functionality of files with lower versions and usually more. Installing a lower versioned file over the top of a higher versioned file very likely will decrease the functionality of the file and break any programs depending on that newer functionality. Of course, you can override that rule (the second axiom of setup at work) and force all the files in a Component to get installed even if some of the files exist with newer versions (note, this downgrading of files is dangerous and definitely should be done only as a last resort to get your program running again).
So how does the Windows Installer decide it's going to install a Component? Well, by checking for the existence of the KeyPath, of course. By default, the Windows Installer takes the resource that you marked to be the KeyPath for the Component and checks to see if that resource already exists on the machine. If the resource already exists then the Installer decides the Component does not need to be installed and moves on to the next Component's KeyPath. Again, with versioned files the file is determined to exist if a file of equal or higher version is already on the machine.
But what if you're Component has a bunch of resources in it and only the KeyPath resource already exists on the machine. In that case, none of your resources will be installed because the Installer has decided your Component doesn't need to be installed. This further explains why I said that Components are the "atomic units of setup". It is very much the case of all or nothing with Components. Therefore it is very important to pick the KeyPaths for your Components carefully.
Another option is to put only one resource in each Component. This approach will (for the most part) ensure every resource is appropriately tracked. However, Components are also what I like to call the "primary moving parts" of the install engine. More Components means more code to churn which translates into slower processing. There are also cases that I'll cover in the future where you cannot split the resources across Components. Ultimately, there is a balance to strike for your setup.
I've talked a lot about install but you are probably wondering about uninstall. Well, uninstall is where it really becomes clear that Components are the "atomic units of setup" in the Windows Installer. Remember the ComponentId? Well, the Windows Installer basically counts the number of times it has installed that ComponentId. In other words, every time a program is installed all of the Components that make up the program's setup have the count on their ComponentIds increased by one. This process is called "reference counting". Of course, when a program is uninstalled all of the Components have their ComponentIds decreased by one. When the count on a ComponentId reaches zero the Component must no longer be needed and the Windows Installer deletes all of the resources in the Component from the machine.
Now if you are really sharp you'll see a flaw in here. I'm going to save that discussion for later but suffice it to say this flaw provided one of the primary driving forces for Merge Modules.
There is more to say about Components but I think that's enough for one blog entry. Electrobox ended over two hours ago and in another couple hours the sun will start coming up. Until next time, keep coding, you know I will. <smile/>