我正在.NET中创建一个包装类(VB.NET,因为它发生但与C#同样相关),它暴露给COM,我试图包装的一个属性是Variant。我以为我只能使用一个Object,但是我收到一个错误:
Public Property FieldValue([vFieldID As Object = -1]) As Object
不能作为属性'Let'向COM公开。您将无法使用“Let”语句从Visual Basic 6.0为此属性分配非对象值(如数字或字符串)。*
我的财产声明如下:
Public Property FieldValue(Optional ByVal vFieldID As Object = -1) As Object
Get
Return _objVAccess.FieldValue(vFieldID)
End Get
Set(ByVal value As Object)
_objVAccess.FieldValue = value
End Set
End Property
我的属性实际上从数据库中返回一个值,该值可以是整数,字符串,日期等,因此不是一个COM对象。是否有任何解决方法允许属性Let?
答案 0 :(得分:30)
COM Automation支持一个默认属性,即具有dispid 0的属性。这在VB6代码中使用效果很好,生成非常紧凑的代码。一个典型的例子是:
rs!Customer = "foo"
这是语法糖:
rs.Fields.Item("Customer").Value = "foo"
此处使用了三个默认属性,但未在原始语句中进行命名。 Recordset接口将Fields属性作为默认属性,生成Fields接口引用。其中Item属性作为默认(索引)属性,生成Field接口引用。其中Value属性作为默认属性,生成变体。
哪个非常好。然而,像这样的极端语法糖的价格是蛀牙。在语句中存在语法歧义:
Dim obj
obj = someObject
这是什么意思?是否要将someObject引用分配给obj?或者您想要分配someObject的默认属性?非常不同的是, obj 类型将完全不同。这是在VB6中用 Set 关键字解决的。如果要分配对象引用,则必须编写:
Set obj = someObject
如果您要分配默认属性值,则省略 Set 或明确使用 Let 。这非常令人讨厌,并且长期困扰新手Visual Basic和VB脚本程序员。
COM Automation通过允许属性具有两个 setter来实现此目的。在IDL中分别是 propput 和 propputref ,其中propputref是分配对象的那个。还可以看到这早在IDispatch的定义中,的IDispatch :: invoke()方法在两个与DISPATCH_PROPERTYPUT和DISPATCH_PROPERTYPUTREF之间进行区分。
Zip转发到VB.NET,微软认为模糊性太痛苦,并且消除了默认的非索引属性的概念。哪个幸福也退休了Set关键字。然而,这会产生一个新问题,没有任何方法可以编写一个[ComVisible]类,它可以具有Object类型的属性,并且具有接受对象引用的setter。语言语法只允许一个setter,CLR中的COM互操作层缺少合成两个的管道。值得注意的是,这只是一个警告,你仍然得到了propput setter,你只是不会得到propputref setter。据我所知,无论如何都是你想要的。
在VB6虚拟类中定义接口,或者通过显式写入IDL并使用midl.exe编译它确实是一种绕过警告的方法。正如John Rivard在this question中所示。
答案 1 :(得分:3)
您是否尝试使用MarshalAs
属性?
你应该能够像那样应用它(抱歉,如果我有语法错误,我通常使用C#):
Public Property FieldValue(Optional ByVal vFieldID As Object = -1) As <MarshalAsAttribute(UnmanagedType.Struct)> Object
Get
Return _objVAccess.FieldValue(vFieldID)
End Get
Set(ByVal value As Object)
_objVAccess.FieldValue = value
End Set
End Property
这应该告诉编组人员将属性公开为VARIANT
结构。
您可能需要为结构大小等应用其他属性,但我认为这是您可以用来解决问题的方向。