Ошибка «Несоответствие типов» при доступе к массиву, возвращаемому ref из COM + (VBScript) - PullRequest
1 голос
/ 18 ноября 2010

У меня есть сборка .NET 3.5, которая выполняется как серверный компонент COM +, и я хочу вызвать метод этого класса из VBScript (классическая страница ASP).

Это схема метода;

public bool FillArray(ref string[] arrayToFill)
{
    ...
}

Мой VBScript выглядит так:

Dim myComponent, result, myArray

Set myComponent = Server.CreateObject("MyComponentProgID")
result = myComponent.FillArray(myArray)

Response.Write("IsArray = " & IsArray(myArray) & "<br/>")
Response.Write("UBound = " & UBound(myArray) & "<br/>")
Response.Write("TypeName = " & TypeName(myArray) & "<br/>")
Response.Write("Element 1 = " & myArray(1))

Это приводит к следующей ошибке (вызванной строкой, где я делаю вызов FillArray);

Тип ошибки: среда выполнения Microsoft VBScript (0x800A0005) Недопустимый вызов процедуры или аргумент: 'FillArray'

Запуская OLEView, IDL выглядит так:

HRESULT FillArray(
                [in, out] SAFEARRAY(BSTR)* arrayToFill, 
                [out, retval] VARIANT_BOOL* pRetVal);

Я попытался изменить подпись моего метода на следующее:

public bool FillArray(ref object[] arrayToFill)

Что привело к следующему IDL;

HRESULT FillArray(
                [in, out] SAFEARRAY(VARIANT)* arrayToFill, 
                [out, retval] VARIANT_BOOL* pRetVal);

Но все та же ошибка "Недопустимый вызов процедуры или аргумент 'FillArray'".

Наконец, я попытался изменить сигнатуру метода просто на это;

public bool FillArray(ref object arrayToFill)

Который дал следующий IDL;

HRESULT FillArray(
                [in, out] VARIANT* arrayToFill, 
                [out, retval] VARIANT_BOOL* pRetVal);

Теперь выдается новая ошибка;

среда выполнения Microsoft VBScript (0x800A000D) Несоответствие типов

Эта ошибка теперь срабатывает только в последней строке, когда я пытаюсь получить доступ к элементу массива. Если я закомментирую последнюю строку, то получу следующий вывод:

IsArray = True

UBound = 39

TypeName = String ()

Итак, по-видимому, вариант распознается как массив и правильного типа. Кроме того, UBound возвращает правильное количество элементов, но я не могу получить доступ ни к одному из элементов по неизвестной причине.

Кто-нибудь знает, что может быть причиной этого? Я провел некоторое исследование сам и наткнулся на следующую ссылку:

http://connect.microsoft.com/VisualStudio/feedback/details/331632/marshaler-bug-with-vbscript-arrays

Я не уверен на 100%, что это та же самая проблема, так как я не объявляю свои массивы таким же образом в моем коде VBScript. Я искренне надеюсь, что это не та проблема, поскольку у меня нет возможностей для обновления до .NET 4.0.

Ответы [ 4 ]

3 голосов
/ 23 ноября 2010

Мне удалось самому разобраться с этим.

Оказывается, VBScript не обрабатывает массивы, которые не относятся к типу Variant.Итак, в моем коде C # я попробовал это;

public bool FillArray(ref object arrayToFill)
{
    string[] tmpArrayToFill = (string[])arrayToFill;
    ...
    arrayToFill = (object[])tmpArrayToFill
}

«...» относится к дальнейшим вызовам, которые передают arrayToFill по ссылке.

К сожалению, это привело к точно такой же ошибке.

То, что помогло мне решить эту проблему, заключалось в том, что функция VBScript «TypeName ()» STILL видела тип как «String ()».Мне было любопытно, что происходит в коде .NET, поэтому я написал небольшой тест:

string[] stringArray = new string[1];
Console.WriteLine(stringArray.GetType());

object[] objectArray = (object[])stringArray;
Console.WriteLine(objectArray.GetType());

, который дал следующее:

System.String []

System.String []

Это было для меня новостью - я не понимал, что это будет так.Я подумал, что разумно ожидать Object [] для второго типа.

В любом случае, независимо от этого, я написал метод небольшого расширения для создания нового объекта []

public static Array ToObjectArray(this Array input)
{
    if (input != null)
    {
        object[] objArray = new object[input.Length];
        input.CopyTo(objArray, 0);
        return objArray;
    }
    else
    {
        return null;
    } 
 }

Thisэто просто грубый первый шаг - добавим более надежную обработку ошибок и поддержку зубчатых массивов.

Так что мой код теперь выглядит так:

public bool FillArray(ref object arrayToFill)
{
    string[] tmpArrayToFill = (string[])arrayToFill;
    ...
    arrayToFill = tmpArrayToFill.ToObjectArray();
}

А теперь, вVBScript TypeName возвращает Variant () , и я могу получить доступ к массиву, как и ожидалось.

Надеюсь, это поможет всем, кто столкнется с этой проблемой в будущем.

1 голос
/ 18 ноября 2010

Трудно понять, почему интерпретатор скриптов отклоняется при индексации массива.Это может быть связано с тем, что нижняя граница массива равна нулю, в VBScript нет оператора Option Base.Создание массива, который не начинается с нуля в .NET, технически возможно с помощью Array.CreateInstance (), одна из его перегрузок позволяет создавать массив с ненулевыми нижними границами.Я упомяну класс VariantWrapper, но не думаю, что он уместен.Создание возвращаемого значения массива - это еще один способ обойти это.

0 голосов
/ 29 ноября 2011

Это то, что я сделал, основываясь на ответе С. МакАткни:

Private _lastErrors As List(Of Object)
''' <summary>
''' Expose error array to COM object consumer
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks>Type is Object() because VBScript requires a variant type.</remarks>
Public Property LastErrors As Object()

    Get
        If _lastErrors Is Nothing Then
            _lastErrors = New List(Of Object)
        End If

        Return _lastErrors.ToArray
    End Get
    Private Set(value As Object())
        If _lastErrors Is Nothing Then
            _lastErrors = New List(Of Object)
        End If

        _lastErrors.AddRange(value)
    End Set
End Property
0 голосов
/ 10 сентября 2011

У меня была похожая проблема при передаче массива двойников из программы VB 6.0 в COM-сервер, написанный на VB.net (2010).

Объявление функции VB.net как:

Public Function GetSpatialResults(ByRef Results() As Double) As Long

даст мне ошибку несоответствия типов, где:

Public Function GetSpatialResults(ByRef Results As Object) As Long
    Results(1) = 1
    Results(2) = 2
    Results(3) = 3
    GetSpatialResults = 0

Работает нормально, однако мне пришлось использовать индексы массива, начиная с 1, а не 0.

...