Introduction
最近接到一个任务,需要把两个exe间的通信模式由 COM 改为非 COM 的模式。
原因是在win10 UAC模式下,部分COM调用会产生 " No such interface supported" 的错误。
估计给微软开case,他们也会像往常一样糊弄过去。
具体缘由也不细说了,这里主要记录一下除了COM以外,C# IPC (Inter-Process Communication) 的几种主要方法。
Scenario
现有 A.exe B.exe 两个进程,这两个进程没有父子关系,且位于不同的目录。
现在需要 A 按顺序控制 B 执行一些操作。
B 中的方法: 1. Lock() 2. InitData() 3. Run() 4.GetStatus()
前三个方法只需要返回 True False 给 A 就可以了,第四个方法需要返回当前 Status (一个自定义类,包含状态号码和描述信息)
SendMessage
首先想到的是用 Win API: SendMessage WM_COPYDATA
这个原理上很简单,A send message 给 B, B 接收到message后执行一些逻辑,期间 A 阻塞直到 SendMessage 返回。
由于期间牵扯到托管类型与非托管类型的转换,网上各种博客的代码大多是不能拿来就用的。
这里推荐MSDN的一个示例代码:https://code.msdn.microsoft.com/windowsapps/CSReceiveWMCOPYDATA-dbbc7ed7
对于B的前三个方法,SendMessage都是管用的, 但是第四个方法要求返回一个类。
SendMessage 不能改变返回类型,也无法由 B.exe 更改 lParam 传递回 A.exe 。
MS DOC: The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer.
只能让 B 在收到消息后再发一条给 A 作为返回信息。这样就大大增加了通讯的复杂度。
我这里的需求,要考虑其他方法进行 IPC 。
System.IO.Pipes
Anonymous Pipes & Named Pipes 有什么区别,应该用哪个?
An anonymous pipe is an unnamed, one-way pipe that typically transfers data between a parent process and a child process. Anonymous pipes are always local; they cannot be used for communication over a network.
关键词:one-way pipe,parent and child, always local
A named pipe is a named, one-way or duplex pipe for communication between the pipe server and one or more pipe clients. All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles, and provides a separate conduit for client/server communication. The use of instances enables multiple pipe clients to use the same named pipe simultaneously. Named pipes can be used to provide communication between processes on the same computer or between processes on different computers across a network.
关键词:duplex pipe, multiple pipe clients, across a network
由此,区别很明显了,Named Pipes 适用于更复杂的需求,双向管道,多个Client进程,跨网络等。
虽然匿名管道似乎足够了,但这里我还是打算使用命名管道,谁知道需求会不会提升呢?
示例代码中包含了 Native Method 和 Managed Method 两种模式。
示例代码:https://code.msdn.microsoft.com/windowsapps/CSNamedPipeServer-4c760c2c
ReadMode 采用 PipeTransmissionMode.Message
示例代码中可看到,这种模式下管道只能 read,write 字节数组,而我想传一个自定义类型的对象。
这里就需要用到C#的序列化和类型转换。
string >> bytes: System.Text.Encoding.UTF8.GetBytes(myString)
bytes >> stream: var stream = new MemoryStream(bs)
stream >> object: var formatter = new BinaryFormatter(); myObj = formatter.Deserialize(stream);
object >> stream: var formatter = new BinaryFormatter(); formatter.Serialize(stream, myObj);
stream >> bytes: stream.ToArray();
bytes >> string: System.Text.Encoding.UTF8.GetString(bs)
按照上面的套路,发送时:object >> stream >> bytes 接收时:bytes >> stream >> object
还有一点需要注意,就是两个进程传递一个对象,该对象的类不能分别在两个Project中定义(虽然同名但不会被识别为同一个类)。这里我将MyClass定义在一个单独的 dll 中。两个Project都需要引用这个dll 。简单的数据也可以封装为Dictionary<string,string> 或 List < Dictionary <string,string>> 这种形式,这样就不用去单独搞一个dll两边加了。
OK 基本的通信方式确定了,接下来需要处理两个进程通信时的多线程问题。