tags:

views:

64

answers:

2

I'm trying to expose a fairly simple C# class to COM which should be usable from VBScript (among others). Some objects need to be created via COM calls and will be used in furter calls later on. The definition of the exposed classes and interfaces looks like this:

namespace Test 
{
  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface IComInterface
  {
      IFoo CreateFoo();
      void UseFoo(int x, IFoo f);
  }

  [ClassInterface(ClassInterfaceType.None)]
  public sealed class CComInterface : IComInterface
  {
      public CComInterface() {}
      public IFoo CreateFoo() { return new Foo(); }
      public void UseFoo(int x, IFoo f) { f.Bar(); }
  }

  [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
  public interface IFoo
  {
      void Bar();
  }

  [ClassInterface(ClassInterfaceType.None)]
  public class Foo : IFoo
  {
      internal Foo() {}
      public void Bar() {}
  } 
}

The simplest thinkable COM client in VBScript does this:

Dim ci
Set ci = WScript.CreateObject("Test.CComInterface")
Dim foo
Set foo = ci.CreateFoo
foo.Bar
ci.UseFoo 0, foo

While the call to Bar succeeds, calling UseFoo fails with "Error 5: invalid procedure call or invalid argument"

The generated IDL seems ok to me:

dispinterface IComInterface {
    properties:
    methods:
        [id(0x60020003)]
        IFoo* CreateFoo();
        [id(0x60020004)]
        void UseFoo(
                        [in] long x, 
                        [in] IFoo* f);
};

The vbs call succeeds when I wrap the second parameter in parentheses like this:

ci.UseFoo 0, (foo)

As far as I understand (I'm no VBScript expert however) this forces the reference to foo to be passed by value, i.e. a copy of the reference is being made.

How can I change the interface so that it can be called with the foo reference directly? Since this will be a public interface used by customers I don't like the idea of having to explain why all the objects created need to be passed back in an extra pair of parentheses...

+1  A: 

Try changing the C# interface to take a ref parameter.

SLaks
Already tried that, result is that the tlb lists the parameter as void UseFoo([in] long x, [in, out] IFoo** f);but I still need to wrap it in parentheses in VBScript.
nopopem
A: 

Louis.fr on msdn social helped me out - the trick is to tell the marshaller how to marshal the parameter like this:

public void UseFoo(int x, [MarshalAs(UnmanagedType.IDispatch)] IFoo f) { f.Bar(); }
nopopem