ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Hosting Windows Forms Controls in COM Control Containers

by Chris Sells
01/20/2003

As wonderful as the new Windows Forms control model is, if you still need to host controls in COM control containers, you are limited to exactly one control host that also supports the hosting of Windows Forms controls: Internet Explorer. However, if you're brave and can stand the heat from the kitchen, you may have some luck hosting Windows Forms controls in other COM control hosts, including VB6, Office, ATL, and MFC. Since a Windows Forms control implements COM control interfaces and behavior, this can be made to at least seem to work (although it is not supported by Microsoft, even a little bit).

The first step to enabling hosting of a Windows Forms control in a COM control host is to set Project->Properties->Configuration Properties->Build->Register for COM Interop to true on your VS.NET Windows Forms control project. When the project is built, this setting will cause most of the COM registration entries to be added, as shown in Figure 1.

Base COM interop settings
Figure 1: Base COM interop settings

In addition, the Register for COM Interop setting will cause a COM type library to be created and registered. This is the equivalent of using the tlbexp.exe and regasm.exe tools from the command line, but is automatically handled as part of the build in VS.NET. These keys are enough to support CoCreateInstance from C++ or New from VB6, but they're not enough to support COM control creation. Enabling that requires four more keys, as is shown in Figure 2.

Minimum COM Control interop settings
Figure 2: Minimum COM Control interop settings

The Control sub-key marks the COM class as a COM control for MFC's Insert ActiveX Control dialog. The MiscStatus is a set of bits from the OLEMISC enumeration. A value of 131457 works well (see the References section for more information about these bits). The TypeLib key and Version keys point to the COM type library exported by the VS.NET build when Register for COM Interop is set to true.

Getting these keys into the Registry requires some additional work, as the Register for COM Interop setting in VS.NET won't do it for you. Instead of adding these keys by hand, or even writing a .reg file or an installer to do it, I recommend adding a ComRegister function to your Windows Forms control class:

	
using System.Runtime.InteropServices; // COM attributes
using Microsoft.Win32; // RegistryKey

namespace WindowsControlLibrary1 {
  // Stops control from getting a new CLSID
  // at each registration
  [Guid("E73CD054-1247-4853-AF05-B7D26D993E85")]
  public class UserControl1 : UserControl {
    ...
    [ComRegisterFunction]
    static void ComRegister(Type t) {
      string keyName = @"CLSID\" + t.GUID.ToString("B");
      using( RegistryKey key =
               Registry.ClassesRoot.OpenSubKey(keyName, true) ) {
        key.CreateSubKey("Control").Close();
        using( RegistryKey subkey = key.CreateSubKey("MiscStatus") ) {
          subkey.SetValue("", "131457");
        }
        using( RegistryKey subkey = key.CreateSubKey("TypeLib") ) {
          Guid libid = Marshal.GetTypeLibGuidForAssembly(t.Assembly);
          subkey.SetValue("", libid.ToString("B"));
        }
        using( RegistryKey subkey = key.CreateSubKey("Version") ) {
          Version ver = t.Assembly.GetName().Version;
          string version =
            string.Format("{0}.{1}",
                          ver.Major,
                          ver.Minor);
          if( version == "0.0" ) version = "1.0";
          subkey.SetValue("", version);
        }
      }
    }

    [ComUnregisterFunction]
    static void ComUnregister(Type t) {
      // Delete entire CLSID\{clsid} subtree
      string keyName = @"CLSID\" + t.GUID.ToString("B");
      Registry.ClassesRoot.DeleteSubKeyTree(keyName);
    }

  }
}

A static function with the appropriate signature and marked with the ComRegisterFunction attribute will be called as part of the COM registration process for a .NET class. Likewise, a function marked ComUnregistrationFunction will be called when the class is unregistered. This allows you to write the code that adds the keys necessary to make the WinForm control act like a COM control. Once this code is added and your Windows Forms control is built (and registered for COM interop), you can use the Windows Forms control as a COM control. For example, to host the Windows Forms control in an MFC application, right-click on a dialog in design mode and choose Insert ActiveX Control, choosing your Windows Forms control by namespace and class name, e.g. WindowsControlLibrary1.UserControl1.

Where Are We?

I cannot stress enough just how unsupported this technique is. My understanding is that the Windows Forms team spent all of their energy testing that Windows Forms controls work in supported ways for supported Windows Forms and COM Control hosts, and in no other ways in other hosts. The fact that these Registry tricks seem to work is probably just an illusion for the unwary. However, if you need to play with fire, at least I've helped you to learn how to properly light the match.

Chris Sells is a Microsoft Software Legend and a Program Manager with the Distributed Systems Group at Microsoft. His weblog at http://www.sellsbrothers.com is popular with .NET developers for its zany and independent commentary on technology and geek culture.


Return to ONDotnet.com

Copyright © 2009 O'Reilly Media, Inc.