从COM +(VBScript)访问ref返回的数组时出现“类型不匹配”错误

时间:2010-11-18 14:43:55

标签: .net vbscript com+

我有一个.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 个答案:

答案 0 :(得分:3)

我设法自己解决这个问题。

事实证明,VBScript不处理非Variant类型的数组。所以,在我的C#代码中我尝试了这个;

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

“...”指的是通过ref。

传递arrayToFill的其他调用

不幸的是,这产生了完全相同的错误。

让我走上解决问题的道路是“TypeName()”VBScript函数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;
    } 
 }

这只是一个粗略的第一步 - 将增加更强大的错误处理和对锯齿状阵列的支持。

所以我的代码现在看起来像;

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

现在,在VBScript中,TypeName返回 Variant(),我可以按预期访问数组。

希望这将有助于将来遇到此问题的其他人。

答案 1 :(得分:1)

很难理解为什么脚本解释器会对索引数组进行索引。这可能与数组的下限为零有关,VBScript中没有Option Base语句。在.NET中创建一个不从零开始的数组在技术上是可行的,通过Array.CreateInstance(),它的一个重载允许你创建一个具有非零下限的数组。我会提到VariantWrapper类,但不认为它是相关的。使数组成为返回值是另一种尝试作为解决方法。

答案 2 :(得分:0)

我有一个类似的问题,从VB 6.0程序传递一系列双打 用VB.net(2010)编写的COM服务器。

将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开始的数组索引。

答案 3 :(得分:0)

这是我根据C.McAtackney的回答所做的:

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