如何正确地在用C#编写的COM组件中传递VB-Script数组

14

我正在使用C# (.Net 4.0)构建一个COM对象,用于经典asp网站。现在我想知道如何在组件和asp站点之间传递VB-Script数组(单维和多维)的正确方法是什么?非常感谢提供示例代码。

3个回答

19

VBScript只支持处理包含VARIANT的SAFEARRAY。而且它喜欢在COM方法或属性中将这些传递给包含VARIANT的VARIANT。因此,您需要构建一个包含VARIANT类型的SAFEARRAY的VARIANT属性。以下C#代码可以实现此目的。首先使用普通对象数组,然后还展示了我们可以将任何其他托管类型的数组转换为对象数组,以便封送代码将其转换为SAFEARRAY的VARIANT。

using System;
using System.Runtime.InteropServices;
using System.Linq;

namespace StackOverflow
{
    [ComVisible(true)]
    [Guid("2F4C19A6-9BB9-4ACF-90D1-BAF48696740A")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IMyArrayDemo
    {
        [DispId(1)]
        int Count
        {
            [return: MarshalAs(UnmanagedType.I4)]
            get;
        }
        [DispId(2)]
        object Data
        {
            [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)]
            get;
        }
        [DispId(3)]
        object Names
        {
            [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)]
            get;
        }
    }

    [ComVisible(true)]
    [Guid("7EF75834-22BE-4861-879B-EA0CE20E46E9")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("StackOverflow.MyArrayDemo")]
    public class MyArrayDemo : IMyArrayDemo
    {
        object[] mData = new object[10] { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 };
        string[] mNames = new string[5] {"one", "two", "three", "four", "five"};
        public int Count { get { return mData.Length; } }
        public object Data { get { return mData; } }
        public object Names { get { return mNames.Cast<object>().ToArray(); } }
    }
}
这可以通过以下VBScript进行测试:

Option Explicit
Sub Main
  Dim o, v
  Set o = CreateObject("StackOverflow.MyArrayDemo")
  WScript.Echo "Count " & o.Count & " type: " & TypeName(o.Data) & " names: " & TypeName(o.Names)
  For Each v in o.Data : WScript.Echo CStr(v) : Next
  For Each v in o.Names : WScript.Echo v : Next
End Sub
Main

这里报告的类型是Variant(),也就是一组变量数组。

C:\Users\pat>\windows\SysWOW64\cscript.exe -nologo arraytest.vbs
总数为10,类型为:Variant(),名称为:Variant()
0
1
1
2
3
5
8
13
21
34
one
two
three
four
five

在 mNames.Cast 行上出现以下错误:'string[]' 不包含 'Cast' 的定义,也没有接受类型为 'string[]' 的第一个参数的扩展方法 'Cast' 可用(是否缺少 using 指令或程序集引用?) - Graham
Cast方法位于System.Linq命名空间中,且在System.Core程序集中提供(请参阅http://msdn.microsoft.com/en-us/library/bb341406.aspx),因此默认项目应该已包含所有所需的引用。我拥有的项目副本只包含System、System.Core和Microsoft.CSharp三个引用,没有其他引用。上面显示了using块。我使用Visual Studio 2010构建了这个项目,但2008也应该可以。 - patthoyts
我的参考资料包括其中的三个以及其他一些,例如System.Xml.Linq。我正在使用2010版本。我已经将参考资料减少到只有这三个,但错误仍然存在。 - Graham
1
啊 - 仔细看我的当前版本,我怀疑在某个时候从VS2008切换到VS2010时修改了public object Names { get { return mNames.Cast<object>().ToArray(); } } - patthoyts
看起来你所有的 [MarshalAs(...)] 属性都可以被删除,因为默认情况下已经使用了 VARIANT 和 SAFEARRAY。此外,我找不到任何关于你在 UnmanagedType.Struct 中使用 SafeArraySubType = VarEnum.VT_ARRAY(而不是 UnmanagedType.SafeArray)的参考。它可能没有效果并被忽略。(文档说:“指示 UnmanagedType.SafeArray 的元素类型。”)。这篇博客文章(http://blogs.msdn.com/b/adam_nathan/archive/2003/04/24/56642.aspx)可能会有所帮助。 - Micha Wiedenmann

2

这不是一个答案,而是一些额外的信息:

以下是使用VBScript在Classic ASP中消耗patthoyts答案的方法:

<%@Language=VBScript%>
<%
  Dim o, v
  Set o = CreateObject("StackOverflow.MyArrayDemo")
  Response.Write "Count " & o.Count & " type: " & TypeName(o.Data) & " names: " & TypeName(o.Names)
  For Each v in o.Data
    Response.Write "<br />" & v
  Next
  For Each v in o.Names
    Response.Write "<br />" & v
  Next
%>

我无法访问单个数组元素(例如o.Names(2)),这表明它不是一个数组,而更像是一个集合。

JScript版本:

<%@Language=JScript%>
<%
  var o, v;
  o = Server.CreateObject("StackOverflow.MyArrayDemo")
  Response.Write ("Count " + o.Count + " type: " + (typeof o.Data) + " names: " + (typeof o.Names));

  var a = o.Data.toArray();
  for (v=0; v<a.length; v++)
    Response.Write ("<br />" + a[v]);

  var b = o.Names.toArray();
  for (v=0; v<b.length; v++)
    Response.Write ("<br />" + b[v]);
%>

1

有点晚了,但以备后人需要:

我成功地将一个HashtableArrayList传递给了Classic ASP。似乎可以传递System.Collections命名空间的类型,但不能传递System.Collections.Generic

.cs文件:

using System;
using System.Runtime.InteropServices;
using System.Collections;

namespace Test
{
    [ComVisible(true)]
    [Guid("D3A3F3E7-F1A9-4E91-8D7B-D9E19CF38165")]
    public interface iDemo
    {
        [return: MarshalAs(UnmanagedType.Struct, SafeArraySubType = VarEnum.VT_ARRAY)]
        ArrayList DemoMethod();
    }


    [ProgId("Test.Demo")]
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("F53257DD-9275-4D6C-A758-EFF6932FF8B2")]
    [ComVisible(true)]
    public class Demo : iDemo
    {
        [ComVisible(true)]
        public ArrayList DemoMethod()
        {
            ArrayList Results = new ArrayList();

            for (int i = 0; i < 5; i++)
            {
                Hashtable table = new Hashtable();
                table.Add("Text", "Test"+i);
                table.Add("Number", i);
                Results.Add(table);
            }
            return Results;
        }
    }
}

"

.asp文件:

"
<%
set test = server.createObject("Test.Demo")
set results = test.DemoMethod()
response.write "Results: " & results.count & "<br><br>"
for each result in results
    response.write result("Text") & "<br>"
    response.write result("Number") & "<br><br>"
next
%>

输出:

Results: 5

Test0
0

Test1
1

Test2
2

Test3
3

Test4
4

这对于从C#到经典ASP(应该也适用于VB Script,但未经测试)传递大量数据非常方便,因为您可以循环遍历具有任何属性的对象。另一种方式没有进行测试,因为我只需要将数据从C#传递到经典ASP。

@RezaJenabi 请仅在代码中使用反引号。"VB Script" 不是代码,不应该用反引号包含。 - Konrad Rudolph

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