Friday, July 13, 2007

Marshalling Arrays To VB6's COM Funland pt2

Ok, yesterday I figured out how to get an array of interfaces (actually, objects that implement an interface, but go along with my sloppy grammer, ok?) out of C#, through the COM Callable Wrapper (CCW) and into COM-land.

Wouldn't you know it, but I also need to be able to pass an array of objects back into C# from COM-land.

Well, I'm here to say that figuring this out was a lot easier than yesterday's problem. At first I tried something like:


[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface ITest {
   void TakeBusinessObjects(IBusinessObject[] bos);
}



Well, that doesn't work. In VB6, when you try to compile something that calls TakeBusinessObjects(...) you get the a compile error like the following:


Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic



Well, my Google digging on that particular error actually proved fruitful and the answer is simple:


[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface ITest {
   void TakeBusinessObjects(ref IBusinessObject[] bos);
}


(credit goes to Jon Wojtowicz for his Using COM Callable Wrappers to Extend Existing Visual Basic 6.0 Applications post at EggHeadCafe.)

Hooray! That works! The array gets passed back into .NET-land, and things seem happy. Except, I wouldn't be writing this post if I didn't have a problem, right? Well, TakeBusinessObjects(...) doesn't throw an exception, and it reaches it's return statement successfully. Unfortunately, something gets lost in translation while returning control to VB6-land because I intermediately upon returning, VB6 raises this error:


Class does not support Automation or does not support expected interface
Number: 430



This error makes it sound like I've developed on v2 of some COM component, but I've deployed the compiled EXE on a machine that only has v1 of the COM component. What I don't understand is that the array gets passed through the CCW into .NET-land! It works! Something just goes wrong on the way back to VB-land.

Update: The Answer! (Kind of)


I gotta hand it to my buddy Jimmy - that guy has given me the "Try XYZ" that has fixed whatever problem I was tackling so many times -- and he's come through once again! He suggested that, I take a look at the [In] and [Out] attributes.

By default, when I compile my COM component, it's being assumed that I not only want to be able to take in an array reference, but that I also want to push any changes made to that array reference back out to the caller. Well, lucky for me, I don't make any changes to the array once it's in .NET-land, and I can flag the parameter as only needing to come into the method, and not out. Like this:


[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface ITest {
   void TakeBusinessObjects([In] ref IBusinessObject[] bos);
}



So, while this fixes most of the situations where I would need to pass an array from COM-land into .NET-land, it still irks me that I don't know why VB6 throws that error if the CCW marshaller tries to move the array back out of .NET-land when the method returns.

No comments: