實現不同進程之間的通訊

  • 2019 年 10 月 3 日
  • 筆記

  進程之間的通訊是為了解決不同進程之間的數據傳輸問題,這樣可以讓不同程式交互數據。實現進程通訊的方式:1、剪切板;2、COM;3、記憶體映射文件;4、WCF

1、剪切板Clipboard在進程間傳送對象

  剪切板是一個供應用程式使用的公有區域。在.NET中定一個了一個DataFormats類,此類包含一些靜態欄位,定義了剪切板中可以存放的數據類型。使用Clipboard類可以向剪切板中放入數據。

  如將文字放入剪切板,使用方法SetDataObject即可:Clipboard.SetDataObject(“剪切板文字2”); 在讀取的時候,先判斷剪切板中是否有文字,然後再讀取:
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');

  在同一個進程中,可以針對同一個記憶體映射文件創建多個“記憶體映射視圖訪問對象”,從而允許同時修改同一個文件的不同部分,在關閉這些對象時,由作業系統保證將所有修改都寫回原始文件。

  MemoryMappedViewAccessor 的Write和Read有泛型方法,單類型只能是結構體類型(應用類型在程式運行時,電腦無法知道應該向記憶體映射文件寫入多少位元組數據,引用類型的對象位於託管堆中,其大小需要經過計算,但非常耗時(而且對象可能引用了其他對象),音效記憶體映射文件的效率)。
  可以使用序列化方式,將引用對象數據進行序列化後,寫入記憶體映射文件中。
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)

  1. 匿名管道:只允許單向通訊,由於沒有名字,因此要通訊的兩個進程應該是父子關係,父進程在創建子進程時,負責將代表匿名管道的句柄傳送給子進程,子進程可以通過該句柄獲取父進程傳輸的數據。其優點是佔用資源少、效率高;缺點是通訊進程必須為父子關係,限制了使用場景。
  2. 命名管道:這種類型的管道擁有一個在本機唯一的名字,可以用於在一個服務進程和多個客戶進程之間進行單/雙向通訊。命名管道是基於消息的通訊模式,即一個進程一次可以向另一方進程連續發生多個消息(消息之間通過消息的定界符進行劃分),接收方通過定界符提取完整的消息。

  在命名空間System.IO.Pipes中,提供了一些用於實現基於管道的進程間通訊,如AnonymousPipeClientStream和AnonymousPipeServerStream可用於實現匿名管道,而NamedPipeClientStream和NamedPipeServerStream可以實現命名管道。但相對於WCF,其比較繁瑣,WCF的管道進程通訊更加簡便和靈活。

  WCF應用程式使用命名管道實現進程通訊:WCF提供了一個NetNamedPipeBinding綁定,它可以在地層使用命名管道實現進程通訊。