【发布时间】:2020-02-05 18:59:28
【问题描述】:
我正在用 C# (.Net 4.0) 构建一个 COM 对象,用于经典的 asp 站点。现在我想知道在组件和asp站点之间来回编组VB-Script数组(单维和多维)的正确方法是什么?非常感谢您提供代码示例。
【问题讨论】:
标签: .net arrays asp-classic marshalling com-interop
我正在用 C# (.Net 4.0) 构建一个 COM 对象,用于经典的 asp 站点。现在我想知道在组件和asp站点之间来回编组VB-Script数组(单维和多维)的正确方法是什么?非常感谢您提供代码示例。
【问题讨论】:
标签: .net arrays asp-classic marshalling com-interop
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 一 二 三 四 五
【讨论】:
Cast 方法在 System.Linq 命名空间中提供,并在 System.Core 程序集中提供(请参阅msdn.microsoft.com/en-us/library/bb341406.aspx),因此默认项目应该包含您需要的所有引用。我的项目副本包含 System、System.Core 和 Microsoft.CSharp 的引用,仅此而已。 using 块如上图所示。我用 Visual Studio 2010 构建了这个,但 2008 也应该没问题。
public object Names { get { return mNames.Cast<object>().ToArray(); } },我怀疑它在某个时候从 VS2008 切换到 VS2010 时被修改了。
[MarshalAs(...)] 属性都可以删除,因为默认值已经使用了 VARIANT 和 SAFEARRAY。此外,我找不到您使用SafeArraySubType = VarEnum.VT_ARRAY 和UnmanagedType.Struct(而不是UnmanagedType.SafeArray)的任何参考。它可能没有效果并被忽略。 (文档说:“表示 UnmanagedType.SafeArray 的元素类型。”)。这篇博文 (blogs.msdn.com/b/adam_nathan/archive/2003/04/24/56642.aspx) 可能会引起您的兴趣。
与其说是答案,不如说是一些附加信息:
这是如何使用 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]);
%>
【讨论】:
有点晚了,但如果将来有人需要这个:
我设法将ArrayList 的Hashtables 传递给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。
【讨论】: