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:
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?
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[]
.