C# 程序間通訊(一)
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 有什麼區別,應該用哪個?
Ananonymous pipe is an unnamed,one-way pipe that typically transfers data betweena parent process and a child process . Anonymous pipes arealways local ; they cannot be used for communication over a network.
關鍵詞:one-way pipe,parent and child, always local
Anamed pipe is a named, one-way orduplex 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 enablesmultiple 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 computersacross 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 。
OK 基本的通訊方式確定了,接下來需要處理兩個程序通訊時的多執行緒問題。