如何将字节数组作为UDT属性从VB6 / VBA传递到C#COM DLL?

我有一个C#库,我正试图向VBA公开。 我可以很好地将参数传递给函数(即“ref byte [] someArray”),但传递对象或结构只是不起作用。

如果我尝试传递一个字节数组作为类的属性,我在VB-中得到以下错误

标记为受限制的函数或接口,或者该函数使用Visual Basic中不支持的自动化类型

如果我尝试传递一个字节数组作为结构的属性,我在VB-中得到以下错误

我已经打了两天了,而且我一直在寻找那些声称有答案的post,但是没有一个能为我效力。

所以这是我目前的代码:

[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属性是不必要的,编组器默认情况下会正确检测它)。

问题是,与.NET中的所有属性设置器一样,setter按值传递value参数。 不幸的是,VBA不支持按值传递数组。 这是从第一天起就一直存在的语言的基本限制。 更不幸的是,COM interop没有提供任何方法来使用属性覆盖此行为。 你有两个选择:

A – 定义您自己的setter方法并从VBA而不是属性setter调用它,例如

 void SetSomeByteArray(ref byte[] value) { SomeByteArray = value; } 

B – 将属性类型更改为object并使用variant数组而不是强类型数组。

PS:也要小心string属性。 这些通常工作正常,但如果您将null字符串值传递给VBA,它将会出错,因为VBA String类型不能存储null引用。

在您的代码类中,Detail将ClassInterfaceType设置为None,如果将其设置为AutoDispatch,则应该使用您应该使用的代码。 来自MSDN:

对于脚本化客户端,Microsoft Visual Basic 6.0客户端或任何不缓存接口成员DispIds的后期绑定客户端,使用类接口是可接受的选项。“

http://msdn.microsoft.com/en-us/library/4fcadw4a(v=vs.110).aspx

由于您调用的客户端是VB6 – 您可以将ClassInterfaceType设置为AutoDispatch,甚至可以省略它(默认为AutoDispatch)。 这将仅生成Dispatch only类接口,并且不包括接口中的任何成员。 从VB6调用时,将数组直接分配给属性应该可以正常工作,因为它使用IDispatch Invoke函数(后期绑定)。

我们用字符串数组测试了它,它可以工作。