是否可以使用 com 互操作将变体数组传递给 C#?

发布于 2024-11-06 09:16:15 字数 772 浏览 1 评论 0原文

我尝试在 Excel 和 C# 之间传输一些数据。为此,我编写了一个简单的 C# 类,其中包含一个用于设置和获取数据的属性。

[Guid("xxx")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Vector
{
    private object[,] _values;

    public object[,] ExcelValues
    {
        get { ... }
        set { ... }
    }
}

在 VBA 中获取 ExcelValues 属性效果很好,但无法在 VBA 中设置它。如果我尝试设置该属性,VBA 代码不会编译:

    Dim values As Variant
    With myRange
        ' typo: Set values = Range(.Offset(0, 0), .Offset(100, 0))
        values = Range(.Offset(0, 0), .Offset(100, 0))
    End With

    Dim data As New Vector

    ' this doesn't compile
    data.ExcelValues = values      

    ' this works
    values = data.ExcelValues

有什么建议可以完成此操作,而不一次设置变体数组中的每个值吗?

I try to transfer some data between Excel and C#. For this I wrote a simple C# class with a property to set and get the data.

[Guid("xxx")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class Vector
{
    private object[,] _values;

    public object[,] ExcelValues
    {
        get { ... }
        set { ... }
    }
}

Getting the ExcelValues property in VBA works well, but it is not possible to set it in VBA. The VBA code does not compile if I try to set the property:

    Dim values As Variant
    With myRange
        ' typo: Set values = Range(.Offset(0, 0), .Offset(100, 0))
        values = Range(.Offset(0, 0), .Offset(100, 0))
    End With

    Dim data As New Vector

    ' this doesn't compile
    data.ExcelValues = values      

    ' this works
    values = data.ExcelValues

Any suggestions how I can acomplish this, without setting each value from the variant Array one at a time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

同尘 2024-11-13 09:16:15

我找到了一个基于此处发布的代码的解决方案。变体数组必须作为对象(而不是 object[,])从 VBA 传递到 C#。然后可以通过使用反射将其转换为更方便的东西:

[Guid("xxx")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Vector
{        
    [ComVisible(false)]
    public IList<double> Values { get; set; }

    public object[,] GetExcelValues()
    {
        // own extension method
        return Values.ConvertToExcelColumn();
    }

    public void SetExcelValues(object comArray)
    {
        IEnumerable<object> values = ConvertExcelCloumnToEnumerable(comArray);
        Values = new List<double>(values.Select(Convert.ToDouble));
    }

    private static IEnumerable<object> ConvertExcelCloumnToEnumerable(object comObject)
    {
        Type comObjectType = comObject.GetType();

        if (comObjectType != typeof(object[,]))
            return new object[0];

        int count = (int)comObjectType.InvokeMember("Length", BindingFlags.GetProperty, null, comObject, null);
        var result = new List<object>(count);

        var indexArgs = new object[2];
        for (int i = 1; i <= count; i++)
        {
            indexArgs[0] = i;
            indexArgs[1] = 1;
            object valueAtIndex = comObjectType.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, comObject, indexArgs);
            result.Add(valueAtIndex);
        }

        return result;
    }
}

另一种方式 - 从 C# 到 VBA - 它可以更舒适地作为 object[,] 或 double[,] 传递。
希望没有语法错误:)。

I found a solution based on code that was posted here. A variant array has to be passed from VBA to C# as an object (not object[,]). Then it can be converted to something that is more handy by using reflection:

[Guid("xxx")]
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Vector
{        
    [ComVisible(false)]
    public IList<double> Values { get; set; }

    public object[,] GetExcelValues()
    {
        // own extension method
        return Values.ConvertToExcelColumn();
    }

    public void SetExcelValues(object comArray)
    {
        IEnumerable<object> values = ConvertExcelCloumnToEnumerable(comArray);
        Values = new List<double>(values.Select(Convert.ToDouble));
    }

    private static IEnumerable<object> ConvertExcelCloumnToEnumerable(object comObject)
    {
        Type comObjectType = comObject.GetType();

        if (comObjectType != typeof(object[,]))
            return new object[0];

        int count = (int)comObjectType.InvokeMember("Length", BindingFlags.GetProperty, null, comObject, null);
        var result = new List<object>(count);

        var indexArgs = new object[2];
        for (int i = 1; i <= count; i++)
        {
            indexArgs[0] = i;
            indexArgs[1] = 1;
            object valueAtIndex = comObjectType.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, comObject, indexArgs);
            result.Add(valueAtIndex);
        }

        return result;
    }
}

The other way - from C# to VBA - it can be passed more comfortable as object[,] or double[,].
Hope there are no syntax typos :).

清晨说晚安 2024-11-13 09:16:15

通过使用 Set,您可以告诉 VBA“值”是一个对象(在本例中为 Range),但在将其分配给 ExcelValues 时您并没有使用 set。当您读取值时尝试删除 Set。

With myRange         
    values = Range(.Offset(0, 0), .Offset(100, 0)).Value     
End With 

By using Set you're telling VBA that "values" is an object (in this case a Range), but then you're not using set when assigning that to ExcelValues. Try dropping the Set when you're reading the values.

With myRange         
    values = Range(.Offset(0, 0), .Offset(100, 0)).Value     
End With 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文