问题
I am not able to create a functioning ActiveX control in C#; I have tried following tutorials to do so without success.
I create a sample Class Library project which includes this code:
namespace AACWCSurvey
{
[ProgId("Prisoner.PrisonerControl")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Class1
{
public Class1()
{
MessageBox.Show("FIRETRUCK!!!");
}
}
}
I then did the following steps:
- Properties => Application => Assembly Information => Make Assembly COM-visible
- Build => Register for COM interop TRUE (checked)
- Make Strong name for assembly (signing)
- Build the project
regasm MyDll.dll /tlb /codebase
Can't see
Prisoner.PrisonerControl
in tstcon32 =(
My OS is WinXP x86.
UPD: it works from VBScript:
Dim objJava
Set objJava = WScript.CreateObject("Prisoner.PrisonerControl")
but it is not visible in tstcon32.
回答1:
If you read the actual article using the Prisoner.PrisonerControl control a sub key named Control
is created inside the key with your control GUID.
On my machine with the guid {9DEA5F06-E324-31A7-837B-D0F3BDE91423}
creating the key
HKEY_CLASSES_ROOT\CLSID\{9DEA5F06-E324-31A7-837B-D0F3BDE91423}\Control
Make the control appears in tstcon32
. And with or without it the ActiveX is usable for javascript
var x = new ActiveXControl("Prisoner.PrisonerControl");
Actually i had to fight windows on both the javascript execution and registry path to test it on my system because it's an x64 machine but that's another story.
回答2:
You have created a COM server but not an ActiveX control, which is a far more intricate COM object, the kind that you can exercise with tstcon32.exe.
It must implement a bunch of interfaces, key ones are IOleObject and IOleWindow. The kind of interfaces that allows it to do the required negotiations with an ActiveX host and create a visible window. The Winforms Control class is your best bet to create one.
回答3:
Here are the relevant steps as documented externally. This is summarized leaving out some exposition but not any necessary steps.
This example is also very similar to the article Using Managed Controls as ActiveX Controls by Garry Trinder, November 25, 2008 and I've included some notes from this article as well.
Exposing Windows Forms Controls as ActiveX controls
This article will describe how to utilise Windows Forms controls outside of .NET.
Writing the control
- Create a new control project from within Visual Studio - my examples are all in C# but VB.NET could also be used.
[Here Garry's article suggests, "First, create a managed usercontrol project – either a Windows Forms class library or control library project. Use the usercontrol designer to design your custom usercontrol the way you want it (using any standard controls you like)."]
Add controls etc to the form, put in the code etc.
Add in the following using clauses...
using System.Runtime.InteropServices; using System.Text; using System.Reflection; using Microsoft.Win32;
- Attribute your class so that it gets a ProgID. This isn't strictly necessary as one will be generated, but it's almost always best to be explicit.
[ProgId("Prisoner.PrisonerControl")] [ClassInterface(ClassInterfaceType.AutoDual)]
This assigns the ProgID, and also defines that the interface exposed should be 'AutoDual' - this crufts up a default interface for you from all public, non-static members of the class. If this isn't what you want, use one of the other options.
- Update the project properties so that your assembly is registered for COM interop.
If you're using VB.NET, you also need a strong named assembly. Curiously in C# you don't - and it seems to be a feature of the environment rather than a feature of the compiler or CLR.
- Add the following two methods into 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 attributed with ComRegisterFunction - this static method will be called when the assembly is registered for COM Interop. All I do here is add the 'Control' keyword to the registry, plus add in the CodeBase entry.
CodeBase is interesting - not only for .NET controls. It defines a URL path to where the code can be found, which could be an assembly on disk as in this instance, or a remote assembly on a web server somewhere. When the runtime attempts to create the control, it will probe this URL and download the control as necessary. This is very useful when testing .NET components, as the usual caveat of residing in the same directory (etc) as the .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 remove the registry entries added when (if) the class is unregistered - it's always a good suggestion to tidy up as you go.
Now you are ready to compile & test your control.
Additional notes from Garry's blog:
[The] additional registry entries:
Control
,MiscStatus
,TypeLib
andVersion
[can be created] with a.REG
script, but it’s generally better to write functions that will be called on registration/unregistration
He describes the registry keys in some detail:
Control
is an empty subkey.TypeLib
is mapped to the GUID of the TypeLib (this is the assembly-level GUID in the assemblyinfo.cs).Version
is the major and minor version numbers from the assembly version. The only mildly interesting subkey isMiscStatus
. This needs to be set to a value composed of the (bitwise) values in theOLEMISC
enumeration, documented here. To make this enum available, add a reference toMicrosoft.VisualStudio.OLE.Interop
(and a suitable ‘using’ statement for the namespace).
His final note is a warning:
Note: this seems to work OK for Excel (with the very limited testing I've done), partly works with PowerPoint, but fails miserably with Word. Possibly, some more of the
OLEMISC
values might improve this; possibly there are some messages we need to hook; possibly there are some more interfaces we need to implement ... The fact that I’ve only barely got it to work in a very limited way should tell you that this is probably not a technique you want to use in any serious way.
来源:https://stackoverflow.com/questions/7712815/how-to-create-an-activex-control-in-c