前提・実現したいこと
WCFを使用して、名前付きパイプによるプロセス間通信を行うプログラムを実装しています。
プロセス間でイベント通知を行い、データを転送したいのですが、双方向通信にしてもイベントが飛びません。
WCFを利用したプロセス間のイベント通知によるデータ転送の実現方法を教えて頂きたいです。
やりたいことのイメージとして、以下の画像を参考にして下さい。
ClientとServiceの双方向通信のサンプルはネット上にたくさん転がっていますが、
あるClientがイベントを叩くと、もう一方のClientにイベント通知がされる、
というパターンが見つかりませんでした。
参考としてサイトをご紹介頂ける場合は、英語のサイトでも構いません。
発生している問題・エラーメッセージ
WCFによるプロセス間のイベント通知が出来ない。
下記のソースコードのようにすると、以下のようになってしまいます。
- 1つ目のプロセスでイベント登録(OnCallback)。
- 2つ目のプロセスでInvokeしようとすると、OnCallbackがnullになっていてInvokeされない。
該当のソースコード
サービスの定義(IdataTransferService.cs)
C#
1namespace DataTransferWCFService 2{ 3 [ServiceContract(CallbackContract = typeof(IGetResponseCallback))] 4 public interface IDataTransferService 5 { 6 [OperationContract(IsOneWay = true)] 7 void Register(byte[] data); 8 9 [OperationContract(IsOneWay = true)] 10 void Clear(); 11 12 [OperationContract] 13 byte[] GetResponse(UInt32 id, byte[] request); 14 15 /// <summary> 16 /// コールバック用 17 /// </summary> 18 /// <param name="id"></param> 19 /// <param name="message"></param> 20 [OperationContract(IsOneWay = true)] 21 void Send(UInt32 id, byte[] message); 22 } 23}
コールバックインターフェースの定義(IGetResponseCallback.cs)
C#
1namespace DataTransferWCFService 2{ 3 public interface IGetResponseCallback 4 { 5 [OperationContract(IsOneWay = true)] 6 void GetResponseCallback(UInt32 id, byte[] message); 7 } 8}
サービスの実装(DataTransferService.cs)
C#
1namespace DataTransferWCFService 2{ 3 public class DataTransferService : IDataTransferService 4 { 5 private static List<byte[]> dataList = new List<byte[]>(); 6 7 public void Clear() 8 { 9 dataList.Clear(); 10 } 11 12 public byte[] GetResponse(uint id, byte[] request) 13 { 14 15 byte[] response = dataList.ElementAt((int)id) 16 .Concat(request) 17 .ToArray(); 18 19 return response; 20 } 21 22 public void Register(byte[] data) 23 { 24 dataList.Add(data); 25 } 26 27 /// <summary> 28 /// コールバック用 29 /// </summary> 30 /// <param name="id"></param> 31 /// <param name="request"></param> 32 public void Send(uint id, byte[] message) 33 { 34 IGetResponseCallback callback = OperationContext.Current.GetCallbackChannel<IGetResponseCallback>(); 35 36 callback.GetResponseCallback(id, message); 37 } 38 } 39}
サービスの開始周り(DataTransfer.cs)
C#
1namespace DataTransfer 2{ 3 public class DataTransfer 4 { 5 public _DataTransfer instance = _DataTransfer.Instance; 6 7 /// <summary> 8 /// サービスの開始とチャンネルの実体化 9 /// </summary> 10 public void PreStart() 11 { 12 this.instance.Start(); 13 this.instance.Create(); 14 } 15 16 /// <summary> 17 /// チャンネルの実体化 18 /// </summary> 19 public void Create() 20 { 21 this.instance.Create(); 22 } 23 24 public void Register(byte[] data) 25 { 26 this.instance.Register(data); 27 } 28 29 public void Clear() 30 { 31 this.instance.Clear(); 32 } 33 34 public byte[] GetResponse(UInt32 id, byte[] request) 35 { 36 return this.instance.GetResponse(id, request); 37 } 38 } 39 40 public class _DataTransfer : IGetResponseCallback 41 { 42 private static string PIPE_NAME = @"net.pipe://sample"; 43 public ServiceHost host; 44 public IDataTransferService proxy; 45 public DuplexChannelFactory<IDataTransferService> factory; 46 public delegate void CallbackEventHandler(object sender, CommunicationEventArgs args); 47 public event CallbackEventHandler OnCallback; 48 //シングルトンパターン 49 private static _DataTransfer instance = new _DataTransfer(); 50 public static _DataTransfer Instance 51 { 52 get 53 { 54 return instance; 55 } 56 } 57 58 private _DataTransfer() 59 { 60 61 } 62 63 /// <summary> 64 /// サービスを開始 65 /// </summary> 66 public void Start() 67 { 68 Instance.host = new ServiceHost(typeof(IDataTransferService), new Uri(PIPE_NAME)); 69 70 Instance.host.AddServiceEndpoint(typeof(IDataTransferService), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) 71 { 72 ReceiveTimeout = TimeSpan.MaxValue 73 }, string.Empty); 74 75 try 76 { 77 host.Open(); 78 } 79 catch (Exception ex) 80 { 81 Debug.WriteLine(ex.Message); 82 } 83 } 84 85 /// <summary> 86 /// チャンネルの実体化 87 /// </summary> 88 public void Create() 89 { 90 Instance.factory = new DuplexChannelFactory<IDataTransferService>(new InstanceContext(Instance), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) 91 { 92 MaxReceivedMessageSize = Int32.MaxValue 93 }, PIPE_NAME); 94 Instance.proxy = Instance.factory.CreateChannel(); 95 } 96 97 public void Clear() 98 { 99 try 100 { 101 Instance.proxy.Clear(); 102 } 103 catch(Exception ex) 104 { 105 Debug.WriteLine(ex.Message); 106 } 107 } 108 109 public void Register(byte[] data) 110 { 111 try 112 { 113 Instance.proxy.Register(data); 114 } 115 catch(Exception ex) 116 { 117 Debug.WriteLine(ex.Message); 118 } 119 } 120 121 public byte[] GetResponse(UInt32 id, byte[] request) 122 { 123 try 124 { 125 Instance.proxy.Send(id, request); 126 127 byte[] response = Instance.proxy.GetResponse(id, request); 128 129 Instance.proxy.Send(id, response); 130 131 return response; 132 } 133 catch(Exception ex) 134 { 135 Debug.WriteLine(ex.Message); 136 137 return null; 138 } 139 } 140 141 /// <summary> 142 /// コールバックメソッドの実装 143 /// </summary> 144 /// <param name="id"></param> 145 /// <param name="message"></param> 146 public void GetResponseCallback(uint id, byte[] message) 147 { 148 Instance.OnCallback?.Invoke(this, new CommunicationEventArgs(id, message)); 149 } 150 } 151}
本体(Program.cs)
C#
1namespace WCFIPC 2{ 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //1つ目のプロセスでは 8 Transfer transfer = new Transfer(); 9 transfer.Register(new byte[] { 0x12, 0x34, 0x56 }); 10 11 //2つ目のプロセスでは 12 DataTransfer.DataTransfer dataTransfer = new DataTransfer.DataTransfer(); 13 dataTransfer.Create(); 14 var response = dataTransfer.GetResponse(0xFF, new byte[] { 0x78, 0x90 }); 15 } 16 } 17 18 class Transfer 19 { 20 public DataTransfer.DataTransfer service = new DataTransfer.DataTransfer(); 21 22 public void Register(byte[] data) 23 { 24 this.service.PreStart(); 25 this.service.Register(data); 26 27 //ここでイベントの購読をしたい 28 this.service.instance.OnCallback += Instance_OnCallback; 29 } 30 31 private void Instance_OnCallback(object sender, DataTransferWCFService.CommunicationEventArgs args) 32 { 33 Console.Write(args.Message); 34 } 35 36 public void Clear() 37 { 38 this.service.Clear(); 39 } 40 } 41}
試したこと
・上記実装。
・以下のようにイベントを処理するクラスを実装し、DataTransferServiceでそのオブジェクトを保持した。サービスインターフェースにそのオブジェクトを返すようなメソッドを定義し実装したが、期待通りの動作をしてくれなかった。
C#
1[DataContract] 2 public class EventHelper 3 { 4 public delegate void CallbackEventHandler(object sender, CommunicationEventArgs args); 5 [field: DataMember] 6 public event CallbackEventHandler OnCallback; 7 8 [OperationContract] 9 public void Fire(UInt32 id, byte[] message) 10 { 11 this.OnCallback?.Invoke(this, new CommunicationEventArgs(id, message)); 12 } 13 }
補足情報(FW/ツールのバージョンなど)
Visual Studio 2019
.NET Framework 4.7.2
回答1件
あなたの回答
tips
プレビュー