- PR -

.NETでマーシャリングされた名前付きオブジェクト

投稿者投稿内容
Dr. K
常連さん
会議室デビュー日: 2003/04/26
投稿数: 25
投稿日時: 2003-06-03 19:35
いつもお世話になっております。
ちょっと題名に無理がありますが・・・

この度は、リモートオブジェクト&名前付きオブジェクトにちなんだ質問です。
例えば、あるDLL内にリモートアクセス可能なクラスSharedClass(MarshalByRefObjectから派生)が存在するとします。また、同じDLL内にもう1つManagerClassというクラスも存在するとします。
ManagerClassにはGetSharedObject()という感じのメソッドが用意されていて、他のAppDomainから何回呼ばれても同じものを参照するオブジェクトを返します。
実現したいのはまさにこのメソッドです。

イメージ的には

EXE1:
ManagerClass mgr = new ManagerClass();
SharedClass sharedObj1 = mgr.GetSharedObject();

EXE2:
ManagerClass mgr = new ManagerClass();
SharedClass sharedObj2 = mgr.GetSharedObject();

この段階でsharedObj1とsharedObj2は同じものを指す。
自分が考えてみたのは、GetSharedObject()内で名前付きオブジェクトみたいなものを取得できないかということです。これができれば、一番最初に呼ばれたときだけオブジェクトがメモリに生成されて、2回目以降はそのオブジェクトが取得されるという感じです。
これ以外でも、皆さんの中で上記のGetSharedObject()を実現する方法を考え付く方がいらしたら是非ご指導お願いいたします。
NothingButXMLInfoSet
ベテラン
会議室デビュー日: 2003/03/31
投稿数: 65
投稿日時: 2003-06-03 22:02
#設計上、このようにするとスケーラビリティの維持が困難であろうと思いますが、ここではその問題はあえて無視します。

・ManagerClassをWellKnownObjectMode.Singletonで公開されているMarshalByRefObjectにします。
・GetSharedObject()メソッドでは、単一オブジェクトを返す実装を行います。

コード:
using System;

using System.Runtime.Remoting;

public class ManagerClass : MarshalByRefObject {
private SharedClass _singleton = null;
public ObjRef GetSharedObject() {
if (_singleton == null)
_singleton = new SharedClass();
return RemotingServices.Marshal(_singleton, "SharedObj");
}
}

public class SharedClass : MarshalByRefObject {
internal SharedClass() {}
private int _state = 0;
public int State {
get { return _state; }
set { _state = value; }
}
}
// csc /t:library rem.cs



コード:
using System;

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

class Server {
static void Main() {
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(ManagerClass), "ManagerObj",
WellKnownObjectMode.Singleton);
ChannelServices.RegisterChannel(new HttpChannel(82));
Console.ReadLine();
}
}
// csc /r:rem.dll server.cs



[code]
using System.Runtime.Remoting;
using System;
using System.Windows.Forms;
class App : Form {
static void Main() {
RemotingConfiguration.RegisterWellKnownClientType(
typeof(ManagerClass), "http://localhost:82/ManagerObj");
Application.Run(new App());
}

TextBox t1 = new TextBox();
App() {
Button b1 = new Button();
b1.Click += new EventHandler(b1_c);
b1.Left = t1.Width;
b1.Text = "SetData";

Button b2 = new Button();
b2.Click += new EventHandler(b2_c);
b2.Left = b1.Width + t1.Width;
b2.Text = "GetData";

Controls.AddRange(new Control[]{t1, b1, b2});
}
void b1_c(object s, EventArgs e) {
ManagerClass mc = new ManagerClass();
SharedClass sc = (SharedClass)
RemotingServices.Unmarshal(mc.GetSharedObject());
sc.State = Convert.ToInt32(t1.Text);
}
void b2_c(object s, EventArgs e) {
ManagerClass mc = new ManagerClass();
SharedClass sc = (SharedClass)
RemotingServices.Unmarshal(mc.GetSharedObject());
t1.Text = sc.State.ToString();
}
}
// csc /t:winexe /r:rem.dll app.cs

これで、Server.exeを起動した状態でApp.exeを複数起動し、お互いに同一オブジェクトを参照していることが確認できます。

【コードが長すぎるのを編集しました】

[ メッセージ編集済み 編集者: NothingButXMLInfoSet 編集日時 2003-06-04 03:35 ]
Dr. K
常連さん
会議室デビュー日: 2003/04/26
投稿数: 25
投稿日時: 2003-06-04 00:04
NothingButXMLInfoSetさん。ご返答ありがとうございました。

NothingButXMLInfoSetさんのサンプルは確かに僕の環境でも動作しています。
ただ、僕の説明不足もあったのですが、この設計だと以下の点で今回僕が求めていた形を満たせていません。

・Server.exeが存在するのは望ましくない。登場するのはサービスDLLとクライアントEXEという形を望んでおります。

・クライアント側でRegisterWellKnownClientTypeを呼び出さなくてはならない。クライアントではサーバーに用意されてるメソッドのみを呼べばいい構成にしたいところです。(サーバーのこのメソッドは呼ばなければならないみたいな規則はあってもいいのですが)

このサンプルを参考に、MSDNなどを参照しながらいくつか改良してみたのですが、やはりこの形式だと後者の点で無理になってしまうと思います。さらに、RegisterWellKnownClientTypeより先にRegisterWellKnownServiceTypeを呼び出さないといけないので、Server.exeがいなくなるのも問題になってしまいます。

.NETではWin32APIに用意されているような名前付きオブジェクトは存在しないのでしょうか?もしくはそれに変わるような何か・・・
NothingButXMLInfoSet
ベテラン
会議室デビュー日: 2003/03/31
投稿数: 65
投稿日時: 2003-06-04 03:31
すみません。MarshalByRefObjectと書かれていたので、Remotingが前提だと思い込んでました。で、そうなると意味がわからないのですが、

>・Server.exeが存在するのは望ましくない。登場するのはサービスDLLとクライアントEXEという形を望んでおります。

サービスDLLはどこで動作するんでしょうか?クライアントのEXEを複数起動されるようですから、クライアントのEXEとは別のプロセスに存在するんですよね?DLLのみでプロセスを作ることはできないので、そうなるとどういう構成なのかわからないのですが。というか、Windowsではおっしゃる構成は不可能だと思います。

>・クライアント側でRegisterWellKnownClientTypeを呼び出さなくてはならない。

面倒なので全部コードで書いてしまったのですが、この部分は構成ファイルで代替するのが普通です。ただし、それでも構成ファイルを読み取るコードは書かなければなりませんが。

>Server.exeがいなくなるのも問題になってしまいます。

そこがDr. Kさんのアプリの問題点になるのは間違いないです。それに耐えられる構造にするのは、Singletonの仕組みでは大変なのです。これは.NETの問題ではありません。アプリケーション・アーキテクチャの問題です。

「Win32APIに用意されているような名前付きオブジェクト」ってなんですか(ミューテックスとか)?それを使うとDr. Kさんのおっしゃる構成が実現できていたのでしょうか?何か動いていたコード例とかがあると把握しやすいのですが。
cats
大ベテラン
会議室デビュー日: 2002/11/29
投稿数: 221
お住まい・勤務地: 東京
投稿日時: 2003-06-04 07:28
#pragma comment(linker, "/SECTION:.shared,RWS")
#pragma data_seg(".shared")
int nSharedInt = 0;
#pragma data_seg()

こんなコードでDLLでデータ共有ができます。
はずしている?
Dr. K
常連さん
会議室デビュー日: 2003/04/26
投稿数: 25
投稿日時: 2003-06-04 11:40
またまたご返答ありがとうございます&返信遅れてすいません。

引用:

MarshalByRefObjectと書かれていたので、Remotingが前提だと思い込んでました。


この場合、AppDomainの境界は越えた通信になるのでRemotingということになるのではないでしょうか?

引用:

サービスDLLはどこで動作するんでしょうか?クライアントのEXEを複数起動されるようですから、クライアントのEXEとは別のプロセスに存在するんですよね?DLLのみでプロセスを作ることはできないので、そうなるとどういう構成なのかわからないのですが。というか、Windowsではおっしゃる構成は不可能だと思います。


すみません。これもものすごく説明不足ですね。(言い訳すると最初にドバっと書くと良くないかなと思ったので)
サーバーEXEがいたら問題というのは、最初にサーバーEXEを起動しなければならないと早とちりしてしまったからです。DLL内のメソッドで、サーバーEXEがいなかったら(つまり最初の呼び出し)、サーバーEXEを起動するようにしようと思います。
Windows上で僕がやろうとしていることと近い構成としては、Microsoft Officeアプリケーションのオブジェクトモデルになると思います。(特にOutlookとMAPIの関係)
つまり、サーバーEXE自体が存在するのは問題ないのですが、サーバー自体でも他のクライアントが参照してるDLL内のオブジェクトを使いたいということになります。(これができれば解決?)そこで、サーバー側でRegisterWellKnownServerTypeを呼び出した後に、RegisterWellKnownClientTypeやActivator.GetObject()などを呼んでみたのですが、例外が発生してしまいました。これは間違いなんでしょうか。

引用:

クライアント側でRegisterWellKnownClientTypeを呼び出さなくてはならない。


これについては、このメソッド呼ぶコードをDLLのメソッド内に書いて、それをクライアントに呼んでもらえば解決できそうな気もしてます。

引用:

「Win32APIに用意されているような名前付きオブジェクト」ってなんですか(ミューテックスとか)?


これは抽象的な意味で使ってしまいました。もちろんMutexなどカーネルオブジェクトのことです。ただ、これ自体はNothingButXMLInfoSetが教えてくださったRemotingの方法でいいと思うのです。サーバー自体がクライアントになることさえできれば。理論的にできると思うのですが、僕の知識不足でちょっとハマってしまっています。

今まとめてみたのですが、現在の問題点は単にサーバーEXE自体がクライアントになることができないということかもしれません。これってできる感じがするのですが、上記のように例外が投げられてしまっています。
夜にならないと時間が取れないのですが、またがんばってみようと思います。よろしければ、助言をいただけると幸いです。

*僕はまだ学生でみなさんに比べると経験が乏しいです。なので、時々あいまいな表現などを使ってしまうことがあるのですが、大目にみてください
がんばって勉強します。
Dr. K
常連さん
会議室デビュー日: 2003/04/26
投稿数: 25
投稿日時: 2003-06-04 11:44
catsさん。ご返答ありがとうございます。

引用:

#pragma comment(linker, "/SECTION:.shared,RWS")
#pragma data_seg(".shared")
int nSharedInt = 0;
#pragma data_seg()

こんなコードでDLLでデータ共有ができます。
はずしている?


おお。これは古き良きC++時代にも使えた技ですね。これって.NET(C#)でも可能なんでしょうか。(今コーディングできる状況にいないので確認とれませんが)
このnSharedIntをマーシャリング可能な(ん?この場合マーシャリングは必要ないか)、目的のクラスインスタンスにすれば実現できそうな感じもしますね。
今夜試してみようと思います。
cats
大ベテラン
会議室デビュー日: 2002/11/29
投稿数: 221
お住まい・勤務地: 東京
投稿日時: 2003-06-04 12:08
すいません。
「DLLでデータ共有」は、C++の話で、C#ではないです。
(たぶん、C#では無理っぽい気が。。。)

スキルアップ/キャリアアップ(JOB@IT)