将 Windows Forms 控件公开为 ActiveX 控件






4.86/5 (32投票s)
2001 年 7 月 18 日
4分钟阅读

813684

9429
本文介绍了如何构建一个 C# Windows Forms 控件并将其公开为 ActiveX 控件
引言
本文将介绍如何在 .NET 外部使用 Windows Forms 控件。在最近一期 MSDN 杂志关于 .NET 互操作性的文章(此处可查阅)中,讨论了将 .NET 对象公开给“旧版”环境的各种方法,包括将 Windows Forms 控件公开为 ActiveX 控件。
问题是,自文章撰写以来,目标已经发生了变化,因为 Beta 2 现已发布,而且不幸的是,此支持已被删除 - 请参阅 此帖 在 http://discuss.develop.com 的 .NET 列表上。
下图显示了一个纯粹在 .NET 中编写的控件,托管在一个 ActiveX 控件容器中 - 在此例中是 tstcon32.exe。
由于 Beta1 支持此功能,并且我有些好奇,我决定看看是否能找到一种方法来公开控件。附加的项目创建了“Prisoner”控件,虽然它不会引起轰动,但确实展示了在 VB6 中运行 .NET 控件所需完成的主要工作。
注意: 由于 .NET Beta2 已放弃此支持,如果它损坏了您的 PC 或烧毁了您的猫,请不要怪我。
现在言归正传,它是如何实现的?
编写控件
- 在 Visual Studio 中创建一个新的控件项目 - 我的示例都是 C#,但也可以使用 VB.NET。
- 向窗体添加控件等,并在其中放置代码等。
- 添加以下 using 子句...
using System.Runtime.InteropServices; using System.Text; using System.Reflection; using Microsoft.Win32;
- 为您的类添加属性,以便它具有 ProgID。这并非绝对必要,因为会生成一个 ProgID,但几乎总是最好明确指定。
[ProgId("Prisoner.PrisonerControl")] [ClassInterface(ClassInterfaceType.AutoDual)]
这会分配 ProgID,并定义公开的接口应为“AutoDual” - 它会为您从类的所有公共、非静态成员中创建一个默认接口。如果这不是您想要的,请使用其他选项之一。
- 更新项目属性,以便为您的程序集注册 COM 互操作。
如果您使用的是 VB.NET,还需要一个强命名的程序集。奇怪的是,在 C# 中则不需要 - 这似乎是环境的功能,而不是编译器或 CLR 的功能。
- 将以下两个方法添加到您的类中。
[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 ( ) ; }
RegisterClass 函数具有 ComRegisterFunction 属性 - 此静态方法将在注册程序集以进行 COM 互操作时调用。我在这里所做的只是向注册表中添加 'Control' 关键字,并添加 CodeBase 条目。CodeBase 很有意思 - 不仅仅是针对 .NET 控件。它定义了一个代码可以找到的 URL 路径,该路径可以是此实例中的磁盘上的程序集,也可以是 Web 服务器上的远程程序集。当运行时尝试创建控件时,它会探测此 URL 并根据需要下载控件。这在测试 .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 ( ) ; }
第二个函数将在类(如果)注销时删除添加的注册表项 - 最好在进行过程中进行清理。
现在您可以编译和测试您的控件了。
测试控件
在本例中,我选择了 tstcon32.exe,它随 .NET 安装一起提供。我使用它的主要原因而不是 VB6 是因为我不再有 VB6 了。
插入控件
首先,您需要插入控件,为此,请选择 编辑 -> 插入新控件,然后从下拉列表中选择您的控件...
如果您正在按照我的示例代码进行操作,这将导致文章顶部所示的显示。
测试方法
示例控件仅包含一个方法,“Question”。要在 TstCon32 中测试它,请从菜单中选择 控件 -> 调用方法,然后选择您要调用的方法。请注意,因为我将接口定义为 AutoDual,所以我得到了大量的(gazillions)方法。如果您实现了一个接口并将其公开为默认接口,那么方法列表将更易于管理。
单击“调用”按钮将执行该方法,该方法在这种情况下会显示一个标准的(obligatory)消息框。
总结
放弃从 Windows Forms 控件创建 ActiveX 控件的支持很麻烦,这是我希望 Microsoft 没有做出的一个决定。
本文介绍了一种将 .NET 控件公开为 ActiveX 控件的方法,并且似乎效果不错。尽管如此,我还没有对此进行详尽的测试,谁知道其中隐藏着什么 bug。我还没有深入研究事件或属性更改通知,如果您喜欢这类事情,那么那里还有一些有趣的探索空间。
.NET 框架确实是自切片面包以来最好的东西,但缺乏从 Windows Forms 控件创建 ActiveX 控件的支持很不方便。有许多应用程序(包括我们的)可以通过 ActiveX 控件进行扩展。鉴于框架中其余的支持,能够将 Windows Forms 控件公开给 ActiveX 容器将是一件很美好的事情,也许有一天这种支持就会出现。