C#でプロセス間通信(IPC通信)

C#ではアプリ間でデータの受け渡しがができるプロセス間通信という仕組みがあります。

 

プロセス間通信は以下のようなクラスを使って実現する事が出来ます。

 System.Runtime.Remoting.Channels.Ipc.IpcServerChannel クラス

 System.Runtime.Remoting.Channels.Ipc.IpcClientChannel クラス

 System.Runtime.Remoting.Channels.ChannelServices クラス

 System.Runtime.Remoting.RemotingServices クラス

 System.MarshalByRefObject クラス

 

System.Runtime.Remotingの参照を追加

必要なクラスを使えるようにするためフレームワークの参照の追加を行います。

メニューから「プロジェクト(P)」→「参照の追加(R)...」で参照マネージャーを開き、

「System.Runtime.Remoting」を追加しましょう。

共有するデータ構造を作成

まずは、プロセス間で受け渡しを行うデータ構造を作成します。

 

このデータ構造は MarshalByRefObject クラスを継承して作成します。

    public class IpcTestObject : MarshalByRefObject
    {
        public string TestField;


        public void SetTextField(string text)
        {
            TestField = text;
        }
    }

このクラスはサーバー側とクライアント側の両方で利用します。

サーバーとクライアントがそれぞれこのクラスのフィールドやメソッドにアクセスする事でデータを共有する事ができます。

サーバー側

サーバー側は、サーバーチャネルの作成チャネルを登録共有するオブジェクトを公開という流れになります。

    public partial class MainWindow : Window
    {
        private IpcServerChannel Channel;
        private IpcTestObject    TestObj;


        public MainWindow()
        {
            InitializeComponent();

            TestObj = new IpcTestObject();
            TestObj.TestField = "TEST!";

            // "IpcTest"という名前でIPCチャネルを作成
            Channel = new IpcServerChannel("IpcTest");
            
            // 作成したチャネルを登録
            ChannelServices.RegisterChannel(Channel, true);
            
            // "TestObj"という名前でオブジェクトを公開
            RemotingServices.Marshal(TestObj, "TestObj", typeof(IpcTestObject));
        }
    }

クライアント側

クライアント側は、クライアントチャネルの作成チャネルを登録共有されたオブジェクトを取得という流れになります。

 

    public partial class MainWindow : Window
    {
        private IpcClientChannel Channel;
        private IpcTestObject    TestObj;


        public MainWindow()
        {
            InitializeComponent();

            // IPCチャネルを作成
            Channel = new IpcClientChannel();

            // 作成したチャネルを登録
            ChannelServices.RegisterChannel(Channel, true);
            
            // サーバーが公開した名前を使ってオブジェクトを取得
            var url = "ipc://IpcTest/TestObj";
            TestObj = (IpcTestObject)Activator.GetObject(typeof(IpcTestObject), url);
        }
    }

しばらくアクセスしないと接続が切れる

プロセス間通信はしばらく通信が行われないと接続が切断されて以下のような例外が発生します。

 

System.Runtime.Remoting.RemotingException はハンドルされませんでした。

オブジェクト '/TestObj' が切断されたか、サーバーに存在しません。

 

 

このような時は、以下のように共有するオブジェクトのInitializeLifetimeService()メソッドをオーバライドして null を返すようにする事で回避できます。

    public class IpcTestObject : MarshalByRefObject
    {
        public string TestField;


        public void SetTextField(string text)
        {
            TestField = text;
        }

        public override object InitializeLifetimeService()
        {
            return null;
        }
    }