Escape DLL Hell

Simplify App Deployment with ClickOnce and Registration-Free COM

Dave Templin

This article is based on a prerelease version of Visual Studio 2005. All information herein is subject to change.

This article discusses:

  • The benefits of Reg-Free COM
  • How to isolate existing COM components and ActiveX controls using Visual Studio 2005
  • The limitations of Reg-Free COM
This article uses the following technologies:
Visual Basic, COM, ClickOnce

Code download available at:RegFreeCOM.exe(1,392 KB)

Contents

Reg-Free COM Overview
Isolating COM Components
Isolating a Simple COM Component
A More Complex Example
Native Assemblies
Conclusion

If you're using Business Objects or UserControls written in Visual Basic® 6.0 or Active Template Library (ATL), then you're using COM. Prior to the Microsoft® .NET Framework, COM was the principal component model that applications in Windows® were built upon. But deploying applications with COM components can be difficult because in order for them to work, they need to be globally registered on the machine. Unfortunately, this global registration can cause unwanted side effects over the lifecycle of applications that use these components, unless they are managed very carefully. This is called DLL Hell.

These factors are generally not a problem in .NET because components do not require registration, and are either totally isolated to an application or are managed in a well-defined side-by-side way with the help of the Global Assembly Cache (GAC). Various migration wizards can help to move your code forward to the .NET Framework, but this isn't always practical. There's also the school of thought that says, "If it ain't broke, don't fix it!" Wouldn't it be great if you could deploy your existing COM components using a model similar to the .NET Framework? Well, starting in Windows XP, it turns out that you can!

Windows XP introduces a new COM activation model called registration-free COM, or Reg-Free COM for short. Simply stated, Reg-Free COM is a registry replacement for COM components. It works by expressing all of the standard component registration information that is typically installed into the system registry in a file that can be stored in the same folder as the application itself.

This article will show you how to deploy applications that use existing COM components using the new support in Visual Studio® 2005 for Reg-Free COM. This functionality allows such applications to be deployed with simpler deployment models such as XCOPY and ClickOnce, overcoming the traditional factors that make the deployment of COM components sometimes difficult.

ClickOnce is a new deployment technology for Windows Forms apps that is being introduced in the .NET Framework 2.0. It is a simplified application deployment model that works over the Web, from network shares, or from distributable media such as CDs. It also provides auto-updating capabilities, safe and isolated application management, and configurable application security. In a nutshell, it makes deploying and updating a Windows Forms application as easy and safe as a Web application. For more information, see the article on ClickOnce by Brian Noyes in the May 2004 issue of MSDN®Magazine.

Reg-Free COM Overview

Traditionally, when an application uses a COM component, the component must be registered for the operation to succeed, as shown in Figure 1. This is true whether the application is native or based in the .NET Framework. When an application instantiates a COM component, the activation generally boils down to the CoCreateInstance API. In simple terms, CoCreateInstance looks in the registry for the identifier (CLSID) specified by the application, finds the associated component module, and activates the corresponding class.

Figure 1 Traditional COM Activation

Figure 1** Traditional COM Activation **

The benefit of describing components in the system registry is that it enables them to be shared among multiple applications. It's also possible to register a component such that it can be installed and activated on another machine over the network. If your application is not using any of these capabilities, then this global machine registration can also be a huge liability for your application. First, ensuring every component is properly registered usually requires the overhead of a separate setup program, such as an MSI or EXE installer. Also, component registration may involve anywhere from dozens to thousands of registry keys, depending on the size of the component. If any one of these entries is corrupted, it can cause the application to fail. Having applications, components, and registration scattered all over the machine can make it very hard to properly manage application updates and uninstallation, particularly if some of the components are shared between multiple applications. This is probably COM's most prevalent problem as it makes some components and applications extremely fragile and difficult to maintain.

Reg-Free COM is different from traditional COM in that it doesn't require the component to be described separately in the system registry. It eliminates all of the previously mentioned problems, at the expense of maintaining the component definition within the scope of the application itself. It works by expressing all of the information that is typically installed into the system registry in an XML manifest. This is just another file in the application folder, as shown in Figure 2. If a component has a manifest, then during activation of the component, the operating system will look at the manifest before looking in the registry.

Figure 2 Isolated COM Activation

Figure 2** Isolated COM Activation **

The presence of an application manifest (such as the WindowsApplication1.exe.manifest) in the same folder as the program (WindowsApplication1.exe) is all that is required to enable the Reg-Free COM component model. This works because each time a program is run, the Native Assembly Loader looks for a manifest file based on the name of the executable. Like the component model for .NET assemblies, there is no extraneous component definition required outside of the application folder. When one or more manifests are present, Windows builds an internal table that relates each identifier (CLSID) to its corresponding component file. This is done early in CreateProcess, before any application code is run. Later, when the application instantiates a component, CoCreateInstance looks in this internal table before looking in the system registry. If all components are fully described in one or more manifests, absolutely no component registration is required in the system registry for the application to function properly!

The principal benefit of this model is that the COM component is totally isolated within the application. Even if other applications that use the same COM component (or a different version of it) require it to be registered, it will not interfere with this application in any way. There is also no way that the COM component for this application can be accessed by other applications. Therefore, problems due to version conflicts or the need to change a component's GUID may be sidestepped by using Reg-Free COM exclusively, although this should not be used as an excuse to dispense with any versioning strategies. Finally, update and uninstallation couldn't be simpler—just remove or replace the application folder.

Incidentally, existing code does not generally need to be altered to take advantage of Reg-Free COM. Setting up the proper configuration of manifests is all that may be required. In fact, the original intent of Reg-Free COM was to enable existing native applications, such as ones written in Visual Basic 6.0, C++, or some combination of languages. The focus of this article, though, is on using Reg-Free COM from .NET-based applications.

As I mentioned, if the Native Assembly Loader fails to bind to a COM component using the manifest, it will fall back to the global component registration, if present. This behavior can give a false sense that an improperly configured Reg-Free COM application is working properly. Therefore, when verifying that an application is correctly configured for Reg-Free COM, it's best to test on a clean machine. Otherwise, you'll have to take care to unregister all of the constituent components before testing to ensure that the application is running fully Reg-Free.

Isolating COM Components

Visual Studio 2005 introduces a new feature that allows you to isolate any given COM component with the simple flip of a switch. It works by automatically generating a manifest from the component's type library and component registration. Therefore, it is important to note that isolating a COM component requires that it be registered on the developer's machine. The requirement to register the component on end-user machines is removed.

Every COM reference in Visual Studio 2005 has a new property called Isolated. By default, this property is False, indicating that it should be treated like a normal, registered COM reference. If this property is True, it causes a manifest to be generated for the component at build time. It also causes the corresponding component files to be copied to the application folder. When the manifest generator encounters an isolated COM reference, it enumerates all of the CoClass entries in the component's type library, relating each entry with its corresponding registration data. In this manner, manifest definitions are automatically generated for all the COM classes in a file.

The ability to isolate a COM component was primarily designed to enable Reg-Free COM deployment of Visual Basic 6.0 COM components within Visual Basic .NET or C# applications. This feature is not limited to Visual Basic 6.0 components, though, and may work with any COM component that is suitable for use with Reg-Free COM. Not all application and component scenarios are suitable for use with Reg-Free COM, however. See the sidebar, "The Limitations of Reg-Free COM" for more information.

In the next two sections, I'll demonstrate exactly how this works. First, I'll create a simple COM component in Visual Basic 6.0 and show how to deploy it using Reg-Free COM. Then, we'll look at a slightly more sophisticated example that shows how to make several ActiveX® controls work under Reg-Free COM as well.

Isolating a Simple COM Component

Let's start by creating a simple COM component in Visual Basic 6.0. If you have Visual Basic 6.0, the steps to create the component manually are trivial. Alternatively, you can simply copy the VB6Hello.dll file from the sample code for this article and register it by typing "regsvr32 VB6Hello.dll" from a command prompt. Or, you could use ATL or any development environment capable of producing a COM DLL.

Using Visual Basic 6.0, create a new ActiveX DLL named "VB6Hello" and add the following code into the Class1 code module:

Public Sub SayHello() MsgBox "Hello from Visual Basic 6.0" End Sub

Build VB6Hello.dll by selecting the File menu and choosing the Make command. Note that only ActiveX DLL and ActiveX Control project types are supported with Reg-Free COM. ActiveX EXE and ActiveX Document project types cannot be used with Reg-Free COM, as discussed in the sidebar.

Next, we'll reference this COM component from a Windows Forms application. Start Visual Studio 2005, and create a new Visual Basic WindowsApplication project named "RegFreeComDemo1". You can also use C# or J# if you prefer, since the code will be trivial and easily adaptable to the language of choice. Select the Project menu, choose the Add Reference command, and browse to the VB6Hello.dll component produced from the code. Add a button to the form named "Say Hello", double-click on it to go to the code window, and add the following code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles Button1.Click Dim Vb6Obj As New VB6Hello.Class1 Vb6Obj.SayHello() End Sub

If you press F5 to run your application, you should see the appropriate message box when the button is clicked, just like the one shown in Figure 3.

Figure 3 RegFreeComDemo1

Figure 3** RegFreeComDemo1 **

Before we get to the Reg-Free COM part, let's take a moment to look at a few things. First, examine the set of files that were produced by the build: Interop.VB6Hello.dll, RegFreeComDemo1.exe, and RegFreeComDemo1.exe.config. RegFreeComDemo1.exe is, of course, the main program file. Interop.VB6Hello.dll is a COM interop assembly generated automatically by the build. The other files not shown are supporting files and do not pertain to our discussion. Notice, though, that the VB6Hello.dll COM component is not present in the folder.

Try unregistering the COM component (for example, type regsvr32 /u VB6Hello.dll from a command prompt) and then running RegFreeComDemo1.exe from outside of the Visual Studio IDE. You should see that the application now fails when the button is clicked. If you unregistered your COM component, you should reregister it before moving on, or you will not be able to rebuild your application within Visual Studio 2005.The Limitations of Reg-Free COM

Reg-Free COM provides some pretty clear advantages over traditional deployment techniques. There are, however, a few limitations and caveats that should also be pointed out before you get started.

Of course, the biggest limitation of Reg-Free COM is that it only works on the Windows XP operating system or higher. It also requires changes to the way in which components are loaded in the core operating system. Unfortunately, there is no down-level support layer for Reg-Free COM.

Not every component is a suitable candidate for use under Reg-Free COM. A component is not considered suitable if any of the following are true:

  • The component is an out-of-process (ActiveX EXE) server. Only DLLs are supported.
  • The component is a system component or part of the operating system, such as XML, Data Access, Internet Explorer, or DirectX® components. For example, you should not attempt to isolate components such as Microsoft XML (MSXml.dll) or Microsoft Internet Controls (SHDocVw.dll). These components are either part of the operating system, or can be installed with a separate redistributable package.
  • The component is part of an application, such as Microsoft Office. For example, you should not attempt to isolate components such as the Microsoft Word Object Model or Microsoft Excel Object Model. These two components are part of Office and can only be used on a machine that has the full Office product installed on it.
  • The component is intended for use as an add-in or a snap-in, such as an Office add-in or a control in a Web browser. Such components typically require some kind of registration scheme defined by the hosting environment that is beyond the scope of the manifest itself. The other problem is an arbitrary application may not be designed to recognize isolated components, as it probably doesn't have a way to reference your component through a manifest.
  • The component manages a shared physical or virtual system resource. For example, it could manage some kind of data connection shared between multiple applications or a device driver for a print spooler.

Data applications generally require a separate Data Access redistributable to be installed before they can be run. You should not attempt to isolate components such as the Microsoft ADO Data Control, Microsoft RemoteData Control, Microsoft DAO Object Library, Microsoft OLE DB, or Microsoft Data Access Components (MDAC). Visual Studio 2005 simplifies the process of deploying data applications with the appropriate redistributables using the new Visual Studio 2005 Bootstrapper feature. If you are using MDAC or SQL Server Express, you can simply check the corresponding item in the Prerequisites dialog on the Publish page in project properties.

In some cases it may be possible for the developer of the component to redesign it for suitable use under Reg-Free COM. If you have components that simply are not suited for use with Reg-Free COM, you can still build and publish ClickOnce applications that depend on them through the standard registration scheme using the Bootstrapper. For more information, see Sean Draine's article on the Visual Studio 2005 Bootstrapper in the October 2004 issue of MSDN Magazine.

As mentioned in the main body of this article, the isolation feature works by examining the component's type library and registration. A build warning will result if any nonstandard component registration is encountered while isolating a COM component. The manifest schema is a closed model that only supports standard COM registration semantics. Such warnings indicate that the component may not be suitable for use under Reg-Free COM.

A COM component can only be isolated once per application. For example, you can't isolate the same COM component from two different ClassLibrary projects that are part of the same application. Doing so will result in a build warning, and the application will fail to load at run time. In order to avoid this problem, Microsoft recommended that you encapsulate COM components in a single class library.

There are several scenarios in which COM registration is required on the developer's machine, even though the deployment of the application does not require registration. The Isolated feature always requires that the COM component be registered on the developer's machine in order to auto-generate the manifest during the build. There are no registration-capturing capabilities that invoke the self-registration during build. Also, any classes not explicitly defined in the type library will not be reflected in the manifest. When using a COM component with a preexisting manifest, such as a native reference, the component may not need to be registered at development time. However, registration is required if the component is an ActiveX control and if you'd like to work with it in the Toolbox and the Windows Forms designer.

Each COM component used by your application is represented in your project as a COM Reference. These references are visible under the References node in the Solution Explorer window. If this node is not visible, click the Project menu and select the Show All Files command.

Now let's isolate this COM component. Expand the References node in the Solution Explorer window. Select the VB6Hello item under the References node and set the Isolated property to True in the Properties window, as shown in Figure 4. If the Properties Windows is not visible, press F4.

Figure 4 Isolation

Figure 4** Isolation **

When you press F5, the application works as expected, although it is now running under Reg-Free COM. In order to prove this, try unregistering the VB6Hello.dll component and running RegFreeComDemo1.exe outside of the Visual Studio IDE as before. This time when the button is clicked, it still works! If you temporarily rename the application manifest, it will again fail.

Let's take another look at the set of five files that were produced by the build: Interop.VB6Hello.dll, RegFreeComDemo1.exe, RegFreeComDemo1.exe. con- fig, RegFreeComDemo1.exe.manifest, and VB6Hello.dll. The last two files in this list are new after isolating the COM component. VB6Hello.dll is now copied to the application folder, as is an application manifest file named RegFreeComDemo1.exe.manifest. It is this manifest file that makes Reg-Free COM work. If you XCOPY this set of files to another machine running Windows XP with just the .NET Framework 2.0 installed, the application will run without any registration. Since Windows XP includes the Visual Basic 6.0 runtime libraries, you don't have to worry about having to install MSVBVM60.dll, OLEAUT32.dll, and so on.

The application manifest is actually an XML document, so you can examine it using any text editor. The contents of this file are shown in the code in Figure 5. The hash and GUID identifiers may be different depending on whether you used the exact same files I did. Note the <file> element with <comClass> and <typelib> subelements. As you can see, the class and type library GUIDs are declared in the manifest. This is the same information that traditionally appears in the registry.

Figure 5 Reg-Free COM Application Manifest

<?xml version="1.0" encoding="utf-8"?> <assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <assemblyIdentity name="RegFreeComDemo1.exe" version="1.0.0.0" type="win32" /> <file name="VB6Hello.dll" asmv2:size="20480"> <hash xmlns="urn:schemas-microsoft-com:asm.v2"> ••• </hash> <typelib tlbid="{ea995c49-e5d0-4f1f-8489-31239fc9d9d0}" version="2.0" helpdir="" resourceid="0" flags="HASDISKIMAGE" /> <comClass clsid="{97b5534f-3b96-40a4-88b8-19a3bf4eeb2e}" threadingModel="Apartment" progid="VB6Hello.Class1" tlbid="{ea995c49-e5d0-4f1f-8489-31239fc9d9d0}" /> </file> </assembly>

If you've used ClickOnce before, you may have noticed that the ClickOnce application manifest is stored in the same file as the one I've discussed. It turns out that ClickOnce and RegFree COM definitions can coexist in the same file. Let's see what this same application looks like when published using ClickOnce. In Visual Studio 2005, right-click the project in the Solution Explorer and select the Publish command to publish the project using the Publish Wizard. The contents of the ClickOnce version of the application manifest are available in the download for this article.

The application manifest includes a superset of the definitions from the simple application manifest. It contains the same <file> element with <comClass> and <typelib> subelements, but it also includes a definition for all the files in the entire application. In addition, ClickOnce requires a hash for all application files and assemblies so that it can guarantee application integrity. Another key difference is that all ClickOnce applications must be signed, whereas a manifest intended only for XCOPY deployment does not need to be signed. The application security is defined as full trust. Any applications that directly reference native code (including any COM components) are full trust by definition. Visual Studio 2005 will emit a build warning if you are using partial trust with one or more COM references.

Lastly, you should be aware that the minimum required OS version is automatically set to Windows XP when using Reg-Free COM, so end-users will be blocked from installing the application with a descriptive error message telling them that they need to install a newer version of Windows in order to use the application.

Seeing an application manifest that contains both ClickOnce and Reg-Free COM definitions demonstrates an interesting point about the structure of ClickOnce application manifests. It shows that they are an extension of the existing manifest format that was introduced in the operating system starting with Windows XP.

This example demonstrated how to call into a simple component using Reg-Free COM, though this could just as easily have been a more complex component such as a Business Object. In the next example, you'll see how ActiveX controls work just as well under Reg-Free COM.

A More Complex Example

In this example we'll use a pre-built component called "VB6ControlTest" supplied with the sample code for this article. This is a Visual Basic 6.0 UserControl that uses several other ActiveX controls that come with the Visual Basic 6.0 development environment. Copy VB6ControlTest.ocx from the sample code to your machine now and register it.

Start Visual Studio 2005 and create another new Visual Basic WindowsApplication project named "RegFreeComDemo2". Next you'll add the VB6ControlTest component to your project, but since this is an ActiveX control, you need to add it through the Toolbox. Click on the View menu and select Toolbox. Right-click the General tab and select the Choose Items command. From the Choose Toolbox Items dialog, click the COM tab and browse to VB6ControlTest.ocx. Drag the VB6ControlTest.UserControl1 control to your form and resize it so that the entire tab control is visible. When you've completed all of this, you should have something that looks like Figure 6.

Figure 6 RegFreeComDemo2 Program

Figure 6** RegFreeComDemo2 Program **

Now let's isolate all of the ActiveX controls. Expand the References node in the Solution Explorer window. Set the Isolated property to True for both VB6ControlTest and AxVB6ControlTest items. We've isolated the UserControl, but we'd also like to isolate the controls referenced by the UserControl. To achieve this, you simply add a reference to these additional controls from the .NET-based application and isolate them from there. Using the Add Reference command, go ahead and add the following items from the COM tab, and then isolate them:

  • Microsoft Common Dialog Control 6.0 (SP6)
  • Microsoft Rich Textbox Control 6.0 (SP4)
  • Microsoft Tabbed Dialog Control 6.0 (SP4)
  • Microsoft Windows Common Controls 6.0 (SP6)
  • Microsoft Windows Common Controls-2 6.0 (SP3)

Be careful which controls you choose to isolate. As a guideline, any controls that are purely UI widgets are likely to work just fine under Reg-Free COM, but you should always fully test them with your application to be sure. See the sidebar for more information.

Publish the project using the Publish Wizard. If you have another machine with just the .NET Framework 2.0 installed, go there now and install the ClickOnce application. There you have it—a .NET application with several ActiveX controls deployed smoothly using ClickOnce. A limited user could install it with no fuss, and there's no DLL Hell to worry about.

Native Assemblies

The Isolated property makes it really easy to isolate and deploy existing COM components that don't already have a manifest. However, if a component is supplied with a manifest from the vendor or component owner, you can reference the native assembly directly. In fact, you should always prefer using the manifest supplied by the author of the component wherever possible over the Isolated flag.

Visual Studio 2005 supports references to native assemblies from Visual Basic .NET or C#. A native reference is created when a reference's File Type property is set to Native in the Properties window. To add a native reference, select Project | Add Reference, then click the Browse tab and navigate to the location of the manifest. Some components place the manifest inside the DLL. In this case, you can simply choose the DLL itself and Visual Studio will add it as a native reference if it detects that the component contains an embedded manifest.

Incidentally, native assemblies can define additional files associated with the component, and may refer to other dependent native assemblies. It is possible, then, to encapsulate an arbitrarily complex dependency graph of native assemblies and files in a custom authored manifest. Visual Studio 2005 will automatically include any such files or dependent native assemblies expressed in the manifest if they are in the same folder as the referenced component.

The format of a manifest is plain XML. Therefore, if you are a component owner and you wish to make your component into a native assembly, you can author a manifest manually. You can use the MT tool in the Platform SDK to perform tasks such as computing hash codes, manifest embedding, and generation of Reg-Free COM entries from an ATL RGS script.

You can, of course, use Reg-Free COM and native references from within a Visual Basic .NET or C# class library. Interestingly, the resulting component is made up of both a managed assembly (the .dll) and a native assembly (the .manifest). When referencing the component from another project, you need two references to consume the component properly. When using a project-to-project reference (for example, choosing Add Reference and selecting the component as a project in the same solution), both assemblies are referenced automatically. Otherwise, you must choose Add Reference twice. The first time choose Add Reference and browse to the DLL; then choose Add Reference again and browse to manifest.

Conclusion

Reg-Free COM is a great way to overcome the headaches that typically go along with deploying applications that use COM components. It works for ActiveX controls, but it can also work with nonvisual business objects. It brings native and COM components to level of parity with managed components in terms of both application isolation and ease of deployment. The only significant drawback is the higher minimum-platform requirement of Windows XP. But as more and more desktops are migrated to newer platforms, the benefits offered by these new capabilities become increasingly more compelling.

Dave Templin is a developer on the Visual Basic team at Microsoft and is the lead for all of the great new ClickOnce support in Visual Studio 2005. Dave can be reached online at blogs.msdn.com/dtemp.