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

How to Pass Byte Arrays as UDT Properties from VB6/VBA to C# COM DLL?(如何将字节数组作为 UDT 属性从 VB6/VBA 传递到 C# COM DLL?)
本文介绍了如何将字节数组作为 UDT 属性从 VB6/VBA 传递到 C# COM DLL?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个尝试向 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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)