问题描述
我有一个尝试向 VBA 公开的 C# 库.我可以很好地将参数传递给函数(即ref byte[] someArray"),但是传递对象或结构就行不通了.
I have a C# library that I'm trying to expose to VBA. I can pass parameters to functions just fine (ie "ref byte[] someArray"), but passing objects or structs just won't work.
如果我尝试将字节数组作为类的属性传递,我会在 VB 中收到以下错误-
If I try passing a byte array as a property of a class, I get the following error in VB-
函数或接口标记为受限,或函数使用 Visual Basic 不支持的自动化类型
Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic
如果我尝试将字节数组作为结构的属性传递,我会在 VB 中收到以下错误-
If I try passing a byte array as a property of a struct, I get the following error in VB-
我已经为此奋斗了两天,虽然我一直在寻找声称有答案的帖子,但没有一个对我有用.
I've been fighting this for two days now and while I keep finding posts that claim to have the answer, none of them have worked for me.
这是我目前的代码:
[ComVisible(true)]
[Guid("7F53F7A5-15C9-4A99-A855-38F5E87702D0")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)] // Tried as InterfaceIsDual and as InterfaceIsIDispatch
public interface IDetail
{
[DispId(1)] // Tried with and without these
int SomeInt { get; set; }
[DispId(2)]
string SomeString { get; set; }
[DispId(3)]
byte[] SomeByteArray {
return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
get;
[param: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
set;
}
}
[ComVisible(true)]
[Guid("F77FB3D4-27E0-4BFA-A21E-5ACB671151E9")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("G4COMTest.Detail")]
public class Detail:IDetail
{
public int SomeInt { get;set; }
public string SomeString { get; set; }
// Tried MarshalAs in all combinations of class and interface
public byte[] SomeByteArray {
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
get;
[param: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
set;
}
}
[ComVisible(true)]
[Guid("5E8F9FF0-3156-479E-A91D-0DADD43881FB")]
[ClassInterface(ClassInterfaceType.None)]
public class Worker:IWorker
{
// works with the 'ref'
public int ReturnIntWByteArrayParam(ref byte[] testByteArray)
{
return testByteArray.Count();
}
public int ReturnIntWObjParam(IDetail detail)
{
return detail.SomeInt;
}
public IDetail ReturnObjNoParams()
{
var o = new Detail();
o.SomeInt = 87;
o.SomeString = "What are you doing Dave";
return o;
}
}
[ComVisible(true)]
[Guid("04962F29-DBBD-48AC-B4FB-180EEF562771")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IWorker
{
int ReturnIntWByteArrayParam(ref byte[] testByteArray);
int ReturnIntWObjParam(IDetail detail);
IDetail ReturnObjNoParams();
}
从 VB6 调用它:
Dim o As New G4COMTest.Worker
Dim d As New G4COMTest.Detail
Dim byt(2) As Byte
d.SomeInt = 356 '// Works
d.SomeString = "Hello from client" '// Works
d.SomeByteArray = byt '// Errors as either class or struct
MsgBox mWorker.ReturnIntWObjParam(d)
提前感谢您的帮助!
推荐答案
C# 数组属性向 COM 公开了一个 getter 和一个 setter,正如你所期望的那样(MarshalAs
属性是不必要的,默认情况下,marshaler 会正确检测到它).
The C# array property exposes a getter and a setter to COM exactly as you would expect it to (the MarshalAs
attribute is unnecessary, the marshaler does detect it correctly by default).
问题在于,setter 与 .NET 中的所有属性 setter 一样,逐个传递 value 参数.不幸的是,VBA 不支持按值传递数组.这是从第一天开始就存在的语言的基本限制.更不幸的是,COM 互操作没有提供任何方法来用属性覆盖这种行为.你有两个选择:
The problem is that the setter, like all property setters in .NET, passes the value parameter by value. Unfortunately, VBA does not support passing arrays by value. It's a fundamental limitation of the language that's been there since day one. Even more unfortunately, COM interop does not provide any way to override this behaviour with attributes. You have two choices:
A - 定义您自己的 setter 方法并从 VBA 而不是属性 setter 调用它,例如
A - Define your own setter method and call it from VBA instead of the property setter, e.g.
void SetSomeByteArray(ref byte[] value) { SomeByteArray = value; }
B - 将属性类型更改为 object
并使用变体数组而不是强类型数组.
B - Change the property type to object
and use variant arrays instead of strongly-typed arrays.
PS:也要小心 string
属性.这些通常工作得很好,但是如果你将 null
字符串值传递给 VBA,它会出错,因为 VBA String
类型不能存储 null
参考资料.
PS: Be careful with string
properties too. These normally work just fine, but if you pass a null
string value to VBA, it will error because the VBA String
type can't store null
references.
这篇关于如何将字节数组作为 UDT 属性从 VB6/VBA 传递到 C# COM DLL?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!