進程之間的通信是為了解決不同進程之間的數據傳輸問題,這樣可以讓不同程序交互數據。實現進程通信的方式:1、剪切板;2、COM;3、內存映射文件;4、WCF
1、剪切板Clipboard在進程間傳送對象
剪切板是一個供應用程序使用的公有區域。在.NET中定一個了一個DataFormats類,此類包含一些靜態字段,定義了剪切板中可以存放的數據類型。使用Clipboard類可以向剪切板中放入數據。
IDataObject data = Clipboard.GetDataObject(); if (data.GetDataPresent(DataFormats.Text)) { label1.Text = data.GetData(DataFormats.Text).ToString(); }
將自定義的數據放置到剪切板,自定義一個圖片類,並標記為可序列化(此處使用的命名空間是:TestClipboard)。將自定義數據類型對象放置到剪切板的關鍵是DataObject類,它實現了IDataObject接口。它就像一個容器,存放將被放置在剪切板上的數據。
[Serializable] public class MyPic { /// <summary> /// 圖片 /// </summary> public Image Img; /// <summary> /// 圖片信息 /// </summary> public string ImgInfo; } public void SetMyPicToClipboard() { MyPic obj = new MyPic(); obj.Img = Properties.Resources.Image; obj.ImgInfo = "測試將自定義類型保存至剪切板"; //創建數據對象,並將數據裝入 IDataObject dataObj = new DataObject(obj); //其他類型也可以放置在同一數據對象中 /* dataObj.SetData(DataFormats.UnicodeText, "測試文字"); dataObj.SetData(DataFormats.Bitmap, Properties.Resources.Image); */ //復制到剪切板,第二個參數表示程序退出時不清空 Clipboard.SetDataObject(dataObj, true); }
但是,使用Clipboard.SetDataObject方法將一個DataObject對象放到剪切板后,外界訪問時,需要指定對象的完整類型名稱。如果某種數據類型只能在指定的進程中訪問,則可以使用該方式,指定命名空間。
//首先判斷剪切板上是否有我的數據:需要完全限定命名空間類型 if (Clipboard.ContainsData("WindowsFormsApplication1.MyPic")) { IDataObject dataObj = Clipboard.GetDataObject();//讀取數據 MyPic myPic = dataObj.GetData("WindowsFormsApplication1.MyPic") as MyPic;//轉換數據 pictureBox1.Image = myPic.Img; textBox1.Text = myPic.ImgInfo; }
2、使用FileSystemWatcher實現進程同步
/// <summary> /// 實現寫入數據 /// </summary> /// <param name="fileName"></param> public void SetText(string fileName) { using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read)) { using (StreamWriter writer = new StreamWriter(fs, Encoding.UTF8)) { writer.Write("內容"); } } } /// <summary> /// 實現讀取數據 /// </summary> /// <param name="fileName"></param> public void ReadText(string fileName) { using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (StreamReader reader = new StreamReader(fs, Encoding.UTF8)) { string txt = reader.ReadToEnd(); } } }
然后,使用FileSystemWatcher組件的Changed事件監控文件是否發生改變。在網絡應用程序中,可以使用此組件監控特定的專用於上傳文件的文件夾,當發現用戶上傳文件之后,系統可以自動啟動一系列的處理流程。
3、使用內存映射文件(Memory Mapped File)實現進程通信
含義:在內存中開辟一塊存放數據的專用區域,這區域與硬盤上特定的文件相對應。進程將這塊內存區域映射到自己的地址空間中,完成像訪問普通內存一樣訪問它。windows中的系統分頁文件和休眠文件就是如此實現的。需要引用命名空間System.IO.MemoryMappedFiles。
MemoryMappedFile對象表示一個內存映射文件,通過它的CreateFromFile方法根據磁盤現有文件創建內存映射文件(注意,使用完后要立即釋放資源,實際上它對應的是操作系統的核心對象)。其中,內存映射的容量在未指定時,默認與文件大小相等。在指定大小時,它的值不能小於文件的現有大小。若指定的大小大於磁盤文件大小,磁盤文件會自動增長到內存映射文件聲明的容量大小。
創建MemoryMappedFile對象后,不能直接對其進行讀寫,必須使用MemoryMappedViewAccessor對象(內存映射視圖訪問對象)操作 。可以用MemoryMappedFile對象的方法創建一個訪問對象。其中,可以指定需要訪問文件的范圍,從第幾個字節到第幾個字節。在寫入參數時,也需要指明想哪個位置寫入什么。同時也可以使用MemoryMappedViewAccessor的Read方法讀取數據。
MemoryMappedFile memoryFile = MemoryMappedFile.CreateFromFile("Text.Config", FileMode.OpenOrCreate, "Config", 1400);//kb; MemoryMappedViewAccessor accessor = memoryFile.CreateViewAccessor(0, 1024); accessor.Write(0, '2');
在同一個進程中,可以針對同一個內存映射文件創建多個“內存映射視圖訪問對象”,從而允許同時修改同一個文件的不同部分,在關閉這些對象時,由操作系統保證將所有修改都寫回原始文件。
MemoryMappedFile memoryFile = MemoryMappedFile.CreateFromFile("Text.Config", FileMode.OpenOrCreate, "Config", 1400);//kb; MemoryMappedViewStream stream = memoryFile.CreateViewStream(); MyPic obj = new MyPic(); stream.Seek(0, SeekOrigin.Begin); new BinaryFormatter().Serialize(stream, obj);
4、使用WCF通過管道實現進程通信
“管道(Pipe)”是Windows所提供的一種進程間通信機制,用於在兩個進程間相互傳送數據。Windows提供了兩種類型管道:匿名管道(Anonymous Pipe)、命名管道(Named Pipe)
- 匿名管道:只允許單向通信,由於沒有名字,因此要通信的兩個進程應該是父子關系,父進程在創建子進程時,負責將代表匿名管道的句柄傳送給子進程,子進程可以通過該句柄獲取父進程傳輸的數據。其優點是占用資源少、效率高;缺點是通信進程必須為父子關系,限制了使用場景。
- 命名管道:這種類型的管道擁有一個在本機唯一的名字,可以用於在一個服務進程和多個客戶進程之間進行單/雙向通信。命名管道是基於消息的通信模式,即一個進程一次可以向另一方進程連續發生多個消息(消息之間通過消息的定界符進行划分),接收方通過定界符提取完整的消息。
在命名空間System.IO.Pipes中,提供了一些用於實現基於管道的進程間通信,如AnonymousPipeClientStream和AnonymousPipeServerStream可用於實現匿名管道,而NamedPipeClientStream和NamedPipeServerStream可以實現命名管道。但相對於WCF,其比較繁瑣,WCF的管道進程通信更加簡便和靈活。
WCF應用程序使用命名管道實現進程通信:WCF提供了一個NetNamedPipeBinding綁定,它可以在地層使用命名管道實現進程通信。