如何将字节数组作为UDT属性从VB6/VBA传递到C#COM DLL? [英] How to Pass Byte Arrays as UDT Properties from VB6/VBA to C# COM DLL?

查看:111
本文介绍了如何将字节数组作为UDT属性从VB6/VBA传递到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)

在此先感谢您的帮助!

If I try passing a byte array as a property of a class, I get the following error in VB-

Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic

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.

So here's my code as it currently sits:

[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();
}

Calling it from 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)

Thanks in advance for any help!

解决方案

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).

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 - 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 - Change the property type to object and use variant arrays instead of strongly-typed arrays.

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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆