Implementing IDispatch in c#
Asked Answered
E

2

6

I'm writing some test code to emulate unmanaged code calling my c# implementation of a late binding COM object. I have an interface that is declared as an IDispatch type as below.

 [Guid("2D570F11-4BD8-40e7-BF14-38772063AAF0")]
 [InterfaceType(ComInterfaceType.InterfaceIsDual)]
 public interface TestInterface
 {
     int Test();
 }

 [ClassInterface(ClassInterfaceType.AutoDual)]
 public class TestImpl : TestInterface 
 {
 ...
 }

When I use the code below to call IDispatch's GetIDsOfNames function

  ..
  //code provided by Hans Passant
  Object so = Activator.CreateInstance(Type.GetTypeFromProgID("ProgID.Test"));
  string[] rgsNames = new string[1];
  int[] rgDispId = new int[1];
  rgsNames[0] = "Test";

  //the next line throws an exception
  IDispatch disp = (IDispatch)so;

Where IDispatch is defined as:

 //code provided by Hans Passant
 [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-C000-000000000046")]
 private interface IDispatch {
     int GetTypeInfoCount();
     [return: MarshalAs(UnmanagedType.Interface)]
     ITypeInfo GetTypeInfo([In, MarshalAs(UnmanagedType.U4)] int iTInfo, [In, MarshalAs(UnmanagedType.U4)] int lcid);
     void GetIDsOfNames([In] ref Guid riid, [In, MarshalAs(UnmanagedType.LPArray)] string[] rgszNames, [In, MarshalAs(UnmanagedType.U4)] int cNames, [In, MarshalAs(UnmanagedType.U4)] int lcid, [Out, MarshalAs(UnmanagedType.LPArray)] int[] rgDispId);
  }

An InvalidCastException is thrown. Is it possible to cast a c# interface into IDispatch?

Expeditious answered 9/11, 2011 at 18:1 Comment(6)
that cast can only succeed if the C# interface were actually derived from IDispatch which from the codesnippets you provide is not the case...Improve
@Improve - thanks for that. I'm not sure why the same cast snippet succeeds if the ProgID of an unmanaged COM object implementing IDispatch is used?Expeditious
because that COM object is actually derived from IDispatch - I would also suspect some special handling (for performance reasons) in case the .NET runtime sees you access a managed COM object (esp. IF it uses the same runtime version)Improve
Ok, but isn't that the point of the [InterfaceType(ComInterfaceType.InterfaceIsDual)] which "Indicates the interface is exposed to COM as a dual interface, which enables both early and late binding."Expeditious
Yes, but again: IIRC the .NET runtime behaves differently when using a managed COM object from managed code... why not mock the COM object with unmanaged code ?Improve
Why does the IDispatch interface not contain a contract definition for IDispatch::Invoke? - msdn.microsoft.com/en-us/library/aa912367.aspxCactus
P
1

You need to register you assembly with regasm, and you need to mark the classes you want to access from COM with the [ComVisible] attribute. You may also need to generate and register a type-library using tlbexp (to generate) and tregsvr to register it.

Also (from a Win32 perspective) "disp = (IDispatch) obj" is not the same as "disp = obj as IDispatch" - using the 'as' operator actually calls the QueryInterface method on the object to get the pointer to the requested interface, instead of trying to cast an object to to the interface.

Lastly using c#'s 'dynamic' type will probably be closer to what the other guys are doing to access your class.

Profusive answered 13/3, 2012 at 1:27 Comment(0)
L
1

You should just be able to use reflection on the COM type to get the list of methods.

Type comType = Type.GetTypeFromProgID("ProgID.Test");
MethodInfo[] methods = comType.GetMethods();
Langur answered 9/11, 2011 at 18:41 Comment(1)
thanks for the tip, unfortunately the code is being called from 3rd party library and they call GetIDsOfNames. I was trying to implement it in managed code if possible.Expeditious
P
1

You need to register you assembly with regasm, and you need to mark the classes you want to access from COM with the [ComVisible] attribute. You may also need to generate and register a type-library using tlbexp (to generate) and tregsvr to register it.

Also (from a Win32 perspective) "disp = (IDispatch) obj" is not the same as "disp = obj as IDispatch" - using the 'as' operator actually calls the QueryInterface method on the object to get the pointer to the requested interface, instead of trying to cast an object to to the interface.

Lastly using c#'s 'dynamic' type will probably be closer to what the other guys are doing to access your class.

Profusive answered 13/3, 2012 at 1:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.