草庐IT

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

coder 2023-07-11 原文

我需要在 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# - 在 C#/.Net 中创建进程外 COM?的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  3. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  9. ruby - 如何在 Ruby 中创建无类 DSL? - 2

    我正在尝试找出如何为我的Ruby项目创建一种“无类DSL”,类似于在Cucumber步骤定义文件中定义步骤定义或在Sinatra应用程序中定义路由。例如,我想要一个文件,其中调用了我的所有DSL函数:#sample.rbwhen_string_matches/hello(.+)/do|name|call_another_method(name)end我认为用我的项目特有的一堆方法污染全局(内核)命名空间是一种不好的做法。因此方法when_string_matches和call_another_method将在我的库中定义,并且sample.rb文件将以某种方式在我的DSL方法的上下文中

  10. ruby-on-rails - 如何在 Rails 3 中创建自定义脚手架生成器? - 2

    有这些railscast。http://railscasts.com/episodes/218-making-generators-in-rails-3有了这个,你就会知道如何创建样式表和脚手架生成器。http://railscasts.com/episodes/216-generators-in-rails-3通过这个,您可以了解如何添加一些文件来修改脚手架View。我想把两者结合起来。我想创建一个生成器,它也可以创建脚手架View。有点像RyanBates漂亮的生成器或web_app_themegem(https://github.com/pilu/web-app-theme)。我

随机推荐