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

如何在C#中创建ActiveX控件?,c#,com,activex,tstcon32,C#,Com,Activex,Tstcon32,我无法在C#中创建正常运行的ActiveX控件;我尝试了以下教程,但没有成功 我创建了一个示例类库项目,其中包括以下代码: namespace AACWCSurvey { [ProgId("Prisoner.PrisonerControl")] [ClassInterface(ClassInterfaceType.AutoDual)] public class Class1 { public Class1() {

我无法在C#中创建正常运行的ActiveX控件;我尝试了以下教程,但没有成功

我创建了一个示例类库项目,其中包括以下代码:

namespace AACWCSurvey
{
    [ProgId("Prisoner.PrisonerControl")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class Class1
    {
        public Class1()
        {
            MessageBox.Show("FIRETRUCK!!!");
        }
    }
}
然后,我执行了以下步骤:

  • 属性=>应用程序=>程序集信息=>使程序集COM可见
  • Build=>注册COM互操作TRUE(选中)
  • 为程序集生成强名称(签名)
  • 建设项目
  • regasm MyDll.dll/tlb/codebase

  • 在tstcon32中看不到
    囚犯.PrisonerControl
    =(

  • 我的操作系统是WinXP x86


    UPD:它从VBScript开始工作:

    Dim objJava
    Set objJava = WScript.CreateObject("Prisoner.PrisonerControl")
    
    但是它在tstcon32中不可见。

    如果您使用Captive.PrisonerControl控件进行读取,则会在具有控件GUID的键内创建名为
    control
    的子键

    在guid为
    {9DEA5F06-E324-31A7-837B-D0F3BDE91423}
    创建密钥的计算机上

    HKEY_CLASSES_ROOT\CLSID\{9DEA5F06-E324-31A7-837B-D0F3BDE91423}\Control
    
    使控件出现在
    tstcon32
    中。无论有无控件,ActiveX都可用于javascript

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


    实际上,我不得不在javascript执行和注册表路径上与windows抗争,以便在我的系统上测试它,因为它是一台x64机器,但这是另一回事。

    您创建了一个COM服务器,但没有ActiveX控件,这是一个复杂得多的COM对象,您可以使用tstcon32.exe执行


    它必须实现一系列接口,关键的是IOleObject和IOleWindow。这种接口允许它与ActiveX主机进行所需的协商并创建一个可见窗口。Winforms控件类是创建一个窗口的最佳选择。

    以下是相关步骤。这是一个总结,省去了一些说明但没有任何必要的步骤

    这个例子也非常类似于Garry Trinder在2008年11月25日发表的文章,我还包括了本文中的一些注释

    将Windows窗体控件公开为ActiveX控件

    本文将介绍如何使用Windows窗体控件 在.NET之外

    写入控件

  • 从VisualStudio中创建一个新的控件项目——我的示例都是C语言,但也可以使用VB.NET
  • [此处Garry的文章建议,“首先,创建一个托管usercontrol项目–Windows窗体类库或控件库项目。使用usercontrol设计器以您想要的方式设计自定义usercontrol(使用您喜欢的任何标准控件)。”

  • 在表单中添加控件等,输入代码等

  • 添加以下using子句

  • 属性,这样它就可以得到一个ProgID。这并不是必须的,因为它会生成一个ProgID,但最好是这样 明确的
  • 这将分配ProgID,并定义接口 exposed应该是“AutoDual”-这为 您可以从类的所有公共、非静态成员中删除。如果不是 您需要什么,请使用其他选项之一

  • 更新项目属性,以便为COM互操作注册程序集
  • 如果您使用的是VB.NET,则还需要一个强名称程序集。 奇怪的是,在C#中,你没有——这似乎是 环境,而不是编译器或CLR的功能

  • 将以下两个方法添加到类中
  • RegisterClass函数的属性为ComRegisterFunction- 当为注册程序集时,将调用此静态方法 COM互操作。我在这里所做的就是将“Control”关键字添加到 注册表,加上代码库条目中的附加项

    代码库很有趣-不仅仅是.NET控件。它定义了一个URL 可以找到代码的路径,该路径可以是上的程序集 此实例中的磁盘,或web服务器上的远程程序集 当运行时尝试创建控件时,它将 探测此URL并根据需要下载控件。这非常有用 在测试.NET组件时非常有用,这是 与.EXE不适用的目录(etc)相同

    第二个函数将删除(如果)时添加的注册表项 这门课没有注册——这总是一个整理的好建议 随你去

    现在,您已经准备好编译和测试您的控件了

    Garry博客的补充说明:

    [附加注册表项:
    Control
    MiscStatus
    TypeLib
    Version
    [可以使用
    .REG
    脚本创建],但通常更好 编写在注册/注销时调用的函数

    他详细描述了注册表项:

    Control
    是空子键。
    TypeLib
    映射到 TypeLib(这是assemblyinfo.cs中的程序集级别GUID)。
    Version
    是程序集的主要版本号和次要版本号 唯一稍微有趣的子键是
    MiscStatus
    。这需要 设置为由
    OLEMISC
    枚举。若要使此枚举可用,请添加 参考
    Microsoft.VisualStudio.OLE.Interop
    (以及合适的 命名空间的“using”语句)

    他的最后一句话是警告:

    注意:这对于Excel似乎是可行的(测试非常有限) 我做过),部分使用PowerPoint,但在使用PowerPoint时失败惨重 可能,更多的
    OLEMISC
    值可能会改善这一点; 可能有一些消息我们需要钩住;可能有 我们需要实现更多的接口…事实上我只需要 几乎没有以非常有限的方式让它工作
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Reflection;
    using Microsoft.Win32;
    
    [ProgId("Prisoner.PrisonerControl")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    
    [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 ( ) ;
    }
    
    [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 ( ) ;
    }