-1

I have a Com-Visible -Net-Assembly which I want to use from VBScript. Most things works fine except one property thats returns a string[] to VBS.

The interface:

[Guid("25267107-CFD3-4A1B-8D94-639A7F189C0B"),
InterfaceType(ComInterfaceType.InterfaceIsDual),
ComVisible(true)]
public interface IComMethods : IDisposable
{
    string[] Interlockings { get; }
}

The implementation:

public string[] Interlockings
{
    get
    {
        return new string[] { "abc", "def" };
    }
}

The VBScript-Client:

Set mms2spc = CreateObject("Promess.mms2spc.ComMethods")
Dim testLCodes : testLCodes = mms2spc.Interlockings
If Not isEmpty(testLCodes) Then
    If isArray(testLCodes) Then
    Dim iCount : iCount = Ubound(testLCodes) + 1
        Stop
        For iLCode = 0 To Ubound(testLCodes)
            sTextInterlock = testLCodes(iLCode)
        Next
    End If
End If

However, when I try to use this in VBScript I see the Strings comming as an Array but I cannot access the elements. You can see this when looking at the debugger - every (n)-access gives Empty:

enter image description here

I think the marshalling from C# from VBS is wrong so I added an attribute but that doens't change anything:

public string[] Interlockings
{
    [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]`
    get{return new string[] { "abc", "def" };}
}

As a workaround I can declare everything in C# as object. That way it works in VBS but thats kind of awkward to me. So how to get this string[] to VBS?

suriel
  • 191
  • 1
  • 10
  • You need to return `object[]` instead of `string[]` as VBScript doesn't handle string arrays as everything is `Variant`. – user692942 Sep 13 '22 at 11:45

1 Answers1

-1

Other languages do support it, but you cannot use an array of strings from VBScript (internally and in the type library it will be represented as a SAFEARRAY of BSTR).

It only support an array of objects, but you can also use the old ArrayList .NET class (which also allows for each type enumeration), for example in C#:

public interface IComMethods
{
    ArrayList Interlockings { get; }
    object[] InterlockingsAsObjects { get; }
}

public class MyClass: IComMethods
{
    public ArrayList Interlockings => new ArrayList(new string[] { "abc", "def" });
    public object[] InterlockingsAsObjects => new object[] { "abc", "def" };
}

And in VBScript:

set x = CreateObject("MyClassLibrary.MyClass")

' ArrayList
WScript.Echo "Count " & x.Interlockings.Count

for each il in x.Interlockings
    WScript.Echo il
next

WScript.Echo x.Interlockings.Item(1) ' def

' array of objects
ar = x.InterlockingsAsObjects
WScript.Echo "Count " & ubound(ar) - lbound(ar) + 1

for i = lbound(ar) to ubound(ar)
    WScript.Echo ar(i)
next

WScript.Echo ar(1) ' def

Another trick is to declare a COM interface as VBScript expects, but implement it privately so it looks better to .NET callers, something like this:

public class MyClass : IComMethods
{
    // better for .NET clients
    public string[] Interlockings => new string[] { "abc", "def" };

    // explicit implementation
    object[] IComMethods.Interlockings => Interlockings.ToArray<object>();
}

public interface IComMethods
{
    object[] Interlockings { get; }
}

Obviously, you can do the same with ArrayList instead of object[].

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • So there is no way to declare a string[]-prpoperty and marshal it to an variant[]. Using Arraylist instead of object[] is quite the same to me: untyped and non-declarative. Thank you for the answers – suriel Sep 14 '22 at 05:42
  • @suriel - no, you can't have a `string[]` in .NET and expose it somehow as an `object[]` to COM (`object` in .NET will become a `VARIANT` in COM/Type libs) whatever the client language is. But you can use a COM interface private/explicit implementation trick, I have updated my answer. – Simon Mourier Sep 14 '22 at 05:52
  • 2
    Just like it says in the duplicate target already posted hours before this answer was given. All you have to do is look through the answers provided, this answer is basically a summary of those. – user692942 Sep 14 '22 at 08:13
  • @user692942 - "a summary of those" ? not really, the ArrayList has been added *after* this question was posed (?) and I've worked on it and there's nothing about private implementation tricks – Simon Mourier Sep 14 '22 at 08:53
  • 2
    I wonder why Geert chose to [post it in the duplicate target](https://stackoverflow.com/a/73704166/692942) instead of here? Also, it was posted there a good 4 hours before you posted this. If you wanted to leave an answer you should have posted there (the private implementation tricks would have been welcome) and pointed the OP to it as others have done. – user692942 Sep 14 '22 at 08:57
  • @user692942 - "you should have done this". No, as the other answer is 1) not complete and 2) marked asp 3) I do what I think is best – Simon Mourier Sep 14 '22 at 08:59
  • There is no difference, the [tag:asp-classic] tag has no effect on VBScript's ability to communicate with COM DLLs. It just so happens that the question was using VBScript in Classic ASP, which changes nothing at all. That is a really weak argument I've heard many times for justifying earning a few imaginary internet points. – user692942 Sep 14 '22 at 09:04
  • What's rude is ignoring a previously posted duplicate flag and answering anyway when it's clear a duplicate exists. If you really want to help you would add your answer to the existing duplicate target (as Geert did). – user692942 Sep 14 '22 at 10:32