使用 COM 在 C++ 中实例化 C# 类成员

Posted

技术标签:

【中文标题】使用 COM 在 C++ 中实例化 C# 类成员【英文标题】:Instantiating C# Class Members in C++ using COM 【发布时间】:2013-04-29 08:37:51 【问题描述】:

我想在 C++ 中创建一个 C# 类对象并设置其成员字段。 虽然我能够创建一个 C++ 类对象,但我无法访问它的成员并设置成员字段值。

/// <summary>
/// Class for AQS Entity
/// </summary>
[ClassInterface(ClassInterfaceType.None)]
[Guid("70F12A44-B91D-474D-BD70-32B1ACE041D6")]
[ProgId("AQSEntity")]
public class AQSEntity : IEntity


    public AQSEntity()
    
        sRecoveryDBName = String.Empty;
        arrSourceMailboxesEntity = null;
        sQueryString = String.Empty;
    

    [MarshalAs(UnmanagedType.BStr)]
    public string sRecoveryDBName = string.Empty;

    [MarshalAs(UnmanagedType.ByValArray)]
    public MailBoxCollection arrSourceMailboxesEntity;

    [MarshalAs(UnmanagedType.BStr)]
    public string sQueryString;



IEntity 类定义如下

[Guid("5C71057E-9FD9-47D5-B022-8D5F9C7007D3")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IEntity


在 C++ 中,

IEntity* pTest1 = NULL;
hr = CoCreateInstance(__uuidof(**AQSEntity**),NULL,CLSCTX_INPROC_SERVER,__uuidof(IEntity),(void**)&pTest1);

我想在 C++ 中访问 AQSEntity 类的成员。但我无法访问它们。

pTest1-> sQueryString

给出错误。

'sQueryString' : 不是 'AsigraExchange::IEntity' C:\PROJECTS\COM\COMClient\COMClient.cpp 168

谁能指出我哪里错了。

谢谢, 加根

【问题讨论】:

您的界面完全是空的。向它添加属性和方法。不要添加字段。 还有其他实现 IEntity 的类。如果我向 IEntity 添加属性和方法,这些类不能从 IEntity 继承。 【参考方案1】:

一切都按照应有的方式运行 :)

在您的 C++ 项目中,您可以访问在您的 IEntity 接口上声明的所有方法,这些方法都没有。

您的实现,即AQSEntity 类,应该实现所有IEntity 成员。现在在该类中声明的方法是该类的成员,它们与IEntity 没有任何关系。

这意味着您需要在IEntity 接口中声明所有必需的方法,然后在AQSEntity 中实现它们。请注意,您还公开了类的字段,而不是方法。您必须定义方法(或属性,将转换为 C++ 端的方法)然后实现它们。比如:

public interface IEntity

    public string RecoveryDBName  get; 

您还必须在界面中指定[MarshalAs] 属性,尽管UnmanagedType.BStr 是字符串的默认值,因此您可以省略它们。

编辑:

基于 cmets,IEntity 似乎只是一个标记接口,不打算作为 API 公开(这里的属性可能是更好的选择,因为 IEntity 无论如何都不会在客户端使用)。

在这种情况下,有两种选择:

1) 更好的方法,虽然它需要更多的工作:从IEntity 派生IAQSEntity 接口,在其上声明方法,并在AQSEntity 类上实现它

2) 更少的工作,但更脆弱:将 AQSEntity 标记为 ClassInterfaceType.AutoDual 而不是 ClassInterfaceType.None - 这会将成员公开给 COM 客户端,但版本化会更加困难,并且还会公开基本类型成员。

这是我会选择的:

[ClassInterface(ClassInterfaceType.None)]
...
[Entity]  // instead of IEntity marker interface
public class AQSEntity : IAQSEntity

    public string RecoveryDbName  get; 


[Guid("...")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IAQSEntity // no base interface IEntity!

    string RecoveryDbName  get;         

使用Entity 属性而不是IEntity 标记接口的唯一缺点是,如果您想将该接口用作泛型的约束

【讨论】:

还有其他实现 IEntity 的类。如果我向 IEntity 添加属性和方法,这些类不能从 IEntity 继承。 那么你需要从实体派生的IAQS接口。除了 COM,您如何在 C# 中通过 IEntity 访问 AQSEntity 成员?没办法-在您的情况下,它似乎是一个标记界面。另一种选择是使用ClassInterfaceType.AutoDual 而不是ClassInterfaceType.None,但这被认为是一种不好的做法。 我正在根据这篇文章中的建议更改我的设计。谢谢 请务必阅读ClassInterfaceType.AutoDual 上的文档,尤其是如果您走这条路的注意事项 - 它在版本控制和公开基本类型方法方面存在缺陷。【参考方案2】:

您的界面确实不包含任何成员。您需要将成员放入接口中,然后在您的类中实现该接口。

【讨论】:

还有其他实现 IEntity 的类。如果我向 IEntity 添加属性和方法,则这些类不能从 IEntity 继承。我没有向接口添加字段 嗯,这是你的问题,而不是技术问题。为什么要为其他类实现 COM 接口而不是要向 COM 公开的类?不要这样做。您不能添加字段的事实应该告诉您有关您的类设计的一些信息。不要使用公共字段。

以上是关于使用 COM 在 C++ 中实例化 C# 类成员的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C# 公共静态变量不需要实例化?

c++实例化一个对象

深入理解C# 静态类与非静态类静态成员的区别

C++:使用友元类限制对象实例化

C# 如何根据指定变量来实例化对象?

如何从 C# 导入和使用非托管 C++ 类?