實現不同程式之間的通訊

一隻獨行的猿發表於2019-08-01

  程式之間的通訊是為了解決不同程式之間的資料傳輸問題,這樣可以讓不同程式互動資料。實現程式通訊的方式: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繫結,它可以在地層使用命名管道實現程式通訊。

 

 

相關文章