![](/img/trans.png)
[英]Calling a VB6 DLL function with a complex User Defined Type (UDT) from C#
[英]How to Pass Byte Arrays as UDT Properties from VB6/VBA to C# COM DLL?
我有一个C#库,试图将其公开给VBA。 我可以很好地将参数传递给函数(即“ ref byte [] someArray”),但是传递对象或结构将不起作用。
如果我尝试将字节数组作为类的属性传递,则会在VB-中收到以下错误
函数或接口标记为受限,或者函数使用Visual Basic不支持的自动化类型
如果我尝试将字节数组作为结构的属性传递,则会在VB-中收到以下错误
我已经为此奋斗了两天,尽管我不断找到声称有答案的帖子,但没有一个对我有用。
所以这是我目前的代码:
[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中的所有属性设置器一样,按值传递value参数。 不幸的是,VBA不支持按值传递数组。 从第一天开始,这就是对这种语言的基本限制。 更不幸的是,COM互操作无法提供任何方法来使用属性覆盖此行为。 您有两种选择:
A-定义您自己的设置器方法,然后从VBA而不是属性设置器中调用它,例如
void SetSomeByteArray(ref byte[] value) { SomeByteArray = value; }
B-将属性类型更改为object
并使用变量数组而不是强类型数组。
PS:还要注意string
属性。 这些通常可以正常工作,但是如果将null
字符串值传递给VBA,则会出错,因为VBA String
类型不能存储null
引用。
在您的代码类中,Detail的ClassInterfaceType设置为None,如果将其设置为AutoDispatch,则您可以运行的代码。 从MSDN:
对于脚本客户端,Microsoft Visual Basic 6.0客户端或任何不缓存接口成员的DispId的后期绑定客户端,使用类接口是可接受的选项。”
http://msdn.microsoft.com/zh-CN/library/4fcadw4a(v=vs.110).aspx
由于您从中呼叫的客户端是VB6-您可以将ClassInterfaceType设置为AutoDispatch或什至忽略它(默认为AutoDispatch)。 这将仅生成“仅分发”类接口,并且不包括该接口中的任何成员。 从VB6调用时,直接使用属性分配数组应该起作用,因为它使用IDispatch Invoke函数(后期绑定)。
我们使用字符串数组对此进行了测试,并且可以正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.