将C#对象按值传递给COM

7

我正在尝试使用从C#访问COM接口,该接口由tlbimp生成并公开以下接口:

[Guid("7DDCEDF4-3B78-460E-BB34-C7496FD3CD56")]
[TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
public interface IFred 
{
    [DispId(1)]
    IBarney Pall { get; set; }
}

[Guid("E390230E-EE9C-4819-BC19-08DAB808AEA9")]
[TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FNonExtensible | TypeLibTypeFlags.FDispatchable)]
public interface IBarney
{
    [DispId(1)]
    double Wilma { get; set; }
}

生成的包装程序集不包含 IBarney 接口的实现。我创建了一个实现 IBarney 接口的 C# 结构,如下所示:
[Guid("2C61BA37-7047-43DB-84B1-83B4268CF77B")]
[ComVisible(true)]
public struct Barney : IBarney
{
    public double Wilma { get; set; }
}

现在的问题是,Barney实例是按值传递还是按引用传递?这很重要,因为涉及网络开销。理想情况下,执行类似以下的操作:

fredInstance.Pall = new Barney { Wilma = 0.37 };

执行此操作将导致一次网络往返。我如何验证这一点,或者我如何告诉COM互操作我的Barney结构应始终按值进行编组?

更新:

考虑到Hans Passant的评论。什么是“正确”的设计方式?允许将简单结构用作COM接口的“属性”的值所需的IDL是什么?查看我当前使用的接口生成的IDL,添加具有默认IBarney接口的coclass声明就足够了,对吗?


1
COM 不关心对象的实现方式,只与接口指针一起工作。这将对每个属性访问进行往返传输。从技术上讲,可以通过自定义代理/存根来解决这个问题,但这超出了 .NET 的范围。考虑实现 IPersistStream。 - Hans Passant
@HansPassant:我猜你的意思是在COM/本地端实现IPersistStream?因为谷歌搜索只显示非.NET相关的内容。 - Bas Bossink
你是否在两端都使用C#开发?或者还涉及其他编程语言吗?比如说,你为什么要首先创建COM接口?为什么要使用IDL来定义接口?你需要自动化兼容的接口吗?你是否了解“纯”COM和COM+自动化之间的区别? - Simon Mourier
我在一家同时使用.NET和C++的公司工作,两个世界之间的交互是通过COM完成的。我个人参与了.NET开发。要更改IDL/COM部分,需要进行一些协商,但原则上它们可以被更改。COM接口的最初要求之一是它们与OLE自动化兼容。正如你所猜测的那样,我对COM不太了解,我不知道COM和COM+自动化之间的区别。如果有任何资源指导,将不胜感激。 - Bas Bossink
好的,所以你需要使用C++和自动化(我不是在谈论COM+,而是COM +自动化:-)。在这种情况下,你必须知道自动化将大大减少你可以使用的类型(它也有其优点):https://msdn.microsoft.com/en-us/library/cc237562.aspx(注意,VT_RECORD并非所有客户端都支持,它是后期添加的)。最简单的方法是使用SAFEARRAY(of UI1),或VARIANT(可以包装任何自动化类型)。PS:当你想回复某人时,请不要忘记添加@<user>。我只是路过,注意到你回答了我的问题。 - Simon Mourier
1个回答

1

一些评论...不确定这是否会成为答案...

1) 我认为您应该将您的接口设置为 [ComVisible(true)]

2) 为什么要将Barney定义为一个结构体?它应该是一个类。与C++不同,其中类和结构体之间唯一的区别是默认的私有成员和公共成员,C#的结构体和类在本质上是不同的。


广告1)该代码是从由tlbimp生成的汇编代码反汇编而来,我假设它在生成的代码中使用了[assembly: ComVisible(true)]。 广告2)Barney是一个结构体,强调它应该被视为值类型,在示例中用作包含一个double的“转储”容器。实际上,它是一个包含两个double的数据类型。我已经在.NET端将其实现为结构体,一切都“正常”。但是正如Hans所提到的,这仍然会导致太多的远程调用。我只想将结构作为单个数据项传递到“栅栏”的另一侧。 - Bas Bossink
如果你担心网络开销和初始化结构体是你想做的事情,那么尝试通过传递文本blob--XML或JSON,甚至是逗号分隔列表来解决。 - Joseph Willcoxson
我并不担心额外的开销,只是觉得当前的解决方案“次优”,想知道当前的情况是否可以通过符合COM的使用方式来改进。我不想引入一些诸如传递文本字符串并在另一侧进行解析之类的小技巧。 - Bas Bossink
我认为你犯了两个基本错误。第一个是你肯定还没有做出任何测量,并且你不知道这是否 实际上 是你需要做的事情。第二个错误更大,问题中根本没有暗示这实际上是一个 DCOM 场景。这是一种可能会导致属性访问变慢并产生网络往返开销的场景。只有在从 ServicedComponent 类派生你的类时才能直接在 .NET 中支持,显然你的结构体没有做到这一点。这个问题太 Flintstone 了,看不出你应该实现什么。 - Hans Passant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接