c# - 在 C#/.Net 中创建进程外 COM?

标签 c# com

我需要在 C# 中创建一个进程外 COM 服务器 (.exe),它将被同一个机器上的多个其他进程访问。该组件必须是单个进程,因为它将在内存中缓存它提供给其消费者的信息。

注意:将访问我的 COM 服务器的进程主要是 Matlab 进程,因此 必需 COM 接口(interface)。

我在堆栈溢出 (Create COM ...) 和网络上看到了有关在 .Net 中创建进程内 COM 组件的线程,但我很难找到一种方法来使用 .Net 创建进程外组件。网。

这是如何实现的?有任何建议的引用资料吗?

谢谢。

最佳答案

很多年前,我们也遇到过一些关于 regasm 和将 COM 类作为本地 EXE 服务器运行的问题。

这有点乱七八糟,我欢迎任何使它更优雅的建议。它是在 .NET 1.0 时代为一个项目实现的,从那时起就没有被触及过!

基本上,它在每次应用程序启动时执行 regasm 风格的注册(在 COM 容器应用程序中实例化 COM 对象之前,它需要运行一次以创建注册表条目)。

我从我们的实现中复制了以下重要部分,并重命名了几个类来说明示例。

从 Form Loaded 事件调用以下方法来注册 COM 类(在本例中重命名为 MyCOMClass)

private void InitialiseCOM()
    {
        System.Runtime.InteropServices.RegistrationServices services = new System.Runtime.InteropServices.RegistrationServices();
        try
        {
            System.Reflection.Assembly ass = Assembly.GetExecutingAssembly();
            services.RegisterAssembly(ass, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase);
            Type t = typeof(MyCOMClass);
            try
            {
                Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\{" + t.GUID.ToString() + "}\\InprocServer32");
            }
            catch(Exception E)
            {
                Log.WriteLine(E.Message);
            }

            System.Guid GUID = t.GUID;
            services.RegisterTypeForComClients(t, ref GUID );
        }
        catch ( Exception e )
        {
            throw new Exception( "Failed to initialise COM Server", e );
        }
    }

对于所讨论的类型,MyCOMObject,需要一些特殊属性才能与 COM 兼容。一个重要的属性是指定一个固定的 GUID,否则每次编译注册表时都会填满孤立的 COM GUID。您可以使用 VisualStudio 中的“工具”菜单为您创建一个唯一的 GUID。

  [GuidAttribute("D26278EA-A7D0-4580-A48F-353D1E455E50"),
  ProgIdAttribute("My PROGID"),
  ComVisible(true),
  Serializable]
  public class MyCOMClass : IAlreadyRegisteredCOMInterface
  {
    public void MyMethod()
    {
    }

    [ComRegisterFunction]
    public static void RegisterFunction(Type t)
    {
      AttributeCollection attributes = TypeDescriptor.GetAttributes(t);
      ProgIdAttribute ProgIdAttr = attributes[typeof(ProgIdAttribute)] as ProgIdAttribute;

      string ProgId = ProgIdAttr != null ? ProgIdAttr.Value : t.FullName;

      GuidAttribute GUIDAttr = attributes[typeof(GuidAttribute)] as GuidAttribute;
      string GUID = "{" + GUIDAttr.Value + "}";

      RegistryKey localServer32 = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\LocalServer32", GUID));
      localServer32.SetValue(null, t.Module.FullyQualifiedName);

      RegistryKey CLSIDProgID = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\ProgId", GUID));
      CLSIDProgID.SetValue(null, ProgId);

      RegistryKey ProgIDCLSID = Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}", ProgId));
      ProgIDCLSID.SetValue(null, GUID);

      //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}}", GUID));
      //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{63D5F430-CFE4-11d1-B2C8-0060083BA1FB}}", GUID));
      //Registry.ClassesRoot.CreateSubKey(String.Format("CLSID\\{0}\\Implemented Categories\\{{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}}", GUID));
    }

    [ComUnregisterFunction]
    public static void UnregisterFunction(Type t)
    {
      AttributeCollection attributes = TypeDescriptor.GetAttributes(t);
      ProgIdAttribute ProgIdAttr = attributes[typeof(ProgIdAttribute)] as ProgIdAttribute;

      string ProgId = ProgIdAttr != null ? ProgIdAttr.Value : t.FullName;

      Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\{" + t.GUID + "}");
      Registry.ClassesRoot.DeleteSubKeyTree("CLSID\\" + ProgId);
    }

  }

主窗体中的InitialiseCOM方法使用RegistrationServices注册类型。然后,框架使用反射找到标记有 ComRegisterFunction 属性的方法,并使用正在注册的类型调用该函数。

ComRegisterFunction 标记的方法,手动创建本地 EXE 服务器 COM 对象的注册表设置,如果您使用 REGEDIT 并在中找到键,这可以与 regasm 进行比较问题。

我已经注释掉了三个 \\Registry.ClassesRoot.CreateSubKey 方法调用,因为这是我们需要自己注册类型的另一个原因,因为这是 OPC 服务器和第三方 OPC 客户端使用这些实现的类别扫描兼容的 OPC 服务器。除非我们自己完成工作,否则 REGASM 不会为我们添加这些。

如果在函数开始时在函数上放置断点,您可以很容易地看到它是如何工作的。

我们的实现使用了一个已经注册到 COM 的接口(interface)。对于您的申请,您需要:-

  1. 扩展上面列出的注册方法,将接口(interface)注册到COM
  2. 或者使用接口(interface)定义创建一个单独的 DLL,然后将该接口(interface)定义导出到类型库并按照您在问题中添加的 StackOverflow 链接中的讨论进行注册。

关于c# - 在 C#/.Net 中创建进程外 COM?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/446417/

相关文章:

c# - 强制 JSON.NET 在序列化 DateTime 时包含毫秒(即使 ms 组件为零)

c# - .NET Core 3 WPF 和路径

.net - ParamArray 不适用于 COM

c# - 从 C++ com 加载项调用 C#

c# - 反序列化有时会生成对象列表或空对象的 JSON 字符串

c# - 是否可以在不评估其表达式的情况下在 foreach 循环中递增标识符?

c++ - 我想从 Python 调用 Windows C++ 函数 WinHttpGetProxyForUrl - 这可以做到吗?

c# - 当只需要几个方法时,有什么方法可以避免创建巨大的 C# COM 接口(interface)包装器?

c# - COM异常 : 80040154

c# - Windows 窗体组合框控件的奇怪行为