Mitch Mitch - 2 months ago 22
C# Question

Cross process .net COM IPC throws E_NOINTERFACE

I am trying to use

IRunningObjectTable
to allow a client caller to access an object in another process for purposes of UI automation.

+----------------+ +-----------+
| Client Process |---------------->| UI server |
| .Net WPF | | .Net WPF |
+----------------+ +-----------+


I have implemented an example interface with implementation as follows, and am seeing the registered object in the running object table, but get
E_NOINTERFACE
when casting to the typed interface.

Server



Interface.cs:

[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IChatTarget))]
[ComVisible(true), Guid("FEF67638-6654-47DB-A40D-F11FE72795A9")]
public class ChatInterface : IChatTarget
{
public void PrintString(string text)
{
Console.WriteLine(text);
}
}

[ComVisible(true)]
[Guid("9A3164C9-F800-4B7D-9AB0-29E9F5D325B2")]
public interface IChatTarget
{
void PrintString(string text);
}


Program.cs:

class Program
{
static void Main(string[] args)
{
var ct = new ChatInterface();

var rot = NativeMethods.GetRunningObjectTable(0);
IMoniker moniker = NativeMethods.CreateItemMoniker("!", "TestMoniker");

var hRotEntry = rot.Register(NativeMethods.ROTFLAGS_REGISTRATIONKEEPSALIVE, (IChatTarget)ct, moniker);

MSG msg;
while (NativeMethod.GetMessage(out msg, IntPtr.Zero, 0, 0))
{
NativeMethod.TranslateMessage(ref msg);
NativeMethod.DispatchMessage(ref msg);
}

rot.Revoke(hRotEntry);
}
}


Client



Proxy.cs:

public class ChatProxy : IChatTarget
{
IChatTarget target;

public ChatProxy()
{
var rot = NativeMethods.GetRunningObjectTable(0);
var moniker = NativeMethods.CreateItemMoniker("!", "TestMoniker");

object utobj;
if (rot.GetObject(moniker, out utobj) != 0 /* S_OK */)
{
throw new InvalidOperationException("Moniker not in table");
}
// This throws ComException(E_NOINTERFACE)
target = (IChatTarget)utobj;
}

public void PrintString(string text)
{
target.PrintString(text);
}
}


Program.cs:

class Program
{
static void Main(string[] args)
{
var cp = new ChatProxy();
while (true)
cp.PrintString(Console.ReadLine());
}
}


I am registering the server assembly using
regasm
.

Answer

In order to get the typed proxy, the assembly containing the interface must be strongly named.

Sign the assembly with a strong name key, and all should be well.

Other pitfalls:

  • regasm must be called with the /tlb flag to register for OLE marshalling, otherwise you can get E_NOINTERFACE. You can check by looking for the ProxyStubClsid32 key. Complete command should be regasm assembly.dll /tlb and regasm assembly.dll /tlb /u to unregister.
  • For an in-process server, the assembly must be in the GAC, or the /codebase flag must be passed to regasm
  • Registration should be using the same bitness as the client (Wow6432Node)
  • For an assembly marked with Any CPU, you must register with both the 32-bit and 64-bit versions of regasm