2012-10-19 7 views
2

Этот C# код в сборе .NET 4.5 ComVisible:C# свойство подвергается VBA (COM): Ошибка выполнения '424': Требуется объект

C# Код

[InterfaceType(ComInterfaceType.InterfaceIsDual)] 
[Guid("22341123-9264-12AB-C1A4-B4F112014C31")] 
public interface IComExposed 
{ 
    double[] DoubleArray { get; set; } 
    object[] ObjectArray { get; set; } 
    object PlainObject { get; set; } 
    double ScalarDouble { get; set; } 
} 

[ClassInterface(ClassInterfaceType.None)] 
[Guid("E4F27EA4-1932-2186-1234-111CF2722C42")] 
[ProgId("ComExposed")] 
public class ComExposed : IComExposed 
{ 
    public double[] DoubleArray { get; set; } 
    public object[] ObjectArray { get; set; } 
    public object PlainObject { get; set; } 
    public double ScalarDouble { get; set; } 
} 

из Excel 2010 32bit VBA, я получил следующее поведение:

VBA код

Dim VBArray(1 To 3) As Double 
VBArray(1) = 1 
VBArray(2) = 2 
VBArray(3) = 3 

Dim oComExposedEarlyBinding As New ComExposed 

' Works 
oComExposedEarlyBinding.ScalarDouble = 5 

' Compile Error: Function or interface marked as restricted, 
' or the function uses an Automation type not supported in Visual Basic 
oComExposedEarlyBinding.DoubleArray = VBArray 

' Compile Error: Function or interface marked as restricted, 
' or the function uses an Automation type not supported in Visual Basic 
oComExposedEarlyBinding.ObjectArray = VBArray 

' Run-time error '424': Object required 
oComExposedEarlyBinding.PlainObject = VBArray 

' Run-time error '424': Object required 
oComExposedEarlyBinding.PlainObject = 5 

Dim oComExposedLateBinding As Variant 
Set oComExposedLateBinding = New ComExposed 

' Works 
oComExposedLateBinding.ScalarDouble = 5 

' Run-time error '5': Invalid procedure call or argument 
oComExposedLateBinding.DoubleArray = VBArray 

' Run-time error '13': Type mismatch 
oComExposedLateBinding.ObjectArray = VBArray 

' Works 
oComExposedLateBinding.PlainObject = VBArray 

' Works 
oComExposedLateBinding.PlainObject = 5 

Как вы уже заметили, что PlainObject работает в режиме позднего связывания, но, очевидно, за счет потери ввода и, следовательно, потери автоматического завершения (IntelliSense) в VBA, что неприемлемо в моем сценарии.

Линии, которые я забочусь и в моем примере, являются следующие строки:

oComExposedEarlyBinding.DoubleArray = VBArray 
oComExposedEarlyBinding.ObjectArray = VBArray 
oComExposedEarlyBinding.PlainObject = VBArray 

Получение любой из трех линий выше работы будет удовлетворять мои потребности, так что у вас есть какие-либо обходной путь или решение, которое сделало бы эта работа (обратите внимание, что мне не интересно передавать массив в качестве параметра функции)?

Обновление: После подачи этой проблемы на поддержку Microsoft и ожидания почти трех недель. Они подтвердили, что это ошибка, и это KB: http://support.microsoft.com/kb/327084, и единственным обходным решением в C# является то, что помечено как решение ниже. Однако я могу подтвердить, что этот код работает так, как ожидалось, если он написан на C++/CLI.

+2

Массив VBA, должно быть, нижняя граница 0, чтобы быть совместимым с C# массива. –

+0

Я передавал массивы в виде функциональных параметров от COM до .NET, а затем преобразовывал их в нуль в .NET.Вы говорите, что из-за этой проблемы вышеописанный код не работает? – Adam

ответ

2

VBA массив должен быть равен нулю на основе и в C# использование параметра реф, образец:

Option Explicit 

Sub test() 
    Dim VBArray(0 To 2) As Double 
    VBArray(0) = 1 
    VBArray(1) = 2 
    VBArray(2) = 3 

    Dim oComExposedEarlyBinding As New ComExposed 
    oComExposedEarlyBinding.SetDoubleArray VBArray 

End Sub 

using System.Runtime.InteropServices; 

namespace COMVisibleTest 
{ 
    [InterfaceType(ComInterfaceType.InterfaceIsDual)] 
    [Guid("22341123-9264-12AB-C1A4-B4F112014C31")] 
    public interface IComExposed 
    { 
     void SetDoubleArray(ref double[] doubleArray); 
    } 

    [ClassInterface(ClassInterfaceType.None)] 
    [Guid("E4F27EA4-1932-2186-1234-111CF2722C42")] 
    [ProgId("ComExposed")] 
    public class ComExposed : IComExposed 
    { 
     private double[] _doubleArray; 

     public void SetDoubleArray(ref double[] doubleArray) 
     { 
      _doubleArray = doubleArray; 
     } 
    } 
} 
+0

Это уже было сказано в ответе, который я удалил, поскольку OP не понравилось это решение. – yms

+0

Спасибо, Дэниел. У меня нет проблем передавать его как параметр функции. Но я спрашивал конкретно о свойствах, следовательно, название и примечание в конце. – Adam

+0

Ну, Адам, я не заметил, что вы попросили имущество ... хотя вы написали его в названии своего вопроса :-) ... извините! Это единственный способ, которым я знаю, как передавать данные в VBA из C#. Удачи! – dee

1

VBA всегда передает массивы, завернутые в вариант по ссылке (VT_VARIANT | VT_BYREF), с другим вариантом внутри, содержащим фактический массив, поэтому вы не можете использовать массивы в свойствах при задании типов элементов, вам необходимо использовать метод что вы можете указать параметры как «по ссылке».

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] 
[Guid("22341123-9264-12AB-C1A4-B4F112014C31")] 
public interface IComExposed 
{ 
    void setDoubleArray(ref double[] myArray); 
    //(...) 
} 

Аналогичный вопрос:
Pass an array from vba to c# using com-interop

Ответ на этот вопрос упоминается возможность использования определенного пользователем коллекции вместо массива примитивных типов, может быть, также может быть решением для вашего вопрос.

Соответствующие ссылки в документации:

Marshaling ByRef Variants

VARIANT and VARIANTARG in WinAPI

MarshalAsAttribute class in .Net

Passing Arrays to COM in .Net

+0

пользовательская коллекция dosn't помогает мне, поскольку массив в этом примере представляет Range.Value, который является собственным массивом. Хотя решение, которое вы дали, является проработкой, я не думаю, что есть другое решение. – Adam

Смежные вопросы