Before even asking why one should do anything like this, let me clarify that this is a good technique to spice up a bit your VB6 application. Now I know this is not cutting edge but still it is important to a lot of people (as I found out already).

So let’s start then...

Create a new solution that includes a Windows Forms application (to test easily the functionality provided by the user control). Then add a library project that will wrap our user control.

What is demonstrated by this example is a very simple example that includes a label, and a button inside of a user control. When the button is clicked, the code in the Click eventhandler changes the text content of the label.

project 

The library

 

Open Property Settings window, select the Application tab (if not active). You should see an “Assembly Information…” button – click on it and it will open a dialog form with various assembly settings.

You will see a checkbox named “Make assembly COM-visible”. The checkbox must be checked.

 

appSettings 

Application Settings Tab

Also you will see a GUID field – it is used to set the ID of the typelib if this project is exposed to COM.

 

If you want automatic registration of the freshly created ActiveX wrapper you should check the “Register for COM interop” checkbox in the Build tab of the settings form.

buildSettings 

Build Settings

As far as I know the only thing it does is actually to run Regasm.exe after every build to register the types with COM (which you probably shouldn't do until you're stable because your COM GUIDs will change whenever you change the methods unless you specify GUIDs explicitly, so your registry could end up with a lot of registration junk for versions that will never be used).

If you're using VB.NET, you also need a strong named assembly. But it is considered a good practice so I’ve done it even in the C# project.

 

signSettings 

Sign Settings

 

This is all you have to do in the project settings, but we have unfinished business with the code base as well.

 

[ProgId("ActiveXTestLibrary.UserControl")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public partial class UserControl1 : UserControl
{

}

What we have here is the ProgId attribute which provides a unique name for the created ActiveX interface. It is uniquely identified by the assembly GUID, yet the GUIDs are not human-readable, and a second level of abstraction (easy referral) is provided by the (Programmatic IDentifier) or the ProgId attribute. For example, the ProgID for the Word automation object is Word.Basic.

 

The second ClassInterface attribute states whether and how an COM interface will be generated for the managed assembly. The behavior is controlled through an enum named ClassInterfaceType. You can find more on the topic here: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.classinterfacetype.aspx

Last but not least you can provide static methods that are invoked when the ActiveX is registered or unregistered through the Regasm.exe:

[ComRegisterFunction()]
public static void RegisterClass(string key)
{
    StringBuilder sb = new StringBuilder(key);
    sb.Replace(@"HKEY_CLASSES_ROOT\", "");

    // Open the CLSID\{guid} key for write access

    RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

    RegistryKey ctrl = k.CreateSubKey("Control");
    ctrl.Close();

    // Next create the CodeBase entry - needed if not string named and GACced.

    RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);
    inprocServer32.SetValue("CodeBase", Assembly.GetExecutingAssembly().CodeBase);
    inprocServer32.Close();

    k.Close();
}

[ComUnregisterFunction()]
public static void UnregisterClass(string key)
{
    StringBuilder sb = new StringBuilder(key);
    sb.Replace(@"HKEY_CLASSES_ROOT\", "");

    // Open HKCR\CLSID\{guid} for write access

    RegistryKey k = Registry.ClassesRoot.OpenSubKey(sb.ToString(), true);

    // Delete the 'Control' key, but don't throw an exception if it does not exist
    if (k == null)
    {
        return;
    }
    k.DeleteSubKey("Control", false);

    // Next open up InprocServer32

    RegistryKey inprocServer32 = k.OpenSubKey("InprocServer32", true);

    // And delete the CodeBase key, again not throwing if missing 

    inprocServer32.DeleteSubKey("CodeBase", false);

    // Finally close the main key 

    inprocServer32.Close();
    k.Close(); 
}

The methods have predefined names (RegisterClass and UnregisterClass) and should be marked respectively with the ComRegisterFunction/ ComUnregisterFunction attributes. Here in this example we register the category of the ActiveX as a UI control.

Testing the ActiveX control

You can do so by using the tstcon32.exe (ActiveXControlTestContainer) that is provided with your Visual Studio installation. You can invoke it easily through the Visual Studio Command Prompt which has all variables and paths preset correctly.

Unlocking locked assemblies

You can end up with locked assemblies due to the tstcon32 test. What you can do is use file unlock utility to kill the locking process. Then you could unregister the COM interface using the Regasm.exe /unregister option and then delete the file.

 


Comments

Comments are disabled in preview mode.