62

MarshalByRef オブジェクトが AppDomain (1) から別の (2) に渡されるとき、2 番目の AppDomain でメソッドを呼び出す前に 6 分間待機すると (2)、 RemotingException が発生します。

System.Runtime.Remoting.RemotingException: オブジェクト [...] が切断されたか、サーバーに存在しません。

この問題に関するいくつかのドキュメント:

間違っている場合は訂正してください。InitializeLifetimeService が null を返す場合、プロキシが収集された場合でも、AppDomain 2 がアンロードされている場合にのみオブジェクトを AppDomain 1 で収集できますか?

ライフタイムを無効にして、プロキシが Finalized になるまでプロキシ (AppDomain 2 内) とオブジェクト (AppDomain1 内) を存続させる方法はありますか? 多分ISponsorと…?

4

10 に答える 10

46

ここで答えを見てください:

http://social.msdn.microsoft.com/Forums/en-US/netfxremoting/thread/3ab17b40-546f-4373-8c08-f0f072d818c9/

基本的に言う:

[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
public override object InitializeLifetimeService()
{
  return null;
}
于 2011-05-24T13:21:02.650 に答える
13

最終的にクライアントでアクティブ化されたインスタンスを実行する方法を見つけましたが、それにはファイナライザーのマネージド コードが含まれます :( クラスを CrossAppDomain 通信用に特化しましたが、変更して他のリモート処理で試すことができます。バグが見つかったらお知らせください。

次の 2 つのクラスは、関連するすべてのアプリケーション ドメインに読み込まれたアセンブリに含まれている必要があります。

  /// <summary>
  /// Stores all relevant information required to generate a proxy in order to communicate with a remote object.
  /// Disconnects the remote object (server) when finalized on local host (client).
  /// </summary>
  [Serializable]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public sealed class CrossAppDomainObjRef : ObjRef
  {
    /// <summary>
    /// Initializes a new instance of the CrossAppDomainObjRef class to
    /// reference a specified CrossAppDomainObject of a specified System.Type.
    /// </summary>
    /// <param name="instance">The object that the new System.Runtime.Remoting.ObjRef instance will reference.</param>
    /// <param name="requestedType"></param>
    public CrossAppDomainObjRef(CrossAppDomainObject instance, Type requestedType)
      : base(instance, requestedType)
    {
      //Proxy created locally (not remoted), the finalizer is meaningless.
      GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Initializes a new instance of the System.Runtime.Remoting.ObjRef class from
    /// serialized data.
    /// </summary>
    /// <param name="info">The object that holds the serialized object data.</param>
    /// <param name="context">The contextual information about the source or destination of the exception.</param>
    private CrossAppDomainObjRef(SerializationInfo info, StreamingContext context)
      : base(info, context)
    {
      Debug.Assert(context.State == StreamingContextStates.CrossAppDomain);
      Debug.Assert(IsFromThisProcess());
      Debug.Assert(IsFromThisAppDomain() == false);
      //Increment ref counter
      CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain));
      remoteObject.AppDomainConnect();
    }

    /// <summary>
    /// Disconnects the remote object.
    /// </summary>
    ~CrossAppDomainObjRef()
    {
      Debug.Assert(IsFromThisProcess());
      Debug.Assert(IsFromThisAppDomain() == false);
      //Decrement ref counter
      CrossAppDomainObject remoteObject = (CrossAppDomainObject)GetRealObject(new StreamingContext(StreamingContextStates.CrossAppDomain));
      remoteObject.AppDomainDisconnect();
    }

    /// <summary>
    /// Populates a specified System.Runtime.Serialization.SerializationInfo with
    /// the data needed to serialize the current System.Runtime.Remoting.ObjRef instance.
    /// </summary>
    /// <param name="info">The System.Runtime.Serialization.SerializationInfo to populate with data.</param>
    /// <param name="context">The contextual information about the source or destination of the serialization.</param>
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
      Debug.Assert(context.State == StreamingContextStates.CrossAppDomain);
      base.GetObjectData(info, context);
      info.SetType(typeof(CrossAppDomainObjRef));
    }
  }

次に、CrossAppDomainObject であるリモート オブジェクトは、MarshalByRefObject ではなく、このクラスから継承する必要があります。

  /// <summary>
  /// Enables access to objects across application domain boundaries.
  /// Contrary to MarshalByRefObject, the lifetime is managed by the client.
  /// </summary>
  public abstract class CrossAppDomainObject : MarshalByRefObject
  {
    /// <summary>
    /// Count of remote references to this object.
    /// </summary>
    [NonSerialized]
    private int refCount;

    /// <summary>
    /// Creates an object that contains all the relevant information required to
    /// generate a proxy used to communicate with a remote object.
    /// </summary>
    /// <param name="requestedType">The System.Type of the object that the new System.Runtime.Remoting.ObjRef will reference.</param>
    /// <returns>Information required to generate a proxy.</returns>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed override ObjRef CreateObjRef(Type requestedType)
    {
      CrossAppDomainObjRef objRef = new CrossAppDomainObjRef(this, requestedType);
      return objRef;
    }

    /// <summary>
    /// Disables LifeTime service : object has an infinite life time until it's Disconnected.
    /// </summary>
    /// <returns>null.</returns>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed override object InitializeLifetimeService()
    {
      return null;
    }

    /// <summary>
    /// Connect a proxy to the object.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void AppDomainConnect()
    {
      int value = Interlocked.Increment(ref refCount);
      Debug.Assert(value > 0);
    }

    /// <summary>
    /// Disconnects a proxy from the object.
    /// When all proxy are disconnected, the object is disconnected from RemotingServices.
    /// </summary>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public void AppDomainDisconnect()
    {
      Debug.Assert(refCount > 0);
      if (Interlocked.Decrement(ref refCount) == 0)
        RemotingServices.Disconnect(this);
    }
  }
于 2010-03-11T08:19:47.750 に答える
7

残念ながら、AppDomains がプラグインの目的で使用されている場合、この解決策は間違っています (プラグインのアセンブリをメインの appdomain にロードしてはなりません)。

コンストラクターとデストラクターで GetRealObject() を呼び出すと、リモート オブジェクトの実際の型が取得され、リモート オブジェクトのアセンブリを現在の AppDomain に読み込もうとします。これにより、例外 (アセンブリを読み込めない場合) が発生するか、後でアンロードできない外部アセンブリを読み込んだという望ましくない影響が発生する可能性があります。

より良い解決策は、リモート オブジェクトをメインの AppDomain に ClientSponsor.Register() メソッドで登録することです (静的ではないため、クライアント スポンサー インスタンスを作成する必要があります)。デフォルトでは、リモート プロキシは 2 分ごとに更新されます。これは、オブジェクトの有効期間がデフォルトの 5 分間である場合には十分です。

于 2010-04-16T16:17:48.717 に答える
1

クラスを作成したり無限の寿命を与えたりせずに、ガベージ コレクションが行われた後にリモート オブジェクトを再作成する場合ISponsorは、リモート オブジェクトのダミー関数を呼び出しながらRemotingException.

public static class MyClientClass
{
    private static MarshalByRefObject remoteClass;

    static MyClientClass()
    {
        CreateRemoteInstance();
    }

    // ...

    public static void DoStuff()
    {
        // Before doing stuff, check if the remote object is still reachable
        try {
            remoteClass.GetLifetimeService();
        }
        catch(RemotingException) {
            CreateRemoteInstance(); // Re-create remote instance
        }

        // Now we are sure the remote class is reachable
        // Do actual stuff ...
    }

    private static void CreateRemoteInstance()
    {
        remoteClass = (MarshalByRefObject)AppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(remoteClassPath, typeof(MarshalByRefObject).FullName);
    }
}
于 2016-10-08T22:50:22.893 に答える
0
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.Infrastructure)]
public override object InitializeLifetimeService()
{
  return null;
}

私はこれをテストし、正常に動作しています。もちろん、自分で GC を実行するまで、プロキシが永遠に存続することを知っておく必要があります。しかし、私の場合、メイン アプリに接続された Plugin-Factory を使用すると、メモリ リークなどは発生しません。IDisposable を実装していて、正常に動作していることを確認しました (ファクトリが正しく破棄されると、(ファクトリに) ロードされた dll が上書きされる可能性があるため、わかります)

編集:ドメインを介したバブリングイベントの場合、このコード行をクラスに追加してプロキシも作成します。そうしないと、バブリングもスローされます;)

于 2015-10-13T09:59:33.827 に答える
0

IObjectReference を実装するシリアル化可能なシングルトン ISponsor オブジェクトを試すことができます。GetRealObject の実装 (IObjectReference から) は、context.State が CrossAppDomain の場合に MySponsor.Instance を返す必要があります。 static MySponsor.IsFlaggedForUnload を実行し、unload/AppDomain.Current.IsFinalizingForUnload() のフラグが設定されている場合は TimeSpan.Zero を返し、それ以外の場合は LifetimeServices.RenewOnCallTime を返します。

これをアタッチするには、単純に ILease と Register(MySponsor.Instance) を取得します。これは、GetRealObject の実装により、AppDomain 内に設定された MySponsor.Instance に変換されます。

スポンサーシップを停止するには、ILease と Unregister(MySponsor.Instance) を再度取得してから、クロス AppDomain コールバック (myPluginAppDomain.DoCallback(MySponsor.FlagForUnload)) を介して MySponsor.IsFlaggedForUnload を設定します。

これにより、登録解除の呼び出し、FlagForUnload の呼び出し、または AppDomain のアンロードのいずれかが行われるまで、オブジェクトは他の AppDomain で存続します。

于 2014-06-09T20:56:28.797 に答える
-2

最近、この例外にも遭遇しました。現在、私の解決策は、AppDomain をアンロードしてから、長い間隔を置いて AppDomain をリロードすることです。幸いなことに、この一時的な解決策は私の場合に役立ちます。これに対処するためのよりエレガントな方法があればいいのにと思います。

于 2011-01-24T18:32:06.357 に答える