September 1998
Gain Control of Application Setup and Maintenance with the New Windows Installer |
The new Windows installer offers your app some cool features that go well beyond anything available now. What if your app could automatically repair itself, restoring missing files from the installation media when they are referenced? Sound pretty neat? Well, read on for all the details. |
This article assumes you're familiar with C++, Win32, COM |
Mike Kelly is a software developer on the Microsoft Office team, who is working to make Office setup make sense by using the new Windows installer. He can be reached at mikekell@microsoft.com..
|
Compared to some
of the topics covered in Microsoft Systems Journal, like DirectX® and Microsoft® Transaction Server, an article on setup might seem like pretty dry stuff. But the new Windows® installer offers your application some cool features that go well beyond anything available now. What if your application could automatically repair itself, restoring missing files from the installation media when they are referenced? How about adding your application's icon to the Start menu of thousands of machines in an enterprisewithout having to actually install any of the application's files on a machine until a user selects that icon? What if you could advertise all the COM servers in your app to those machines without actually installing anything, and have the system automatically install the COM server when it is first instantiated by any client?
Sound pretty neat? Well, read on for all the details. There are a few key ideas behind the Windows installer. It will make application installation and ongoing management part of the basic Windows system services. This enables the system itself to track what is installed and better manage components that are shared by applications. The installer should persuade developers to think of setup not as a one-time process customers run, but as an integrated part of an application. It should support software installation on "locked down" machines, where ordinary users don't have privileges to do the things that many application installation programs need to do. The Windows installer is a key part of the Zero Administration Windows initiative. Finally, the installer should use the integrated Directory Service (expected to be introduced with Windows NT® 5.0) to introduce two new models of application deployment within large, distributed organizations: assignment and publishing. I'll describe all of the Se in more detail in this article. First, though, let's review the various setup approaches Windows-based applications use today.
Setup Today
How the Windows Installer Helps
|
Figure 1 Windows Installer Architecture |
Products, Features, and Components
Installation Database
EasyMail
|
|
EasyMail is composed of an email editor, a reader, support for various protocols (SMTP, IMAP, and so on), and file converters to enable displaying and sending attachments. the Se features are identified internally with short names such as MailEditor, MailReader, and IMAP. They are identified to users with a display name such as EasyMail Editor. Remember that a feature is the smallest installable unit of functionality; from a user point of view, this maps to a checkbox in the setup dialog.
Now that you've determined how you'll divide your functionality into features, the next step is to divide the files your product installs into components. Remember that components are the smallest unit of sharing. Every change your product makes on a user's machine when it is installed is part of some componentevery registry setting, shortcut, or file. There is a many-to-many relationship between features and components; a feature is composed of one or more components, and a single component may be part of multiple features. Suppose your editor and reader share EASYSYS.DLL, which provides a set of common functionality used by both features. Because you've separated the editor and the reader into features, either or both can be installed. However, if either one is installed, you'd like this common DLL to be there. And since you're a forward-looking developer, you might want future products to use this handy DLL as well. Make EASYSYS.DLL a component. Both features will be able to use the component and your future products will also be able to benefit from sharing that common DLL with EasyMail if it is already installed. If registry entries need to be written or COM servers need registration, you link that to the component as well. The installer will automatically write those registry entries and register those COM servers when the component is installed. Better yet, the installer will also automatically remove those entries and unregister those servers when the component is uninstalled. One warning, though: components are shared among all installed products, so be sure you think carefully about components' contents as you design your components. As a forward-thinking developer, you'd probably also like EasyMail to be available in other languages. You've split all your strings and UIs into resources and are careful to use the Windows National Language System APIs in your code rather than assuming things like the format of dates and numbers. the Only thing you aren't doing quite right is that your localized resources are in your main executable; it isn't possible for a user to install multiple languages of EasyMail without installing copies of a lot of things that aren't localized, such as all the code pages in your main executable and DLLs. In many countries in Europe and the Far East, installing support for more than one language is common. The right approach is to put all your localized resources in a separate, per-language DLL. This DLL has no code, only resources. You can load it at runtime using LoadLibrary with the LOAD_LIBRARY_AS_DATAFILE flag. When you do things like LoadResource or LoadString, you refer to the HINSTANCE of the resource DLL rather than your main DLL. Use the locale identifier (LCID) as part of the name for this resource DLL so you can ship support for as many languages as you want and the DLLs can coexist. For instance, your U.S. English resource DLL will be called EASY1033.DLL. The Japanese one, EASY1041.DLL. Installer-qualified components will help you easily find a particular language DLL at runtime. Using a qualified component is one way to do a single-level indirection. Normal components have a single identifier: the component ID GUID. Qualified components have two identifiers: a category GUID and a qualifier, which is just a text string. Qualified components use the identifier to map to a real, nonqualified component. Another way of describing this is that a qualified component is a dynamic array of components where the name of the array is the category GUID and the array index is a string. I decided to use a qualified component to represent EasyMail Language Resources, and to use the LCID of the language as the qualifier. I can easily map the LCID to a display name for the UI using GetLocaleInfo. Using the installer API MsiProvideQualifiedComponent, I can find the path to a particular per-language resource DLL, and even have the installer automatically install it if it isn't present. It's also possible to enumerate the qualifiers for a qualified component. In this case, that would provide the available LCIDs (the available languages for which EasyMail can present a UI). Even if I only ship U.S. English support now, I can write code that uses qualified components. If I later decide to ship a language pack that supports other languages, it will populate this qualified component and, suddenly, EasyMail will know how to speak French and German. Cool, eh? Of course, it's possible to do something like qualified components yourself; you can use the registry to register your language resource DLLs and look up the path to a language DLL in your code using the registry. The installer just makes it easier, and it gives you on-demand installation for free. Since components are global, so are qualified components. For example, a File Converters qualified component (with an associated, well-known category GUID) that publishes available file format converters could be accessed from multiple products. By agreeing on the qualifier (perhaps some encoding of file type), multiple products could install and share converters. Better yet, the advertisement for the converter can be present without the actual bits being installed.
Installer Programming Interface
There are several ways that Windows-based applications access functionality implemented external to the current executable or DLL. You may use COM (via CoCreateInstance); you might find the path to a DLL using the registry; or you might just assume that a given file is in a particular directory relative to your executable. CoCreateInstance remains unchanged, but for the Other two, you'll want to use installer APIs instead of your existing mechanism. First, let's address COM and CoCreateInstance. Because the new installer is part of Windows, COM has been modified to work with it. When you call CoCreateInstance, you pass the CLSID of the COM class you want to instantiate. Previously, COM would look in the registry to find the absolute path to the executable or DLL that is the server for that class and launch it (for EXE servers) or load it (for in-process servers). Starting with Windows NT 5.0, COM (through OLE32.DLL) first looks for an MSI Descriptor under the CLSID key in the registry. If present, COM knows this class was installed with the Windows installer and invokes the installer to give the path to the COM server. This allows the installer to install the server bits if the server is advertised. If the server is already installed but the user has somehow removed the DLL or EXE supporting the server, the installer will detect that and automatically reinstall it, providing resiliency for COM servers. So for functionality you access through COM, your application doesn't need to do anything new. You should figure out other ways to find external components to use the installer interfaces. You could, of course, mimic COM and write some sort of descriptor to the registry in place of paths and have your app continue to look up those descriptors from the registry and translate the descriptor to a path using the installer APIs. But why are you writing to the registry at all? Most applications do this to allow their setup program to communicate with the running application through the registrythe setup program writes the path to the bits in the registry and the application reads the path from there. You can accomplish this goal without using the registry simply by using an installer component. EasyMail can call MsiProvideComponent to find the path to any component given the component ID. The installer will automatically install the component if you pass the flag INSTALLMODE_DEFAULT as the dwInstallMode parameter, or only return the path to an already available component (or an error code if the component is not installed) if you pass INSTALLMODE_ EXISTING. This does three things for your application. First, it reduces your dependency on fragile, registry-based communication between your setup program and your application. It also simplifies your setup since you no longer need to write the Se registry values. Second, it makes your application more resilient; even if the user inadvertently deletes a necessary file, the installer can automatically reinstall it when your application looks for it. Third, it allows users to make any feature "run from installation source" without any additional work by your application. The user (or administrator) can determine the right tradeoff between local disk usage and speed. If for some reason you want to allow administrators to override the setup-provided bits for a component, you can still check the registry setting and use the path contained in the registry if it is set. You might want to do this, for example, for a dictionary; you provide the default dictionary, but administrators can substitute their own dictionary by pointing a registry setting to it. If the registry is not set, you fall back on the installer to provide the default bits.
Qualified Component APIs
Installation and Configuration Functions
Other Installer APIs
Conclusion
From the September 1998 issue of Microsoft Systems Journal.
|