对于在同一台机器上运行的进程,可能最轻量级的解决方案是使用 PostThreadMessage()。我真的很惊讶没有人给出这个答案,这是老式的 Windows 编程。OP非常接近。观察:
- 每个进程都有一个主线程(本机线程)。
- 主线程有一个消息队列。
- 主线程具有系统全局的线程 ID。
所有的成分都在那里,这是一个把它们放在一起的问题。从概念上讲,它很简单,但棘手的部分是将 RECEIVER 的主线程 ID 传递给 SENDER。你有几个选择:
- 在 SENDER 中,在 Win32 中,您可以从 RECEIVER 的线程信息块中挖掘出线程 ID。 https://stackoverflow.com/a/8058710/420400
- 当 RECEIVER 启动时,您可以将线程 ID 保存在其 Process.StartInfo.Environment 中。它确实存在,并且可以在 SysInternals 的 Process Explorer 中看到——但你很难理解它。同样有一个 Win32 解决方案。 https://www.codeproject.com/Articles/25647/Read-Environment-Strings-of-Remote-Process
- 当 RECEIVER 启动时,您可以将线程 ID 保存在共享内存中。
- (或者更好的东西......)
选项 1 和 2 看起来像是安全漏洞,所以对于这个例子,我选择了选项 3,并在一个很小的内存映射文件中共享线程 ID。
接收器看起来像这样
enum WM { USER = 0x400 }
class MyMessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if ((WM)m.Msg == WM.USER)
{
Console.WriteLine("WM_USER received.");
return true;
}
return false;
}
}
class RECEIVER : IDisposable
{
MemoryMappedFile mmf;
bool disposed = false;
public void MyMessageLoop()
{
uint mainThreadId = GetCurrentThreadId();
Console.WriteLine(mainThreadId);
mmf = MemoryMappedFile.CreateNew(Constants.ThreadIDFileName, IntPtr.Size, MemoryMappedFileAccess.ReadWrite);
using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.ReadWrite))
{
accessor.Write(0, mainThreadId);
}
Application.AddMessageFilter(new MyMessageFilter());
Application.Run();
}
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
// Implement IDisposable and ~RECEIVER() to delete the semaphore, omitted for brevity
// https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.7.2
#region
...
#endregion
}
SENDER 看起来像这样
enum WM { USER = 0x400 }
class Program
{
static void Main(string[] args)
{
string procName = "RECEIVER";
Process[] processes = Process.GetProcesses();
Process process = (from p in processes
where p.ProcessName.ToUpper().Contains(procName)
select p
).First();
uint threadId;
using (var mmf = MemoryMappedFile.OpenExisting(Constants.ThreadIDFileName, MemoryMappedFileRights.Read))
using (var accessor = mmf.CreateViewAccessor(0, IntPtr.Size, MemoryMappedFileAccess.Read))
{
accessor.Read(0, out serviceThreadId);
}
PostThreadMessage(threadId, (uint)WM.USER, UIntPtr.Zero, IntPtr.Zero);
}
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
}