Skip to content

Instantly share code, notes, and snippets.

@sevaa
Last active July 28, 2022 18:17
Show Gist options
  • Save sevaa/9a7dbe682258a2a3c2f404be90d119be to your computer and use it in GitHub Desktop.
Save sevaa/9a7dbe682258a2a3c2f404be90d119be to your computer and use it in GitHub Desktop.
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Text;
using System;
using System.Threading;
namespace COMNET
{
public class Program
{
[STAThread]
public static int Main(string[] args)
{
string Cookie = "0123456789"; //In a real application, probably somehow conditional
//Run the server process - if that somehow fails, an exception will be thrown
StartServer(Cookie);
//Pull the server object from the ROT
IProtocol Server = new RunningObjectTable().Get(Protocol.MONIKER_PREFIX + Cookie) as IProtocol;
//Invoke the method
string Response = Server.Hello(17, ASCIIEncoding.ASCII.GetBytes("Hello from client"));
Console.WriteLine(Response);
return 0;
}
private static void StartServer(string Cookie)
{
using (EventWaitHandle Evt = new EventWaitHandle(false, EventResetMode.AutoReset, Protocol.STARTED_EVENT + Cookie))
{
using (Process? Proc = Process.Start(new ProcessStartInfo("Server.exe", Cookie)))
{
if (Proc == null)
throw new Exception($"Server could not start");
using (ProcessWaitHandle HProcess = new ProcessWaitHandle(Proc))
{
if (WaitHandle.WaitAny(new WaitHandle[] { Evt, HProcess }) == 1)
{
Proc.WaitForExit();
throw new Exception($"Server could not start with exit code {Proc.ExitCode}");
}
}
}
}
}
public class ProcessWaitHandle : WaitHandle
{
public ProcessWaitHandle(Process p)
{
SafeWaitHandle = new SafeWaitHandle(p.Handle, false);
}
}
}
}
using System.Runtime.InteropServices;
namespace COMNET
{
public class Protocol
{
public static readonly string
STARTED_EVENT = "COMNET_Started_",
MONIKER_PREFIX = "COMNET/";
}
[Guid("00020400-0000-0000-C000-000000000046")] //IDispatch, so that it marshals without registration
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IProtocol
{
string Hello(int n, byte[] b);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
namespace COMNET
{
internal class RunningObjectTable
{
#region API
[DllImport("ole32.dll")]
private static extern int CreateItemMoniker([MarshalAs(UnmanagedType.LPWStr)] string
lpszDelim, [MarshalAs(UnmanagedType.LPWStr)] string lpszItem,
out IMoniker ppmk);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
#endregion
private IRunningObjectTable m_rot;
public RunningObjectTable()
{
GetRunningObjectTable(0, out m_rot);
}
private IMoniker CreateItemMoniker(string s)
{
IMoniker mon;
CreateItemMoniker("", s, out mon);
return mon;
}
public int Register(string ItemName, object o)
{
return m_rot.Register(0, o, CreateItemMoniker(ItemName));
}
public void Unregister(int ROTCookie)
{
m_rot.Revoke(ROTCookie);
}
public object Get(string ItemName)
{
object o;
m_rot.GetObject(CreateItemMoniker(ItemName), out o);
return o;
}
}
}
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System;
namespace COMNET
{
public class Program
{
[MTAThread]
public static int Main(string[] args)
{
string Cookie = args[0];
//Create a server object, register in the Running Object Table with a cookie derived name
new RunningObjectTable().Register(Protocol.MONIKER_PREFIX + Cookie, new ServerImpl())
//Report readiness to the parent process
using (EventWaitHandle StartedEvent = EventWaitHandle.OpenExisting(Protocol.STARTED_EVENT + Cookie))
StartedEvent.Set();
//And wait for ever.
EventWaitHandle QuitEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
QuitEvent.WaitOne();
return 0;
}
}
//The server object
[ClassInterface(ClassInterfaceType.None)]
public class ServerImpl : IProtocol
{
public string Hello(int n, byte[] b)
{
return $"Hello from server: got {n} and {ASCIIEncoding.ASCII.GetString(b)}";
}
}
}
@sevaa
Copy link
Author

sevaa commented Dec 11, 2021

Companion gist for this blog post

It demonstrates a simple COM based cross process communication on the cheap, for managed code. Originally compiled against .NET 6, but it works equally well under .NET Framework 4.7.2, if recompiled.

A similar IPC technique in native C++ code is under this gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment