简体   繁体   English

如何在 C# 中创建 ActiveX 控件?

[英]How to create an ActiveX control in C#?

I am not able to create a functioning ActiveX control in C#;我无法在 C# 中创建功能正常的 ActiveX 控件; 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:然后我做了以下步骤:

  1. Properties => Application => Assembly Information => Make Assembly COM-visible属性 => 应用程序 => 程序集信息 => 使程序集 COM 可见
  2. Build => Register for COM interop TRUE (checked)构建 => 注册 COM 互操作 TRUE(选中)
  3. Make Strong name for assembly (signing)为程序集设置强名称(签名)
  4. Build the project构建项目
  5. regasm MyDll.dll /tlb /codebase

  6. Can't see Prisoner.PrisonerControl in tstcon32 =(在 tstcon32 中看不到Prisoner.PrisonerControl =(

My OS is WinXP x86.我的操作系统是 WinXP x86。


UPD: it works from VBScript: UPD:它适用于 VBScript:

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

but it is not visible in tstcon32.但它在 tstcon32 中不可见。

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.如果您使用 Prisoner.PrisonerControl 控件阅读实际文章,则会在带有您的控件 GUID 的键中创建一个名为Control的子键。

On my machine with the guid {9DEA5F06-E324-31A7-837B-D0F3BDE91423} creating the key在我的机器上使用 guid {9DEA5F06-E324-31A7-837B-D0F3BDE91423}创建密钥

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

Make the control appears in tstcon32 .使控件出现在tstcon32 And with or without it the ActiveX is usable for javascript无论有没有它,ActiveX 都可用于 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.实际上,我不得不在 javascript 执行和注册表路径上与 windows 进行斗争才能在我的系统上对其进行测试,因为它是一台 x64 机器,但那是另一回事了。

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.您已经创建了一个 COM 服务器,但没有创建一个 ActiveX 控件,它是一个复杂得多的 COM 对象,您可以使用 tstcon32.exe 练习这种对象。

It must implement a bunch of interfaces, key ones are IOleObject and IOleWindow.它必须实现一堆接口,关键是IOleObject和IOleWindow。 The kind of interfaces that allows it to do the required negotiations with an ActiveX host and create a visible window.允许它与 ActiveX 主机进行所需协商并创建可见窗口的接口类型。 The Winforms Control class is your best bet to create one. Winforms Control 类是创建一个的最佳选择。

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.此示例也与 Garry Trinder 于 2008 年 11 月 25 日发表的文章将托管控件用作 ActiveX 控件非常相似,我也包含了本文中的一些注释。

Exposing Windows Forms Controls as ActiveX controls将 Windows 窗体控件公开为 ActiveX 控件

This article will describe how to utilise Windows Forms controls outside of .NET.本文将介绍如何在 .NET 之外使用 Windows 窗体控件。

Writing the control编写控件

  1. Create a new control project from within Visual Studio - my examples are all in C# but VB.NET could also be used.从 Visual Studio 中创建一个新的控件项目 - 我的示例都是用 C# 编写的,但也可以使用 VB.NET。

[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). "] [此处 Garry 的文章建议,“首先,创建一个托管用户控件项目——Windows 窗体类库或控件库项目。使用用户控件设计器以您想要的方式设计您的自定义用户控件(使用您喜欢的任何标准控件)。 ” ]

  1. Add controls etc to the form, put in the code etc.将控件等添加到表单中,放入代码等。

  2. Add in the following using clauses...添加以下 using 子句...

 using System.Runtime.InteropServices; using System.Text; using System.Reflection; using Microsoft.Win32;
  1. Attribute your class so that it gets a ProgID.属性您的类,以便它获得 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.这会分配 ProgID,并定义公开的接口应该是“AutoDual”——这为您从类的所有公共非静态成员中生成了一个默认接口。 If this isn't what you want, use one of the other options.如果这不是您想要的,请使用其他选项之一。

  1. Update the project properties so that your assembly is registered for COM interop.更新项目属性,以便为 COM 互操作注册程序集。

If you're using VB.NET, you also need a strong named assembly.如果您使用的是 VB.NET,您还需要一个强命名程序集。 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.奇怪的是,在 C# 中你没有 - 它似乎是环境的一个特性,而不是编译器或 CLR 的一个特性。

  1. 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. RegisterClass 函数具有 ComRegisterFunction 的属性 - 当为 COM Interop 注册程序集时,将调用此静态方法。 All I do here is add the 'Control' keyword to the registry, plus add in the CodeBase entry.我在这里所做的就是将“Control”关键字添加到注册表,并添加到 CodeBase 条目中。

CodeBase is interesting - not only for .NET controls. CodeBase 很有趣 - 不仅适用于 .NET 控件。 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.它定义了一个可以找到代码的 URL 路径,它可以是磁盘上的程序集,如本例中那样,也可以是 Web 服务器上某处的远程程序集。 When the runtime attempts to create the control, it will probe this URL and download the control as necessary.当运行时尝试创建控件时,它将探测此 URL 并根据需要下载控件。 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.这在测试 .NET 组件时非常有用,因为与 .EXE 驻留在同一目录(等)中的通常警告不适用。

 [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: Garry 博客的其他说明:

[The] additional registry entries: Control , MiscStatus , TypeLib and Version [can be created] with a .REG script, but it's generally better to write functions that will be called on registration/unregistration [The] 附加注册表项: ControlMiscStatusTypeLibVersion [可以创建] 使用.REG脚本,但通常最好编写将在注册/注销时调用的函数

He describes the registry keys in some detail:他详细描述了注册表项:

Control is an empty subkey. Control是一个空子项。 TypeLib is mapped to the GUID of the TypeLib (this is the assembly-level GUID in the assemblyinfo.cs). TypeLib映射到TypeLib的 GUID(这是 assemblyinfo.cs 中的程序集级 GUID)。 Version is the major and minor version numbers from the assembly version. Version是来自程序集版本的主要和次要版本号。 The only mildly interesting subkey is MiscStatus .唯一稍微有趣的子项是MiscStatus This needs to be set to a value composed of the (bitwise) values in the OLEMISC enumeration, documented here .这需要设置为由OLEMISC枚举中的(按位)值组成的值, 记录在此处 To make this enum available, add a reference to Microsoft.VisualStudio.OLE.Interop (and a suitable 'using' statement for the namespace).要使此枚举可用,请添加对Microsoft.VisualStudio.OLE.Interop的引用(以及适合命名空间的“using”语句)。

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.注意:这似乎适用于 Excel(我所做的测试非常有限),部分适用于 PowerPoint,但在 Word 中失败。 Possibly, some more of the OLEMISC values might improve this;可能,更多的OLEMISC值可能会改善这一点; 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.可能还有一些我们需要实现的接口......事实上,我只是勉强让它以非常有限的方式工作,这应该告诉你,这可能不是你想要以任何严肃的方式使用的技术。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM