I have a C++ COM module that accept IDispatch interfaces and on certain condition invoke them using DISPID_VALUE. This method work very well in C++. Now I have a client in C# and I want to implement an object that implement IDispatch and have a method with DISPID = 0(DISPID_VALUE). I already tried this:
// This will generate invalid cast
[ComVisible(true)]
class Callback1
{
[DispId(0)]
void Execute(object arg) {...}
}
// This also generate invalid cast
[ComVisible(true)]
[Guid("163AC24E-90DB-47D4-8580-EBB21E981FBF"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
interface ICallback2
{
[DispId(0)]
void Execute(object arg) ;
}
[Guid("842A7754-7CE6-4991-9E12-3FAB2367591A"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(ICallback2))]
class Callback2 : ICallback2
{
public void Execute(object arg) {}
}
Also I don’t remember how, but I also write a code that cast successfully but call nothing at all. Now I want to know how should I write a class that implement IDispatch and on call an specific method when DISPID = 0.
Cast exception is:
System.InvalidCastException was unhandled
Message=Specified cast is not valid.
Source=mscorlib
StackTrace:
at System.StubHelpers.InterfaceMarshaler.ConvertToNative(Object objSrc, IntPtr itfMT, IntPtr classMT, Int32 flags)
at nmclientLib.INMAsyncOperation.AddCallback(Object pCallback)
at NMTools.RecorderRegistration.BeginConnection(OperationDoneHandler h) in D:\Programming\Version 0.9\A_Project\NMTools\RecorderRegistration.cs:line 166
at NMTools.ConnectionManager.NoRequestRegisterConnection(RecorderRegistration r, Boolean bConnect) in D:\Programming\Version 0.9\A_Project\NMTools\ConnectionManager.cs:line 396
at NMTools.ConnectionManager.<InitializeFromDatabase>b__0(Object s, EventArgs e) in D:\Programming\Version 0.9\A_Project\NMTools\ConnectionManager.cs:line 339
at System.Windows.Forms.Timer.OnTick(EventArgs e)
at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at TestApp1.Program.Main() in D:\Programming\Version 0.9\A_Project\TestApp1\Program.cs:line 18
InnerException:
Okay, it is clear from the comments what the problem was. You must declare [ComVisible] interfaces and classes public. The CLR honors accessibility, a COM client doesn’t get to use internal types when a .NET program cannot do so either.
A better exception message would have been nice, but that’s par for the course in COM error handling. Which doesn’t otherwise have anything similar to accessibility constraints so there isn’t a more specific error code than E_NOINTERFACE. Which gets translated to InvalidCastException.
Notable is that this is very rare, using a [ComVisible] .NET class in a .NET application doesn’t make much sense. Just use the class directly by adding a reference to the assembly. You’ll get rid of the registration requirement, the ducky error messages and a chunk of overhead in a method call. Modulo some kind of COM layer you cannot get rid of, that happens.