Here are the relevant steps, documented externally . This is summarized, omitting some of the statements, but not the necessary steps.
This example is also very similar to the article “ Using Managed Controls as ActiveX Controls” written by Harry Trinder on November 25, 2008, and I also included some notes from this article.
Providing Windows Forms Controls as ActiveX Controls
This article will describe how to use Windows Forms controls outside of .NET.
Writing control
- Create a new management project from Visual Studio - all my examples are in C #, but VB.NET can also be used.
[Here Harry's article suggests: "First, create a usercontrol managed project — either a Windows Forms class library or a control library project. Use the usercontrol constructor to create your own user control the way you want it (using any standard controls, which you like). " ]
Add controls, etc. In the form, enter the code, etc.
Add the following items using ...
using System.Runtime.InteropServices; using System.Text; using System.Reflection; using Microsoft.Win32;
- An attribute of your class so that it gets a ProgID. This is not strictly necessary since it will be generated, but it is almost always better to be explicit.
[ProgId("Prisoner.PrisonerControl")] [ClassInterface(ClassInterfaceType.AutoDual)]
This assigns a ProgID, and also determines that the accessible interface should be "AutoDual" - this robs you of the default interface for all open, non-static members of the class. If this is not what you want, use one of the other options.
- Update the project properties so that your assembly is registered for COM interoperability.
If you use VB.NET, you also need an assembly with a strong name. Curiously, in C # you are not doing this - and it seems to be more of an environment function than a compiler or CLR function.
- Add the following two methods to your class.
[ComRegisterFunction()] public static void RegisterClass ( string key ) {
The RegisterClass function is assigned to the ComRegisterFunction - this static method will be called when registering the assembly for COM interaction. All I do here is add the keyword “Control” to the registry and add the CodeBase to the entry.
CodeBase is interesting - not just for .NET controls. It defines the URL path by which you can find the code, which can be a build on disk, as in this case, or a remote build on a web server. When the runtime attempts to create a control, it checks this URL and loads the control if necessary. This is very useful when testing .NET components, since the usual warning that they are in the same directory (etc.) as .EXE does not apply.
[ComUnregisterFunction()] public static void UnregisterClass ( string key ) { StringBuilder sb = new StringBuilder ( key ) ; sb.Replace(@"HKEY_CLASSES_ROOT\","") ;
The second function will delete the registry entries added when (if) the class is not registered - it is always a good idea to put things in order.
Now you are ready to compile and test your control.
Additional notes from Harry's blog:
[The] additional registry entries: Control , MiscStatus , TypeLib and Version [can be created] using the .REG script, but as a rule, it is better to write functions that will be called when registering / unregistering
It describes the registry keys in some detail:
Control is an empty subkey. TypeLib maps to TypeLib GUID (this is the assembly level GUID in assemblyinfo.cs). Version is the major and minor version numbers from the build version. The only slightly interesting subsection is MiscStatus . This should be set to a value composed of (bitwise) values in the OLEMISC enumeration documented here . To make this enumeration available, add a reference to Microsoft.VisualStudio.OLE.Interop (and the appropriate using statement for the namespace).
His last comment is a warning:
Note: this seems to work fine for Excel (with very limited testing that I did), partially works with PowerPoint, but fails miserably with Word. Perhaps some additional OLEMISC values can improve this; perhaps there are some messages that we need to intercept; maybe there are a few more interfaces that we need to implement ... The fact that Ive only barely made it work in a very limited way should tell you that this is probably not the method you want to use in any serious way.