DOBON.NET DOBON.NETプログラミング掲示板過去ログ

双方向プロセス間通信の実現方法

環境/言語:[.NET Framework 2.0]
分類:[.NET]

数日前に「C#→VBへの移植でうまくいきません 」(No.28890)というタイトルで
質問し、適切な回答を頂いた者です。次のステップでやり方が分からなくて
再度質問させて頂きます。

先のコードでクライアント側から共通クラスの変数を変えて、サーバー側はそれを
イベントで知ることが出来ます。そこで今度はサーバー側で値を設定し、クライアント
側にイベントで知らせるというのをやりたいのですが、やり方が分かりません。
具体的にはクライアント側でサーバーに値を渡し、サーバー側でそれを使ってある計算を
して結果をクライアントに返すという使い方を想定してます。
(共通クラスにPublic変数設けて、サーバーで値を入れたのをクライアントで見る
というのはできることを確認済みです。サーバー側の処理時間がちょっと長いので
イベントで計算終了知らせられたら便利なのです。)

クライアント側にもサーバー側と同じようにAddHandlerでイベントを追加して
サーバー側ではm_msg.DataTrance()を呼び出すというのを試しましたが、
クライアントのAddHandlerで
「セキュリティ レベルでは逆シリアル化することが許可されていません」
というエラーが出てうまく行きません。

以上、よろしくお願いします。
できるかどうかは試していません。

・http://msdn.microsoft.com/ja-jp/library/5dxse167.aspx Low から Full にすることで変わるかどうか。
・イベントハンドラは MarshalByRef から派生したクラスに存在しているか?
Azuleanさん、今回もお世話になります。

> ・http://msdn.microsoft.com/ja-jp/library/5dxse167.aspx Low から Full に
このページを見てプログラムによる逆シリアル化レベルの設定を試みていますが、
サンプルはTcpChannel、こちらはIpcServerChannel(サーバー側)という違いのためか
うまくいきません。

' Creating a custom formatter for your TcpChannel sink chain.
        Dim provider As New BinaryServerFormatterSinkProvider()
        provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
        
' Creating the IDictionary to set the port on the channel instance.
        Dim props As IDictionary = New Hashtable()
        props("processtrancetest") = 8085
        ' Pass the properties for the port setting and the server provider in the server chain argument. (Client remains null here.)
        
'IPC Channel を作成
        Dim servChannel As IpcServerChannel = New IpcServerChannel(props, provider, Nothing)
とやると最終行で
「IPC Server Channel をインスタンス化する場合、ポート名を指定する必要があります。」
というエラーが出ます。

.netのRemoting自体の理解不十分なのですが、セキュリティ上問題があるのでリモートオブジェクトについてはクライアント側から
サーバー側にイベントを介してパラメータを送信できるが、逆はデフォルトでは禁止されている。
しかし、セキュリティレベルを変えることでそれも可能となるということでしょうか。


■No28903に返信(Azuleanさんの記事)
> できるかどうかは試していません。
> 
> ・http://msdn.microsoft.com/ja-jp/library/5dxse167.aspx Low から Full にすることで変わるかどうか。
> ・イベントハンドラは MarshalByRef から派生したクラスに存在しているか?
2011/08/18(Thu) 22:17:28 編集(投稿者)

■No28906に返信(ひで46 さんの記事)
> サンプルはTcpChannel、こちらはIpcServerChannel(サーバー側)という違いのためかうまくいきません。

そうお考えなのであれば、出てきたキーワードで再検索してみるとかチャレンジしてみましょうよ。
「IpcServerChannel BinaryServerFormatterSinkProvider」とか。

> props("processtrancetest") = 8085

書き換えるところが違います。
props はキー名を元に値をとることができる HashTable なのですから、”ポート名”は値(代入式の右辺)であるべきであって、サンプルコードで言う 8085 の部分のことです。

どういったキー名を指定すべきかは、以下のようにたどっていけばたどり着けます。

http://msdn.microsoft.com/ja-jp/library/ms147115.aspx
→ http://msdn.microsoft.com/ja-jp/library/kw7c6kwc.aspx
→ http://msdn.microsoft.com/ja-jp/library/bb397826.aspx
→ http://msdn.microsoft.com/ja-jp/library/bb397843.aspx
→ http://msdn.microsoft.com/ja-jp/library/bb397831.aspx

サンプルを適当にいじるのではなく、ヘルプ(MSDN)を読む癖をつけましょう。

# この辺を解決して実現可能かは未確認。
# サーバー側から動くのができるのかどうかが怪しいライン。
Azuleanさん、コメントありがとうございます。
ご指摘のようにMSDN読んだり、検索したりして調べる努力が必要なのは理解しているつもりです。
ただ、この分野について全くの初心者で読んでみるのですが、リモーティングに
ついては意味の分からない用語が多く(例えばシンクやプロバイダー)、なかなか
理解が進みません。
MSDNは概念やクラスの役割を理解していてコーディングの際にパラメータ確認
などで参照するには十分なのですが、チュートリアル的な説明が非常にわかりにくいです。

No.28906の投稿の下部質問とも関連するのですが、リモートオブジェクト
が一方向にしか使えないならもうひとつ別のリモートオブジェクトを作って
サーバーとクライアントの役割を逆にさせたら良いのでは?
とのアイディアを元に実装してみました。

クライアント側フォーム:
 ラジオボタンコントロール3個、テキストボックス1個、ラベル1個とボタンを貼り付け
 デザイナでラジオボタンのタグに1〜3を設定
サーバー側フォーム:
 リストボックス1個とラベル1個貼り付け
各プロジェクトのプロパティでアプリケーションタグの「ルート名前空間」を空欄にする。
各プロジェクトの参照でSystem.Runtime.Remotingを追加する。

コードは以下の通りです。
一応、やりたい事はある程度実現できました。
「ある程度」というのは、クライアント側のコードで
Private Sub m_msgB_OnTrance()の
'Label1.Text = m_msg.CalcR.ToString
という行のコメントを外すとフリーズしてしまいます。
つまり、このプロシージャ内でフォームのコントロールにアクセスできないようです。
(サーバーからの返り値を表示以外に固定文字列表示でもフリーズを確認)
クライアント→サーバーでは同じコードで動いているのに逆では何故フリーズ
するのでしょうか。スレッドが別になるとかの理由でしょうか。回避方法は
あるのでしょうか。
コードだけファイルをアップしました。(ソルーション全体だとファイルサイズ制限オーバーでした)。
添付ファイル: InterProcessCom.zip (3 KB)
再現に手間がかかりそうだとみているため、とりあえず知っていることだけを書いておきます。

・.NET Remoting で呼び出されるメソッドはメインスレッドで実行されるとは限りません。たいていは別スレッドで呼び出されていたはずです。
 フォームなどのコントロールを操作するのであれば、Invoke とか Dispatcher とか使ってください。

・.NET Remoting 自体は現状、新しく使うことは推奨されていないはずです。
 ドキュメントが少なくとも嘆かず、自己責任で使う気持ちでいてください。
 http://msdn.microsoft.com/ja-jp/library/bb397831.aspx などは上部に「レガシー」ときちんと書いていますね。
Azuleanさん、毎度ありがとうございます。

やはり、別スレッドになるのですね。
コントロールへアクセスする部分をBeginInvokeを使って呼び出すように
変えたら、見事うまくいきました(最初はInvokeを使いましたが、こちらはダメでした)。
BeginInvokeとInvokeとの違いは非同期的にデリゲートを実行するか否かです。
クライアントからサーバーへパラメータ送信して結果をクライアントのイベント
ハンドラで受けているという流れの中でどのようにスレッドが生成されているのか
良く分かりません。

> ・.NET Remoting 自体は現状、新しく使うことは推奨されていないはずです。
これもMSDN調べている中で知りました。
Windows Communication Foundation (WCF)を使うべきなんですね。ただ、環境が
.Net Framework2.0なので、使えないのです。WCFはコードを変えずに
コンフィグレーション変えて色々なケース(自PC内やPC間など)に対応できるようで
すね。

Azuleanさんのアドバイスで何とかテストプログラムが動いたので、本番用に
実装しようと思います。
ありがとうございました。

■No28916に返信(Azuleanさんの記事)
> 再現に手間がかかりそうだとみているため、とりあえず知っていることだけを書いておきます。
> 
> ・.NET Remoting で呼び出されるメソッドはメインスレッドで実行されるとは限りません。たいていは別スレッドで呼び出されていたはずです。
>  フォームなどのコントロールを操作するのであれば、Invoke とか Dispatcher とか使ってください。
> 
> ・.NET Remoting 自体は現状、新しく使うことは推奨されていないはずです。
>  ドキュメントが少なくとも嘆かず、自己責任で使う気持ちでいてください。
>  http://msdn.microsoft.com/ja-jp/library/bb397831.aspx などは上部に「レガシー」ときちんと書いていますね。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板