views:

265

answers:

1

What is the proper way to pass an array of user defined classes from vba to .net (specifically c#) using com-interop?

Here's my c# code. If I call Method1 from vba it's failing with "Array or userdefined type expected" or "Function uses an automation type not supported in visual basic".

public class MyClass 
{
    public Method1(UserDefinedClass[] Parameters) { ... }
    public Method2(Object Parameters) { ... }
}

I've read a bit about the MarshallAsAttribute class. Could this be the missing piece in the c# code?

Here's the vba code I'm using:

Dim udt As New UserDefinedClass
Dim myArray()
myArray(1) = udt
myClass.Method1(myArray)
myClass.Method2(myArray)
+1  A: 

IIRC you have to pass arrays by reference.

Try declaring your method as

public class MyClass  
{ 
    public void Method1([In] ref UserDefinedClass[] Parameters) { ... } 
    ...
} 

If you don't want to pollute your class with ref parameters for .NET clients, you can define a ComVisible interface to be used by COM clients, and implement it explicitly thus:

[ComVisible(true)]
public interface IMyClass  
{ 
    void Method1([In] ref UserDefinedClass[] Parameters) { ... } 
    ...
} 

public class MyClass : IMyClass
{
    void IMyClass.Method1(ref UserDefinedClass[] Parameters)
    {
        this.Method1(Parameters);
    }

    public Method1(UserDefinedClass[] Parameters)
    {
        ...
    }
}

** In response to comment ** If you want to expose a collection instead of an array to VBA, you just need to expose an enumerator, and any other methods you want the VBA code to be able to call (e.g. Add, Remove, Insert, Clear, ...). E.g.

[ComVisible]
public interface IUserDefinedClassCollection
{
    IEnumerator GetEnumerator();

    int Count { get; };

    IUserDefinedClass this[int index] { get; }

    int Add(IUserDefinedClass item);

    // etc, other methods like Remove, Clear, ...
}

You can then use it as usual in VBA:

Dim objUserDefinedClasses As UserDefinedClassCollection
...
objUserDefinedClasses.Add objUserDefinedClass 
...
For nIndex = 0 To objUserDefinedClasses.Count

Next nIndex
Joe
I've tried, that but I haven't explicitly tried adding the 'in' clause. I'll give that a shot as well. using 'ref UserDefinedClass[] foo' resulted in the same error I'm afraid.
Freddie
So, you're saying that I shouldn't need to include any additional attributes on the 'Parameters' parameter. I've seen other code that uses MarshallAs here, but as I mentioned that didn't seem to work in my scenario.
Freddie
"So, you're saying that..." - I know you need ref for array arguments of primitive types. The [In] attribute affects generation of the IDL and isn't necessary. I would normally use a collection type for passing user-defined classes to VBA (public class MyUserDefinedClassCollection : Collection<UserDefinedClass>), so I haven't actually tried this.
Joe
A collection class would be fine as well. What would the vba code look like to pass a collection to c#?
Freddie
You would not need to pass it as ref. E.g. "void Method1(MyUserDefinedClassCollection collection);". Of course the collection class as well as MyUserDefinedClass needs to be ComVisible.
Joe
I understand what the c# code would look like for the collection, but what would the VB equivalent be?
Freddie