質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
WCF

WCFは、.NET Frameworkの提供する機能の一つ。サービス指向アプリケーション構築のためのフレームワークです。ネットワークを通して、異なるコンピュータ上で動くソフトウェア間の通信が可能になります。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

6803閲覧

WCFによるプロセス間のイベント通知

kaito_19950605

総合スコア4

WCF

WCFは、.NET Frameworkの提供する機能の一つ。サービス指向アプリケーション構築のためのフレームワークです。ネットワークを通して、異なるコンピュータ上で動くソフトウェア間の通信が可能になります。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2019/08/29 13:05

編集2019/09/18 22:03

前提・実現したいこと

WCFを使用して、名前付きパイプによるプロセス間通信を行うプログラムを実装しています。

プロセス間でイベント通知を行い、データを転送したいのですが、双方向通信にしてもイベントが飛びません。
WCFを利用したプロセス間のイベント通知によるデータ転送の実現方法を教えて頂きたいです。
やりたいことのイメージとして、以下の画像を参考にして下さい。
イメージ説明

ClientとServiceの双方向通信のサンプルはネット上にたくさん転がっていますが、
あるClientがイベントを叩くと、もう一方のClientにイベント通知がされる、
というパターンが見つかりませんでした。

参考としてサイトをご紹介頂ける場合は、英語のサイトでも構いません。

発生している問題・エラーメッセージ

WCFによるプロセス間のイベント通知が出来ない。

下記のソースコードのようにすると、以下のようになってしまいます。

  1. 1つ目のプロセスでイベント登録(OnCallback)。
  2. 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

x_x👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2019/08/29 13:19 編集

そのイベントが飛ばないというコードを提示しましょう 質問は編集できます
kaito_19950605

2019/08/29 13:20

申し訳ございませんが、訳あってソースは載せられません。 そのため、やり方を教えて頂ければと思います。
m.ts10806

2019/08/30 00:25 編集

https://teratail.com/questions/209024 質問は編集できます。新たに質問を立てる必要はありません。 どちらか削除依頼を出しておいてください。 なお、コードも実際に起きている問題や状況(エラーなど)がないのに回答しろというのは無理な内容です。 可能性のある要因も調べれば幾らでも出てくるのではないでしょうか。 (世界で初めてあなただけがぶちあたった問題だろうか?) あとはドキュメント読んで自分でがんばってください。
kaito_19950605

2019/08/30 01:17

失礼致しました。 サンプルだけでも頂けたらと思いまして…。 本日か明日の夜にソースを載せます。
MagoCat

2019/09/02 14:44

サービスとクライアントの間でイベント通知のような仕組みを実現したいと思ったらそれはコールバックで実現するというのがWCFの流儀です。そしてコールバックを用いたイベント通知のような仕組みの実現方法については「WCF 双方向」などと検索するとサンプルコードの載ったブログなどに簡単にたどり着くと思うので貴方もそれらを目にしているはずですが、それらは貴方のやりたいことの実現には役に立たなかったのでしょうか? web上に転がっているサンプルコードと貴方の実現したい何かの間に決定的な違いが存在すると感じるのであれば、その違いの正体について重点的に説明するのが有用な回答を集めるために重要だと思います。
kaito_19950605

2019/09/02 21:09

サンプルを見ながら実装したのですが、イベント通知が出来なかったため、このように質問させて頂きました。 決定的な違いは特に感じていません。
Zuishin

2019/09/18 12:16

サンプルをそのまま実装して、動くことを確かめてからにしてみては?
kaito_19950605

2019/09/18 12:28

参考にしていたサンプルはあくまで、ClientとService間のイベント通知でした。そのため、今回の件にはそのまま使えないので試しても意味ないかと…。
Zuishin

2019/09/18 12:32

意味があるか無いかやってみなくちゃわからないでしょう。 そこから失敗してるかもしれません。 一度にやろうとして無理なら積み重ねてください。 クライアントとサーバーの一対一通信ができることがわかったなら、クライアントからのメッセージをトリガーにサーバーがクライアントにメッセージを飛ばすのは簡単にできるはずです。
y_waiwai

2019/09/18 13:59

努力する気がないならそこまでですねーw
kaito_19950605

2019/09/18 21:27

もう1ヶ月以上毎日いじっていますが、それでも解決出来ないからこのように質問しているのです。
Zuishin

2019/09/18 21:54

あなたのやり方では解決できなかったから別の方法を提案しているのです。
Zuishin

2019/09/18 21:57

まずクライアントとサーバーが接続できているという情報がどこにもありません。だから、そこから確立させるのは当然のことです。
kaito_19950605

2019/09/18 22:01

Zuishinさん 失礼しました。上のコメントはy_waiwaiさんに対してです。 ちなみにclientとserverは接続出来ています。
Zuishin

2019/09/18 22:03 編集

接続できているなら、クライアントからサーバーにメッセージを送り、それをトリガーにしてサーバーがクライアントにメッセージを送ることもできるでしょう。
Zuishin

2019/09/18 22:02

メッセージの送受信ができるかどうかをまず確かめてください。
kaito_19950605

2019/09/18 22:07

そう思うのですが、「発生している問題・エラーメッセージ」に追記した通り、イベントハンドラーがnullになってしまっていて、通知出来ないんです。
Zuishin

2019/09/18 22:08

接続できていることはどうやって確かめましたか?
kaito_19950605

2019/09/18 22:11

プロセス1でサービスのメソッドを叩く、プロセス2でサービスのメソッドを叩く、とするとレスポンスが得られたからです。
Zuishin

2019/09/18 22:13

では次にプロセス 1 のオブジェクトをサーバーに正しく送信できるかどうか確かめてください。
kaito_19950605

2019/09/18 22:24

メソッドを叩く時にそれぞれのプロセスから引数を渡していますが、それが受け取れているので大丈夫です。
Zuishin

2019/09/18 22:33

ではその渡したオブジェクトのメソッドを呼び出した時、クライアントのメソッドが呼び出されるようにしてください。
kaito_19950605

2019/09/18 22:39

すみません、そのやり方は分からないです。
kaito_19950605

2019/09/18 22:47

このサイトは既に目を通しています。コールバックインターフェース使え、ということですよね?私のソースコードでも使用していますが、今回のパターンだと上手くいきませんでした。
Zuishin

2019/09/18 22:54

今回のパターンの前に積み重ねが必要ということです。 一番最初に戻りますが、このサイトの通りに実装し、まずあなたができないと言った「サーバーに渡したオブジェクトのメソッドを呼び出すことでクライアントのメソッドが呼び出されること」を実装してください。
Zuishin

2019/09/18 22:59 編集

このサイトでは、クライアントが自身のコールバックオブジェクトをサーバーに渡し、サーバーがそのオブジェクトのコールバックメソッドを呼び出しています。 ここから少し変えれば、クライアント 2 がコールバックオブジェクトをサーバーに渡し、クライアント 1 がサーバーにメッセージを送信すると、サーバーがクライアント 2 のコールバックメソッドを呼び出すことができるということになります。 ここを目標にしています。 違いますか?
guest

回答1

0

自己解決

1ヶ月も諦めずに色々試してよかったです。ようやく自己解決出来ました。
以下のように修正しました。

DataTransferService.cs
・callback(IGetResponseCallback型)をメンバ変数として宣言しておく。
・片方のプロセスでのみ、OperationContext.Current.GetCallbackChannelを行う。
→ これがコールバックオブジェクトの実体のようで、上記対応をすることでコールバックオブジェクトが共有され、イベント通知が作用しました。
質問で提示したソースコードでは、両方のプロセスでコールバックオブジェクトを設定していたため、2つ目のプロセスで設定したコールバックオブジェクトに上書きされ、1つ目のプロセスにイベント通知がされなかったようです。

皆様お騒がせしました。

投稿2019/09/19 12:59

kaito_19950605

総合スコア4

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問