How to correctly marshal VB-Script arrays to and from a COM component written in C#

VBScript only likes to handle SAFEARRAY's that contain VARIANTS. And it likes to have these passed arround in VARIANTS on the COM methods or properties. So you need to construct a VARIANT property that contains a SAFEARRAY of VARIANT type. The following C# code does this. First using just a plain array of objects and then also showing we can cast an array of any other managed type into an array of objects such that the marshalling code will convert this into a SAFEARRAY of VARIANTs for us.

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(); } }
    }
}

This can be tested using the following 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

You can see the type reported here as Variant() - ie: an array of variants.

C:\Users\pat>\windows\SysWOW64\cscript.exe -nologo arraytest.vbs
Count 10 type: Variant() names: Variant()
0
1
1
2
3
5
8
13
21
34
one
two
three
four
five

Not so much an answer but some additional information:

This is how to consume patthoyts' answer in Classic ASP using VBScript:

<%@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
%>

I cannot access the individual array elements (eg. o.Names(2)) which indicates that it isn't an array but acting more like a collection.

JScript version:

<%@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]);
%>