48

I need to use cross-appdomain calls in my app, and sometimes I have this RemotingException:

Object '/2fa53226_da41_42ba_b185_ec7d9c454712/ygiw+xfegmkhdinj7g2kpkhc_7.rem' has been disconnected or does not exist at the server.

The target object is still alive, I have checked it.

UPD I've set breakpoint in the finalizer of the target object, and it never hits. Thus, this object is alive and wasn't GC'ed.

Satpal
  • 132,252
  • 13
  • 159
  • 168
user626528
  • 13,999
  • 30
  • 78
  • 146
  • Possible duplicate of [AppDomain and MarshalByRefObject life time : how to avoid RemotingException?](https://stackoverflow.com/questions/2410221/appdomain-and-marshalbyrefobject-life-time-how-to-avoid-remotingexception) – cdiggins Jan 27 '19 at 17:32

7 Answers7

35

That is probably because the local garbage collector at the server side collects the object. You can prevent that by renewing the leasing. You can read more about that in these articles:

Update: Unfortunately, the MSDN Magazine issues from 2008 or older are no longer browseable online, but only as .chm files that you have to download to your local machine. The previous issues can be found in:

Bouke
  • 11,768
  • 7
  • 68
  • 102
Marius Bancila
  • 16,053
  • 9
  • 49
  • 91
  • It can't be GC'ed, because server-side keeps reference to this object. – user626528 Jun 14 '11 at 07:16
  • 1
    If you have disabled Lifetime management, the object remains exposed by remoting (so it can't be GC'ed) until you disconnect it on the server side. – Guillaume Jun 14 '11 at 08:24
  • @Guillaume, how I can disconnect cross-domain marshalled reference? – user626528 Jun 14 '11 at 08:57
  • 2
    On the *Server* side you can call RemotingServices.Disconnect(myObject); – Guillaume Jun 14 '11 at 09:26
  • @Guillaume, this seems working, but I still don't understand why this problem happened. – user626528 Jun 14 '11 at 10:22
  • 1
    Remoting keeps a reference on your object as long as it's exposed. If the lifetime is infinite, Remoting will keep a reference forever unless you explicity tell it to close the server and release the reference (that is done by calling Disconnect). – Guillaume Jun 14 '11 at 10:26
  • @Guillaume, I see. But, why the reference to my object died, despite the object itself was still alive? – user626528 Jun 14 '11 at 10:47
  • 2
    @user626528 The reference died ? When ? You mean without changing lifetime ? If so, that's because if no method is called by the client for a certain period, default Remoting automatically disconnect it and release its reference. – Guillaume Jun 14 '11 at 11:55
  • @MariusBancila I wonder if you could by any chance update those links :/ – Prix Jul 14 '15 at 04:27
  • Unfortunately they are gone, as MSDN Issues older than January 2009 are no longer browsable online, but only available as downloads. :( – Marius Bancila Jul 14 '15 at 20:35
  • Does anybody have an actual answer for this question. The links are dead and the chm files not appear to work either. – Matt Burland Apr 26 '16 at 19:17
  • 1
    @MattBurland The CHM files work for me, but only after I unblock them as their [website says](https://msdn.microsoft.com/magazine/msdn-magazine-issues). Right click the .chm file, go to properties, then click "Unblock". Note that before I did this the file would open and show the Contents but would not render any pages. After unblocking it shows the whole file just fine. – Quantic Apr 29 '16 at 19:40
16

This is because the Lifetime management on the server side disconnects the object when its lease expires, to allow GC to collect it. If you try to use it from the client side, you will get an Exception, even if it has not been GC'd on the server yet (e.g. because there still is another reference to it) but the lease has expired. This is to avoid unpredictable behaviour. The accepted answer provides a good reference on how to correctly manage the lifetime of Remote .NET Objects.

Stefan Paul Noack
  • 3,654
  • 1
  • 27
  • 38
7

I had the same problem and I searched for many hours with help of many StackOverflow posts.

I finally found the complete issue.

  1. I have to use a Sponsor to maintain my MarshalByRefObject alive.
  2. I then had the same problem than @user626528 : object is alive but I had the exception. In fact, I needed to "sponsor" ALL THE "TransparentProxy" instances, and not only the main one : my main Object created in SandBox (another AppDomain) returns references to other MarshalByRefObjects.

Here is the complete explanation and use case :

My class "Loader" inherits from MarshalByRefObject, and I keep it alive with a ISponsor class. I know "ClientSponsor" exists in .NET, but I had no way to determine if and when Renewal() is called, so I made my class with help of StackOverflow community (read code comments) :

/// <see cref="https://stackoverflow.com/questions/18680664/remoting-sponsor-stops-being-called"/>
public class RemotingSponsor : MarshalByRefObject, ISponsor, IDisposable
{
    /*
     * @CoryNelson said :
     * I've since determined that the ILease objects of my sponsors 
     * themselves are being GCed. They start out with the default 5min lease 
     * time, which explains how often my sponsors are being called. When I 
     * set my InitialLeaseTime to 1min, the ILease objects are continually        
     * renewed due to their RenewOnCallTime being the default of 2min.
     * 
     */ 

    ILease _lease;

    public RemotingSponsor(MarshalByRefObject mbro)
    {
        _lease = (ILease)RemotingServices.GetLifetimeService(mbro);
        if (_lease == null) throw new NotSupportedException("Lease instance for MarshalByRefObject is NULL");
        _lease.Register(this);
    }

    public TimeSpan Renewal(ILease lease)
    {
        Debug.WriteLine("RemotingSponsor.Renewal called");
        return this._lease != null ? lease.InitialLeaseTime : TimeSpan.Zero;
    }


    public void Dispose()
    {
        if (_lease != null)
        {
            _lease.Unregister(this);
            _lease = null;
        }
    }

    public override object InitializeLifetimeService()
    {
        /*
         *
         * @MatthewLee said:
         *   It's been a long time since this question was asked, but I ran into this today and after a couple hours, I figured it out. 
         * The 5 minutes issue is because your Sponsor which has to inherit from MarshalByRefObject also has an associated lease. 
         * It's created in your Client domain and your Host domain has a proxy to the reference in your Client domain. 
         * This expires after the default 5 minutes unless you override the InitializeLifetimeService() method in your Sponsor class or this sponsor has its own sponsor keeping it from expiring.
         *   Funnily enough, I overcame this by returning Null in the sponsor's InitializeLifetimeService() override to give it an infinite timespan lease, and I created my ISponsor implementation to remove that in a Host MBRO.
         * Source: https://stackoverflow.com/questions/18680664/remoting-sponsor-stops-being-called
        */
        return (null);
    }
}

And then I used this "custom sponsor" like this:

// Loader and Container for MarshalByRefObject in another domain
 public class PluginFile : IDisposable
 {
           private RemotingSponsor _sponsor; // Keep instance not to have Sponsor Garbage Collected
           private AppDomain _sandbox;
           private ICustomPlugin[] _plugins; // I do not store real instances of Plugins, but a "CustomPluginProxy" which is known both by main AppDomain and Plugin AppDomain.

    // Constructor : load an assembly file in another AppDomain (sandbox)
    public PluginFile(System.IO.FileInfo f, AppDomainSetup appDomainSetup, Evidence evidence)
    {
        Directory = System.IO.Path.GetDirectoryName(f.FullName) + @"\";
        _sandbox = AppDomain.CreateDomain("sandbox_" + Guid.NewGuid(), evidence, appDomainSetup);

        _sandbox.Load(typeof(Loader).Assembly.FullName);

        // - Instanciate class "Loader" INSIDE OTHER APPDOMAIN, so we couldn't use new() which would create in main AppDomain.
        _loader = (Loader)Activator.CreateInstance(
            _sandbox,
            typeof(Loader).Assembly.FullName,
            typeof(Loader).FullName,
            false,
            BindingFlags.Public | BindingFlags.Instance,
            null,
            null,
            null,
            null).Unwrap();

        // - Load plugins list for assembly
        _plugins= _loader.LoadPlugins(f.FullName); 


        // - Keep object created in other AppDomain not to be "Garbage Collected". I create a sponsor. The sponsor in registed for object "Lease". The LeaseManager will check lease expiration, and call sponsor. Sponsor can decide to renew lease. I not renewed, the object is garbage collected.
        // - Here is an explanation. Source: https://stackoverflow.com/questions/12306497/how-do-the-isponsor-and-ilease-interfaces-work
        _sponsor = new RemotingSponsor(_loader);

       // Here is my SOLUTION after many hours ! I had to sponsor each MarshalByRefObject (plugins) and not only the main one that contains others !!!
       foreach (ICustomPlugin plugin in Plugins) 
        {
            ILease lease = (ILease)RemotingServices.GetLifetimeService((PluginProxy)plugin);
            lease.Register(_sponsor); // Use the same sponsor. Each Object lease could have as many sponsors as needed, and each sponsor could be registered in many Leases.
        }
    }

 }

The PluginProxy type has a reference towards the real plugin type. Indeed, the PluginProxy is instanciated inside Plugin AppDomain, and returned to main AppDomain, to allow it to call Plugins even if it ignore their real type. So the PluginProxy, to be accessible from main AppDomain, have to be serialized to cross AppDomains limits. I had a problem because I didn't sponsored these MarshalByRefObject(s) :

 /// <see cref="https://stackoverflow.com/questions/4185816/how-to-pass-an-unknown-type-between-two-net-appdomains"/>
    [Serializable]
    public class PluginProxy : MarshalByRefObject, ICustomPlugin
    {
        private ICustomPlugin _hostedPlugin;            

        /// <summary>
        /// Parameterless constructor for deserialization 
        /// </summary>
        public PluginProxy()
        {             
        }

        ~PluginProxy()
        {
            Debug.WriteLine("DESTRUCTOR ~PluginProxy");
        }

        /// <summary>
        /// Constructor reserved from real Plugin type
        /// </summary>
        /// <param name="name"></param>
        public PluginProxy(ICustomPlugin hostedPlugin)
        {
            _hostedPlugin = hostedPlugin;
        }

        public PluginName Name => _hostedPlugin.Name;

        public PluginResult Execute(PluginParameters parameters, PluginQuery query)
        {
            return(_hostedPlugin.Execute(parameters, query));
        }
    }

It was a difficult bunch of problems to solve, hope this helps !

References:

Elo
  • 2,234
  • 22
  • 28
  • 2
    CleientSponsor implementation https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/runtime/remoting/clientsponsor.cs – Evgeny Ivanov Sep 28 '18 at 05:42
5

This question has been answered in great detail already on StackOverflow. TL/DR:

  1. If you want Singleton semantics override InitializeLifetimeService to return null
  2. Use ClientSponsor to keep you object alive longer.
cdiggins
  • 17,602
  • 7
  • 105
  • 102
  • One of the undocumented tricks is that the sponsor is also a remote object. ClientSponsor initializes itself, overriding `InitializeLifetimeService` to return null so _it_ doesn't expire and stop responding to renewal requests. If the sponsor is expired, the remote object will eventually expire, too (if not using Singleton semantics). – Suncat2000 Mar 18 '21 at 16:21
3

This happened for us because we had a static variable in one of our classes that was of type AppDomain. The class was used in a long running windows service. AppDomain has a InitializeLifetimeService method that needs to be overridden like this:

public override object InitializeLifetimeService(){
    return null;
}

We were constantly using this as the private variable that loaded and unloaded some dlls for custom built outside logic. The answer has been taken from here: msdn answer

Because we were not able to change this at production time, we ended with a compromise of restarting the windows service at random intervals that are shorter than the lifetime of the static AppDomain variable which by trial and error we found that it is several days.

This question also helped clarify some things about lifetime: stackoverflow-question

Alin
  • 394
  • 5
  • 14
0

in my case, the problem was that in the client computer, there was an virtual network adapter active, disabling the virtual network adapters, the problem was solved

Corey Adler
  • 15,897
  • 18
  • 66
  • 80
Ismael
  • 16
  • 1
0

In my case, this was happening with SQL LocalDB stored in App_Data folder inside Web project. Whenever I try to use Package Console to run update-database to init my Entity Framework database using migrations, nothing happens. Then after a while, I get that error.

I solved this by revising file permissions on App_Data. Once fixed, voila, it worked.

Korayem
  • 12,108
  • 5
  • 69
  • 56