使用经典ASP从COM对象检索字符串数组

4

我有一个.NET类,其中包含一个简单的字符串数组,可以通过一个访问方法使用,它看起来像这样:

namespace Foo.Bar {
    [ComVisible(true)]
    [Guid("642279A0-85D4-4c7a-AEF5-A9FAA4BE85E5")]
    public class MyClass {
        private string[] _myArray;
        public MyClass() { }

        public MyClass(string[] myArray) {
            _myArray = myArray;
        }

        public string[] MyArray {
            get { return _myArray; }
        }
    }
}

我使用经典ASP使用这个类;

Dim foo 
Set foo = Server.CreateObject("Foo.Bar.MyClass")

if IsArray(foo.MyArray) then Response.Write("IsArray") & "<br />"
Response.Write(typename(foo.MyArray)) & "<br />"
Response.Write(UBound(foo.MyArray)) & "<br />"

这会导致:
IsArray
String()
1

然而,当我尝试使用以下方法访问数组的内容时:;
Response.Write(foo.MyArray(0)) & "<br />"

我遇到的问题:

Microsoft VBScript runtime (0x800A01C2) 参数数量错误或属性赋值无效:'MyArray'

非常感谢任何帮助。

编辑:在消化给出的答案后,提供更多信息。

当将 MyArray 属性的实现更改为:

public object[] MyArray {
    get { return (object[])_myArray; }
}

我随后遇到了以下错误:

Microsoft VBScript runtime (0x800A000D) Type mismatch: 'MyArray'

于是我尝试将每个字符串单独转换为对象;
public object[] MyArray {
    get {
        object[] tmp = new object[_myArray.Count()];
        for (int x = 0; x < _myArray.Count(); x++) {
            tmp[x] = (object)_myArray[x];
        }
        return tmp;
    }
}

那么我回到了:

Microsoft VBScript运行时(0x800A01C2)参数错误或属性赋值无效:“MyArray”

编辑 最终的解决方案是在如何正确地将VB-Script数组从C#编写的COM组件封送到和帮助下完成的。

C#

public object MyArray {
    get { return _myArray.Cast<object>().ToArray(); }
}

VBScript

Dim foo 
Set foo = Server.CreateObject("Foo.Bar.MyClass")

bar = foo.MyArray
Response.Write bar(0)

关键在于暴露对象而不是对象[],正如AnthonyWJones所建议的,在使用之前将数组分配给变量。

再次感谢。


这给了我我所寻找的答案。只需要添加 using System.Linq 就可以使转换起作用。 - Graham
4个回答

3
问题是VBScript实际上不能使用字符串数组,它只能使用变体数组。尝试将MyClass更改为公开一个object[]。

有没有将它转换为“Variant”数组的方法?我之所以问,是因为显然VBScript“知道”它是一个数组,它“知道”它的当前类型,那么它肯定知道如何进行转换吧?我不想改变我的.NET实现,只是为了让遗留系统保持兼容。 - Red Taz
@Rob2211:你可以尝试将属性分配给VBScript变量,然后使用该变量。考虑一下,你可能需要这样做,但我仍然认为你需要改变接口。在COM接口中公开实际使用的内容通常不是一个好主意。最好创建一组特定的COM接口和类来实现这些接口。然后,这些类包装和适配你内部的.NET类。这样,你可以以与消费者(VBScript)兼容的方式构建COM接口,同时不会损害你的.NET代码。 - AnthonyWJones
感谢关于编写包装器的建议,我稍后会重新编写它。不过,VBScript 仍然不太满意,尽管该属性现在公开了 object[](请参见问题编辑),你有什么想法吗? - Red Taz
1
@Rob2211:嗯...听起来你需要明确一下。另一种方法是在属性上添加"MarshalAs"特性。尝试将"UnmanagedType.SafeArray"指定为未托管类型,并将特性的"SafeArraySubType"属性设置为"VarEnum.VT_VARIANT"。 - AnthonyWJones

2

除了Anthony的建议之外

我不确定这是否是最好的方法,但过去我使用了类似于以下代码来处理一维数组。

public object MyArray(int ix = -1){
    string[] tmp = new string[] {"one", "two", "3", "4"};
    return (ix == -1) ? (object)tmp : tmp[ix];
}

在ASP中:

Response.Write(TypeName(foo.MyArray)) 'string()
Response.Write(TypeName(foo.MyArray(0))) 'string

0

VBScript不支持通用集合,例如List<string>,也不支持字符串数组。

我在我的接口类中编写了一个公共函数,将任何通用集合转换为ArrayList。

public ArrayList toArrayList(IEnumerable collection)
{
        var arrayList = new ArrayList();
        foreach (object element in collection)
        {
            arrayList.Add(element);
        }
        return arrayList;
}

这段代码可以在VBScript中像这样使用

 dim connector
 set connector = model.getRelationByID(connectorID)
 'get the related elements
 dim relatedElements
 set relatedElements = model.toArrayList(connector.relatedElements)
 addRelatedElementoAutoDiagram relatedElements(0), relatedElements(1), model

这种方法的优点是我不需要改变C#中任何方法或属性的签名,但仍然可以在VBScript中使用它们。

不需要更改签名的好封装。 - user692942

-2

这段代码演示了如何在COM和ASP之间处理数组:

<% @Language="VBScript" %>
<% Option Explicit %>
<%
Dim tcs
Dim rc
Dim vntInput(0,4)
Dim i

vntInput(0,0) = Request.QueryString("strUser")
vntInput(0,1) = Request.QueryString("intCreate")
vntInput(0,2) = Request.QueryString("intDelete")
vntInput(0,3) = Request.QueryString("intModify")
vntInput(0,4) = Request.QueryString("intView") 

Set tcs = Server.CreateObject("TestCases.ArrayFailure")
rc = tcs.AcceptArray(vntInput)

For i = 0 to UBound(vntInput, 2)
    Response.write "Loop Count " & i & " " & vntInput(0,i) & "<BR>"
Next

%>

这是我找到这段代码的文章链接:
http://202.102.233.250/b2000/ASP/articles/component/pv990826.htm


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接