How to create an ActiveX control in C #?

I cannot create a working ActiveX control in C #; I tried the following lessons to do this without success.

I am creating an example class library project that includes this code:

namespace AACWCSurvey { [ProgId("Prisoner.PrisonerControl")] [ClassInterface(ClassInterfaceType.AutoDual)] public class Class1 { public Class1() { MessageBox.Show("FIRETRUCK!!!"); } } } 

Then I took the following steps:

  1. Properties => Application => Assembly Information => Make Assembly Visible to COM
  2. Build => Register for COM interoperation TRUE (verified)
  3. Make a strong name for the assembly (signature)
  4. Build a project
  5. regasm MyDll.dll/tlb/codebase

  6. Cannot see Prisoner.PrisonerControl in tstcon32 = (

My OS is WinXP x86.


UPD: this works from VBScript:

 Dim objJava Set objJava = WScript.CreateObject("Prisoner.PrisonerControl") 

but this is not visible in tstcon32.

+5
source share
3 answers

If you read a real article using the Prisoner.PrisonerControl control, then a subsection called Control is created inside the key with your control GUID.

On my machine using the {9DEA5F06-E324-31A7-837B-D0F3BDE91423} key creation

 HKEY_CLASSES_ROOT\CLSID\{9DEA5F06-E324-31A7-837B-D0F3BDE91423}\Control 

Make management appear in tstcon32 . And with or without ActiveX can be used for JavaScript

 var x = new ActiveXControl("Prisoner.PrisonerControl"); 

In fact, I had to deal with windows both for javascript execution and for the registry path in order to test it on my system, because it is an x64 machine, but this is a different story.

+2
source

You created a COM server, but not an ActiveX control, which is a much more complex COM object that you can use with tstcon32.exe.

It should implement several interfaces, the key ones of which are IOleObject and IOleWindow. A type of interface that allows it to carry out necessary negotiations with the ActiveX host and create a visible window. The Winforms Control class is your best bet to create one.

+2
source

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

  1. 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). " ]

  1. Add controls, etc. In the form, enter the code, etc.

  2. Add the following items using ...

 using System.Runtime.InteropServices; using System.Text; using System.Reflection; using Microsoft.Win32; 
  1. 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.

  1. 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.

  1. Add the following two methods to your class.

 [ComRegisterFunction()] public static void RegisterClass ( string key ) { // Strip off HKEY_CLASSES_ROOT\ from the passed key as I don't need it 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); // And create the 'Control' key - this allows it to show up in // the ActiveX control container 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 ( ) ; // Finally close the main key k.Close ( ) ; } 

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\","") ; // 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 k.DeleteSubKey ( "Control" , false ) ; // Next open up InprocServer32 RegistryKey inprocServer32 = k.OpenSubKey ( "InprocServer32" , true ) ; // And delete the CodeBase key, again not throwing if missing k.DeleteSubKey ( "CodeBase" , false ) ; // Finally close the main key k.Close ( ) ; } 

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.

0
source

All Articles