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.
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);
}
}
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
.
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./codebase
flag must be passed to regasm
Wow6432Node
)regasm