【问题标题】:How to correctly marshal VB-Script arrays to and from a COM component written in C#如何正确编组 VB 脚本数组进出用 C# 编写的 COM 组件
【发布时间】:2020-02-05 18:59:28
【问题描述】:

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

【问题讨论】:

    标签: .net arrays asp-classic marshalling com-interop


    【解决方案1】:

    VBScript 只喜欢处理包含 VARIANTS 的 SAFEARRAY。它喜欢在 COM 方法或属性的 VARIANTS 中传递这些参数。因此,您需要构造一个包含 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
    Count 10 类型:Variant() 名称:Variant()
    0
    1
    1
    2
    3
    5
    8
    13
    21
    34
    一
    二
    三
    四
    五

    【讨论】:

    • 在 mNames.Cast 行上出现以下错误:“string[]”不包含“Cast”的定义,并且没有扩展方法“Cast”接受“string[] 类型的第一个参数' 可以找到(您是否缺少 using 指令或程序集引用?)
    • Cast 方法在 System.Linq 命名空间中提供,并在 System.Core 程序集中提供(请参阅msdn.microsoft.com/en-us/library/bb341406.aspx),因此默认项目应该包含您需要的所有引用。我的项目副本包含 System、System.Core 和 Microsoft.CSharp 的引用,仅此而已。 using 块如上图所示。我用 Visual Studio 2010 构建了这个,但 2008 也应该没问题。
    • 我的参考资料包括这三个以及 System.Xml.Linq 等。我正在使用 2010。减少了对这 3 个的引用,并且错误仍然存​​在。
    • 啊 - 仔细查看我当前的版本有 public object Names { get { return mNames.Cast&lt;object&gt;().ToArray(); } },我怀疑它在某个时候从 VS2008 切换到 VS2010 时被修改了。
    • 看来您的所有[MarshalAs(...)] 属性都可以删除,因为默认值已经使用了 VARIANT 和 SAFEARRAY。此外,我找不到您使用SafeArraySubType = VarEnum.VT_ARRAYUnmanagedType.Struct(而不是UnmanagedType.SafeArray)的任何参考。它可能没有效果并被忽略。 (文档说:“表示 UnmanagedType.SafeArray 的元素类型。”)。这篇博文 (blogs.msdn.com/b/adam_nathan/archive/2003/04/24/56642.aspx) 可能会引起您的兴趣。
    【解决方案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]);
    %>
    

    【讨论】:

      【解决方案3】:

      有点晚了,但如果将来有人需要这个:

      我设法将ArrayListHashtables 传递给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# 传递到 Classic ASP(也应该在 VB 脚本中工作,但未经测试),这将非常方便,因为您可以遍历具有任何属性的对象。也没有反过来测试,因为我只需要将数据从 C# 传递到 Classic ASP。

      【讨论】:

      • @RezaJenabi 请使用反引号仅用于代码。 “VB 脚本”不是代码,它不应该是反引号。
      猜你喜欢
      • 2017-10-09
      • 2010-09-21
      • 2023-03-22
      • 2021-08-27
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多