Passing SAFEARRAY of Bytes from VBScript to Managed COM object (C#

G

Guest

Attempting to pass a VBScript Array of Bytes to a C# Managed COM object has
resulted in some very undesirable and strange behavior. When definining a
method in my C# COM class as "void Write(byte[] bBytes)" or "void
Write(byte[] bBytes, int nLen)" or "int Write(byte[] bBytes, int
nLen)"VBScript returns a "Invalid procedure call or argument" exception.
However when I define the method as "int Write(byte[] bBytes)" it works just
fine. Unfortunately the method definition that works is not the one I need.
Regardless of whether one works or one doesn't work, they should all work.
Does anyone know why the CCW can't handle the method definition, and why it
is behaving in such a flakey manner?

Things I have tried to make it work:
- I have used the appropriate MarshalAs attribute in front of the byte[]
array.
- I have used the [ClassInterface(ClassInterfaceType.None)] attribute with
an explicit interface to make the tlbexp tool explicitly define the interface
methods.
 
W

Willy Denoyette [MVP]

Complex types as arrays are expected to be VARIANT's when calling into COM
through COM interop.
So you have to declare them as Object (or ref Object for [in,out]) in
CSharp.

Here's a sample that illustrates this...

// CS file
using System.Runtime.InteropServices;
using System;

// interface
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("1ab6f6ea-83b5-4b16-934b-fb82d9af8e0a")]
public interface IDotNetInterface
{
int Write( Object aParam1); //[in]VARIANT
}

// class
[ClassInterface(ClassInterfaceType.None)]
[ProgId("Test.DotNetIf")]
public class DotNetInterface : IDotNetInterface
{
public DotNetInterface() {}
public int Write( Object aParam1)
{
foreach(byte b in aParam1 as Array)
Console.WriteLine(b);
return (aParam1 as Array).Length; // Return Array.Length (will be
marshalled as a VARIANT containing an int type)
}
}

'VBS file
On Error Resume Next
Dim o
Dim ar 'Variant
Dim arr(2) 'Array 3 elements
arr(0) = CByte(1) 'add 1 as byte value in element 0
arr(1) = CByte(2)
arr(2) = CByte(3)
ar = arr 'set ar to array
set o = CreateObject("Test.DotNetIf")
r = o.Write (ar) 'pass Variant containing an array of bytes to callee
if Err.Number <> 0 then
Wscript.Echo "Failure :" & Err.Description
else
Wscript.Echo r
end if

Willy.
 
W

Willy Denoyette [MVP]

Note that you can also pass them byval as SAFEARRAY(VARIANT) (Variant array)

//CS file
public interface IDotNetInterface
{
int Write(object[] aParam1);
}
....

public int Write( object[] aParam1) // Object [] == SAFEARRAY(Variant)
{
foreach(byte b in aParam1) // Variant encapsulates a byte
Console.WriteLine(b);
return aParam1.Length;
}

'SCRIPT
Dim ar
Dim arr(2)
arr(0) = CByte(1)
arr(1) = CByte(2)
arr(2) = CByte(3)
ar = arr
WScript.Echo TypeName(arr)
set o = CreateObject("Test.DotNetIf")
r = o.Write ((ar)) 'Pass ar byval
'OR
r = o.Write ((arr)) 'Pass arr byval

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top