LifetimeService timeout

275 views
Skip to first unread message

Joe

unread,
May 23, 2012, 5:13:24 PM5/23/12
to CS-Script
Hello,

I'm trying to use this as a script engine for a test tool where I'm
basically populating a combo box with all public methods available in
the script and executing the methods as if they were individual
scripts onto themselves. This tool will be loading and unloading
different script files, so I would like to use the Remoting pattern.
Also, I've decided to use an instantiated class instead of a static
class because I can then use inheritance and put some framework code
in a base class contained within my host application.

Also, I won't know what methods will be in each script file until
runtime, so all methods must be executed using pure reflection.
Unfortunately the pattern I've chosen seems to be one that has no
examples. I did figure out how to get all this working (after several
days of pain), but now I'm stuck.

My problem is that no matter what I do, 5 minutes after the script is
loaded, I get a Remoting exception indicating that the object has been
disconnected.

Object [...] has been disconnected or does not exist at the server.

After doing some research, I found that I need to handle the
LifetimeService lease on the object. I believe I've done everything
correctly, except now I'm at a point where things just don't make
sense. I believe I've properly gotten the LifetimeService reference
from myScriptInstance. I'm certain this works because I am able to
change the timeout in various ways to a shorter time period from the
default 5 minutes and the object properly expires. The problem is no
matter what method I use to extend the lifetime of my script it still
expires at the end of the 5 minutes.

This leads me to believe that there is some other object that I don't
have control over that isn't managing its lifetime service. I think
it has something to do with AsmHelper, but I haven't been able to
track it down yet. I tried exposing the asmBrowser object from
AsmHelper and handling the lifetime of that, but it didn't seem to fix
my issue either. Either I did that wrong or there is some other
object that needs to be dealt with.

This is really irritating to debug because every test I do takes 5
minutes.

Here's what I've tried:
From the script: Overriding the InitializeLifetimeService() and
setting up different parameters seems to work in that I can SHORTEN
the lifetime, but if I try to extend it, after 5 minutes it still
dies.
From the script: Overriding the InitializeLifetimeService() and
returning null still causes the script to die after 5 minutes.
From the host: calling ScriptInstance.GetLifeTimeService() allows me
to get a lifetime object that I can control. Again, I can shorten the
lifetime, but if I try to extend it, after 5 minutes it dies.
From the host: calling ScriptInstance.GetLifeTimeService() and
registering a sponsor seems to work. I’ve shortened the lifetime, and
at the end of that I see the sponsor’s Renewal method fire and renew
properly. After 5 minutes it still dies regardless of the renewals.
From the host: calling any method causes a renewal, but after 5
minutes


Here's an article discussing my issue with various solutions.
http://www.codeproject.com/Articles/18997/Remoting-Architecture-in-NET

asmFile = CSScriptLibrary.CSScript.Compile("dynamicscript.cs", null,
true);

CSScriptLibrary.AsmHelper helper = new
CSScriptLibrary.AsmHelper(asmFile, "ScriptDomain1", true);
object myScriptInstance = helper.CreateObject("*");

helper.InvokeInst(myScriptInstance, "*.Initialize", this);
string[] methodnames = (string[])helper.InvokeInst(myScriptInstance,
"*.GetMethodNames");

ScriptLease = (ILease)ScriptInstance.GetLifetimeService();

//I've tried several methods here and they all seem to properly affect
the lifetime of the myScriptInstance object, but I can't access my
script after 5 minutes.

//Use a sponsor object to continually renew the lease
ScriptSponsor = new ClientSponsor();
ScriptLease.Register(ScriptSponsor);

//Try just renewing the lease for a year
ScriptLease.Renew(TimeSpan.FromDays(365));


Any help would be greatly appreciated. Thanks.

Joe

Oleg Shilo

unread,
May 24, 2012, 1:34:16 AM5/24/12
to cs-s...@googlegroups.com
Hi Joe,

It is hard to comment your issue but I doubt that it is related to the AsmHelper. It is a utility class that needed only to simplify the marshaling and AppDomain setup. 
Unless of course if you are disposing your helper at the 5 min point. If your do then the helper will dispose the script object as well. 

If you are not disposing your helper then it would be a good approach to minimize the impact of all CS-Script related routines in order to find (by elimination) what is causing your problem.

Thus for the AppDomain setup you can easily replace the AsmHelper with the AppDomain extensions:


Action job = () =>             
{                 
    Assembly scriptAsm = CSScript.Load(<script code>);
    object myScriptInstance = scriptAsm.Createinstance("*");
    myScriptInstance.GetMethodNames(......
}; 

AppDomain.CurrentDomain
                     .Clone()
                     .Execute(job)         
                     .Unload();

Cheers,
Oleg Shilo
Message has been deleted

jdun...@gmail.com

unread,
May 24, 2012, 3:41:57 PM5/24/12
to cs-s...@googlegroups.com, osh...@gmail.com
I've written this post over the course of several hours, so my thinking has evolved throughout the post.

Unfortunately it's not as simple as me disposing the helper at the 5 minute point. 

Digging into AsmHelper I've found that AsmRemoteBrowser is needed in my case because I'm using a remote appdomain.  After attempting to roll my own AsmHelper I've found that I have no Invoke method.  It looks like AsmHelper is doing a bit more than I initially thought.  I now see what you're talking about with it helping with the marshalling.  The FindMethod and Invoke implementations appear to be what I'm missing. 

Rather than dig into stuff I don't fully understand I decided to just cast the Instance into an Interface class instead of trying to get the reflection working.  This doesn't satisfy my requirement of being able to use pure reflection to call the methods, but it is still a good test.  Anyway, with this test version, the script now survives past the 5 minute mark, but only if I periodically call a method from it.  Obtaining and renewing the lease on my object alone doesn't seem to fix my issue.  It seems that when I call the method it causes a RenewOnCall on some object in the chain.   I believe that the object that is expiring might be the remote proxy.  I tried getting a lease on that and unfortunately that doesn't seem to fix the issue either.

I went back to the project using AsmHelper and attempted getting a lease on the remote proxy.  I have gotten to a point where I can now keep the script alive using AsmHelper.  I believe handling the Lifetime lease on the transparent proxy is what got me further, but I still have to periodically call a method in my script using AsmHelper.InvokeInst() (doable if I have no other choice), but not very satisfying.  The interesting thing is that if I just call a method on the instance object directly, that keeps the instance alive past the 5 minutes, but AsmHelper still times out, so I can no longer use AsmHelper.InvokeInst() after the timeout so I can only call methods defined in my interface.

Now it seems like the AsmHelper object or something in it is timing out.  I tried a custom build of CSScriptLibrary where AsmHelper.asmBrowser was made public.  I then set up a a lifetime lease on that, and still that didn't fix the issue either.  I still need to call the object using AsmHelper.InvokeInst().

Unfortunately I haven't yet been able to track down what is expiring when I use AsmHelper or if I just create the domain by myself.  I'll keep digging because I'd like to be able to use AsmHelper since it does a nice job of handling the reflection.

The best solution would be to get a Lifetime lease on all the objects required and handle it that way instead of having to jump through hoops. 

I've attached a simple example that illustrates the problem.  Run the program, click the "Load Script file" button, wait 5 minutes, then click the Execute button.  You should see a RemotingException stating that the object has been disconnected or does not exist at the server. At the bottom of Form1.cs is a timer event that fires every second.  Uncomment this line:  ScriptDomain.InvokeInst(
ScriptInstance, "*.Keepalive");  and try again.  You should see that the script doesn't time out.

Here's where I'm at now:


asmFile = CSScriptLibrary.CSScript.Compile("dynamicscript.cs", null, true);

//This constructor forces creation of a new appdomain:
ScriptDomain = new CSScriptLibrary.AsmHelper(asmFile, "ScriptDomain1", true);
ScriptInstance = (ScriptBase)ScriptDomain.CreateObject("*");

ScriptDomain.InvokeInst(ScriptInstance, "*.Initialize", this);
string[] methodnames = (string[])ScriptDomain.InvokeInst(ScriptInstance, "*.GetMethodNames");

//Very interesting.  It seems I need to obtain the below leases AND call a method from the instantiated object to keep the script from timing out.
//However, in order to keep AsmHelper alive, I need to use reflection via AsmHelper.InvokeInst() to call the method. 
//If I call the member function directly from the instance, the AsmHelper times out.

//Proxy lease:
System.Runtime.Remoting.Proxies.RealProxy RealProxy = System.Runtime.Remoting.RemotingServices.GetRealProxy(ScriptInstance);
object TransparentProxy = RealProxy.GetTransparentProxy();
ProxyLease = (ILease)(((MarshalByRefObject)TransparentProxy).GetLifetimeService());
ProxyLease.Renew(TimeSpan.FromHours(5));

//Proxy Sponsor:
ProxySponsor = new ClientSponsor();
ProxyLease.Register(ProxySponsor);

//Script Instance lease:
ScriptLease = (ILease)System.Runtime.Remoting.RemotingServices.GetLifetimeService((MarshalByRefObject)ScriptInstance);
ScriptLease.Renew(TimeSpan.FromHours(5));

//Script Sponsor:

ScriptSponsor = new ClientSponsor();
ScriptLease.Register(ScriptSponsor);



Plus I have a timer event:
private void timer1_Tick(object sender, EventArgs e)
{
//btnExecuteScript_Click(this, null);
if (ScriptInstance != null)
{
//This keeps Everything happy:  (In other words if I call this, after 5 minutes everything keeps working just fine)
ScriptDomain.InvokeInst(ScriptInstance, "*.Keepalive");

//This only keeps the ScriptInstance happy:  (In other words if I only call this, after 5 minutes I can't use AsmHelper.InvokeInst().)
//ScriptInstance.Keepalive();
}
}
myCSScript.zip

Oleg Shilo

unread,
May 24, 2012, 9:09:32 PM5/24/12
to CS-Script
Juts a few comments/ideas.

Of course polling your object to keep it alive will always work. It is
just something that one always tries to avoid. :)

I think you have too many variables in your equation so it is hard to
understand which one is impacting the outcome. My advice, forget about
scripting for now. Have your scenario working for concrete classes and
only then update your solution to work with the scripting.

The AsmHelper seems to be an heavy player here. You do not
particularly need it except to simplify the reflection. So why don't
you move the helper completely to your temp AppDomain and use it there
for the reflection only (but not for the marshaling). Remember,
AsmHelper can be instantiated in two modes depending on which
constructor you use. The thing is, that you need to implement the
Dispatch pattern, and you do not need to interact with the AsmHelper
directly for this.This how the solution may look like:

class ScriptProxy : MarshalByRef
{
AsmHelper scriptHelper;
void GetMethods(string script)
{
scriptHelper = new AsmHelper(CSSript.Load());
}
string[] GetMethods()
{
return (string[])scriptHelper.Invoke("GetMethods");
}
object Invoke(string method, params object[] args)
{
return scriptHelper.Invoke(method, args);
}
}

Host:

var scriptInstance=
(ScriptProxy )remoteAppDomain.CreateInstanceFromAndUnwrap(...,
typeof(ScriptProxy));

// Adjust leasing for your scriptInstance here.

scriptInstance.GetMethods();
scriptInstance.Invoke("some method");

remoteAppDomain.Unload(); //when not needed

This way you will have to manage the life time of a very simple type
(ScriptProxy). The AsmHelper is still helping you with the reflection
but you are not passing it through AppDomain boundaries and it is not
involved in any marshaling at all.

Good luck,
Oleg
>  myCSScript.zip
> 259KViewDownload

jdun...@gmail.com

unread,
May 25, 2012, 10:10:33 AM5/25/12
to cs-s...@googlegroups.com
Oleg,

Thanks for the suggestion, but I think you went a bit over my head.  I'm really struggling to keep up with this stuff as I have no experience with appdomains and reflection other than the past week.  I do C# programming only recreationally, so I'm not exactly an expert.  I get the idea that you want to try, but the details are escaping me.  I'm running into trouble in 2 places...

//Your suggestion:
var scriptInstance=(ScriptProxy )remoteAppDomain.CreateInstanceFromAndUnwrap(...,typeof(ScriptProxy));

//My implementation:  (It's not quite right...  I get an exception on the CreateInstanceFromAndUnwrap() saying that the object ScriptProxy cannot be aligned to my interface..  )
                string asmFile;

                asmFile = CSScriptLibrary.CSScript.Compile("ScriptBase.cs", null, true);

                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = Environment.CurrentDirectory;               //Path.GetDirectoryName(asmFile)
                //setup.ApplicationBase = Path.GetDirectoryName(asmFile);
                setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
                setup.ApplicationName = Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                setup.ShadowCopyFiles = "true";
                setup.ShadowCopyDirectories = Path.GetDirectoryName(asmFile);
                ScriptDomain = AppDomain.CreateDomain("", null, setup);

                ScriptInstance = ScriptDomain.CreateInstanceFromAndUnwrap(asmFile, typeof(ScriptProxy).ToString()).AlignToInterface<ScriptInterface>();
                //ScriptInstance = new ScriptProxy();

                ScriptInstance.Invoke("*.Initialize", this);
                string[] methodnames = (string[])ScriptInstance.Invoke("*.GetMethodNames");


//If I skip the CreateInstanceFromAndUnwrap and just create an instance of the ScriptProxy, then I run into issues with the ScriptProxy implementation.  Here's my implementation:
    public interface ScriptInterface
    {
        void Initialize(myCSScript myhost);
        string[] GetMethodNames();
        object Invoke(string method, params object[] args);
        void DebugThis();
        void Keepalive();
    }


    public class ScriptProxy : MarshalByRefObject, ScriptInterface
    {
        AsmHelper scriptHelper;

        public ScriptProxy()
        {
            //I can't get this line right.. It wants an assembly reference of this object, and I don't know how to give it one..
            scriptHelper = new AsmHelper(AppDomain.CurrentDomain.GetAssemblies()[0]);
        }

        public object Invoke(string method, params object[] args)
        {
            return scriptHelper.Invoke(method, args);
        }

        public nsCSScript.myCSScript Host;
        public void Initialize(myCSScript myhost)
        {
            Host = myhost;
        }
        public string[] GetMethodNames()
        {
            return (from mt in this.GetType().GetMethods()
                    where mt.Name != "Initialize"
                    where mt.Name != "GetMethodNames"
                    where mt.Name != "GetLifetimeService"
                    where mt.Name != "InitializeLifetimeService"
                    where mt.Name != "ToString"
                    where mt.Name != "GetHashCode"
                    where mt.Name != "Keepalive"
                    where mt.Name != "GetType"
                    where mt.GetParameters().Length == 0            //only populate items that have no parameters.
                    select mt.Name
            ).ToArray();
        }

        public void Keepalive()
        {
            //This is a dummy method that gets called by the script host to keep the LifeTimeService from timing out.
        }
    }


I'm going to try and move forward with other features that I'll be needing and just live with having to keep "kicking" the asmhelper every few minutes to keep it alive.  This workaround isn't ideal, but its good enough for now.  I'll keep this issue as a todo item, but I'm not going to focus more efforts on it right now unless you have other suggestions.  Thanks for the input.

Joe

jdun...@gmail.com

unread,
May 25, 2012, 10:41:58 AM5/25/12
to cs-s...@googlegroups.com
I knew this would keep bugging me...  I tried again and got past my initial problems...  Now everything works, except when I try to use ScriptInstance.Invoke() I get a null reference exception.  The problem is I don't see what is null.  So, I still can't run the test I want.  Anyway, back to something else. 


                string asmFile;
               
                asmFile = CSScriptLibrary.CSScript.Compile("ScriptProxy.cs", null, true);

                //CSScriptLibrary.AsmHelper scriptHelper = new CSScriptLibrary.AsmHelper(System.Reflection.Assembly.GetExecutingAssembly());


                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = Environment.CurrentDirectory;               //Path.GetDirectoryName(asmFile)
                //setup.ApplicationBase = Path.GetDirectoryName(asmFile);
                setup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
                setup.ApplicationName = Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                setup.ShadowCopyFiles = "true";
                setup.ShadowCopyDirectories = Path.GetDirectoryName(asmFile);
                ScriptDomain = AppDomain.CreateDomain("", null, setup);

                ScriptInstance = ScriptDomain.CreateInstanceFromAndUnwrap(asmFile, "nsCSScript.ScriptProxy").AlignToInterface<ScriptInterface>();

                //ScriptInstance.Invoke("*.Initialize", this);
                ScriptInstance.Initialize(this);

                //string[] methodnames = (string[])ScriptInstance.Invoke("*.GetMethodNames");
                string[] methodnames = ScriptInstance.GetMethodNames();




    public interface ScriptInterface
    {
        void Initialize(myCSScript myhost);
        string[] GetMethodNames();
        object Invoke(string method, params object[] args);
        void DebugThis();
        void Keepalive();
    }


    public class ScriptProxy : MarshalByRefObject, ScriptInterface
    {
        AsmHelper scriptHelper = new AsmHelper(System.Reflection.Assembly.GetExecutingAssembly());


        public object Invoke(string method, params object[] args)
        {
            return scriptHelper.Invoke(method, args);
        }

        public nsCSScript.myCSScript Host;
        public void Initialize(myCSScript myhost)
        {
            Host = myhost;
        }
        public string[] GetMethodNames()
        {
            return (from mt in this.GetType().GetMethods()
                    where mt.Name != "Initialize"
                    where mt.Name != "GetMethodNames"
                    where mt.Name != "GetLifetimeService"
                    where mt.Name != "InitializeLifetimeService"
                    where mt.Name != "ToString"
                    where mt.Name != "GetHashCode"
                    where mt.Name != "Keepalive"
                    where mt.Name != "GetType"
                    where mt.GetParameters().Length == 0            //only populate items that have no parameters.
                    select mt.Name
            ).ToArray();
        }

        /*
        public override object InitializeLifetimeService()
        {
            ILease lease = (ILease)base.InitializeLifetimeService();
            //Debug.Assert(lease.CurrentState == LeaseState.Initial);

            //Set lease properties to expire in 1 year:
            lease.InitialLeaseTime = TimeSpan.FromDays(365);
            lease.RenewOnCallTime = TimeSpan.FromDays(365);
            lease.SponsorshipTimeout = TimeSpan.FromDays(365);
            return lease;
        }
        */

        public void DebugThis()
        {
            //System.Diagnostics.Debugger.Launch();
            System.Diagnostics.Debugger.Break();

Oleg Shilo

unread,
May 25, 2012, 9:40:23 PM5/25/12
to CS-Script
Hi Joe,

Well, there are too many things are happening in your code....

Keep in mind that I had to make a few assumptions from your early
descriptions but now I am not sure these assumptions are valid. For
example I assumed that you need to execute your script in the separate
AppDomain. Now I am not convinced the you really need that temp
AppDomain. But anyway it is not where the problem is.

You have so many things going on in your code that it is very hard
follow. The solution is much, much, much simple. Thus I have prepared
the working code for you: https://dl.dropbox.com/u/45608909/LifetimeManagement.7z

Cheers,
Oleg

jdun...@gmail.com

unread,
May 29, 2012, 12:36:05 PM5/29/12
to cs-s...@googlegroups.com
Oleg,

I appreciate you taking the time to help, but your solution doesn't work.  I get a runtime error: Could not load type 'ScriptProxy' from assembly.  I'm not sure what the error is here.  I tried a few things to get it to work, but I was not able to. 

I'm not sure what you mean by there being too many things happening in my code.  There isn't much there.  1 button to load the script file and create the script instance.  Another button to execute methods.  There is a timer event to call the keepalive method since I wasn't able to get the lifetime service code working properly.  I guess maybe you're talking about the UIBlockingInvoke and the BackgroundWorker code?  Basically I've set up the code to use BackgroundWorker to execute the button handling code in a new thread.  The UIBlockingInvoke() allows my new thread to access a form object from a separate thread.  Whenever I use either one I use an anonymous method to make the code read more cleanly.  The basic concept is that the script will be a potentially long-running method, and I need the U/I to be responsive during it's execution, so I run it in a separate thread and use Invoke's to control the U/I form.  You can pretty much ignore that stuff and the code is the same.  The main concept of the application is to load up a script, extract all the methods available in it, then keep it loaded and ready to go.  A user can then execute the various methods by selecting them from the combo box and clicking execute.  All of this works fine, except that the lifetime is expiring after 5 minutes.

Your attached code unloads the new appdomain immediately after doing any work, so the lifetime of the object cannot possibly be evaluated since it is destroyed immediately after being created.  My problem is that I create an instance of the remote class and everything works fine, but if I stop calling methods on the remote object for 5 minutes, the remote object's lifetime expires and is garbage collected.  I don't see anything in your code referencing System.Runtime.Remoting.Lifetime which you must do in order for a remote object to live past 5 minutes.  Please see the following for more information on LifeTime:
http://www.codeproject.com/Articles/8812/A-Simple-But-Useful-Example-of-NET-Remoting-Part-2
http://www.codeproject.com/Articles/14791/NET-Remoting-with-an-easy-example

I spent a couple hours this morning trying to get your code to work and failing, then going back to my code and trying to use some of the concepts from your code, and again failing.  I'm sorry if I'm missing the point you're trying to make in your example code, but it seems that you don't fully understand my problem.

Also, I do need a remote appdomain because my application will need to be able to load and unload different script files as I mentioned in my first post.

Again, thank you very much for your time.  I do very much like cs-script.  I just need to get past a couple stumbling blocks and it will be an excellent tool in my application.

Joe

Oleg Shilo

unread,
May 29, 2012, 9:47:05 PM5/29/12
to cs-s...@googlegroups.com
Joe,

The challenges you are facing should be separated on two independent parts: Scripting and Remoting.

I was trying to assist you with the first challenge only. In your 2 previous posts you stated that you have the problem with the null reference exception so I went through your code and found some problem with it. When I said "too many things are happening" I mainly meant "unnecessary interface alignment" and "complicated AppDomain setup". I have produced the solution for you, which demonstrates how to avoid these scripting problems. It works with respect to Scripting but it may (or may not work) with respect to Remoting lifetime management. 

Just in case you could not get it working because it did not have VS project infrastructure I have uploaded the full VS project for you: https://dl.dropbox.com/u/45608909/LifetimeManagement_VS.7z

BTW I did a quick modification of the sample solution (3 lines of code) and it seems that  using ClientSponsor in the remote object does do the trick:

var sponsor = new ClientSponsor();
sponsor.Register(this);
sponsor.RenewalTime = TimeSpan.FromMinutes(6);

Thus I was able to extend idle timeout to 6 minutes and the sample solution now life invokes the script method every 5.5 minutes from the background thread. So the sample works for Remoting part as well.

Cheers,
Oleg Shilo

jdun...@gmail.com

unread,
May 30, 2012, 2:07:49 PM5/30/12
to cs-s...@googlegroups.com, osh...@gmail.com
Oleg,

You sir are my hero.  Thank you very much.  That is a viable solution.  Thanks for the complete solution with the visual studio project.  It was much easier to follow your code when it was complete and running in a project.

The thing that gets me is that I still can't get it to work if I use AsmHelper to create the remote object.  I tried the same trick you used to put a ClientSponsor in the remote object, and the remote object still times out.  Clearly I can just create the remote appdomain manually like you did, but then I miss out on being able to use AsmHelper the way it's meant to be used.  I guess this means that AsmHelper has something in it that is timing out.  I've attached a modified version of your solution that uses AsmHelper.  It fails after 5 minutes.  Any ideas?  Sorry to be such a pain.  I'm sure you're getting tired of me, but I think getting this right will be a useful improvement to cs-script that others could benefit from.

By the way, I do have a couple questions about the license for cs-script.  I see that the license requires that the code not be modified without consent from the author. 
1. I have no need to modify CSScriptLibrary, but the binary distributions of the dll don't include the autocomplete xml documentation.  The xml documentation is the .xml file that gets built along side the dll which can be placed in the same folder with the dll in the C# project to provide the autocomplete documentation.  In order to generate this xml file I had to rebuild the CSScriptLibrary from the source.  I also built it for .NET 3.5 which it wasn't configured for initially.  Do these minor changes qualify as a "modification" even though I haven't changed the source code? 
2. I don't see the License block in CSSCodeProvider, so am I correct that this code is available for free and can be modified without consent?  I do need to modify this code because I'd like to add a 'css_include statement for VB.NET.  I suppose if this wasn't possible I could inherit from CSSCodeProvider and make a wrapper that handled the VB.NET, but I'd like to avoid that step if possible.

One last question: Do you have something like debugVS9.0.cs that creates a project file for VB.NET?  If not, I'll be working on that eventually.

I am getting close to completing my Script test application which has some pretty handy features (C# and VB.NET support including an Open script in Visual Studio button).  I'll attach it to this thread when it is complete.

Thanks again,

Joe

LifetimeManagement with asmHelper.zip

Oleg Shilo

unread,
May 31, 2012, 1:38:57 AM5/31/12
to cs-s...@googlegroups.com
The problem in you code was due to the fact that the lifetime of all remote objects (you have created proxies to) has to be extended. You have done it for the script instance but not for the AsmHelper internal remote object. 

Under normal circumstances you need AsmHelper only to make a call but not to keep it alive for a long time. However in your case you are doing just that. 

Out of box AsmHelper does not exposes the internal remote object so you have to use Reflection to get it and prolong its life. In the next release of CS-Script  AsmHelper will have RemoteObject read-only property. 


If you are not using AsmHelper then it is enough to prolong the script object only. This scenario is included in the sample as well (TestWithOutAsmHelper method).

Now your questions...

1) The CSScriptLibrary.dll xml documentation is already distributed with the binaries. Though for the .NET v3.5 equivalent you will need either to rebuild it by yourself or to use v4.0 (they are almost identical anyway). Eather way I have no problem with you doing this. It is not a code modification. 

2) CSSCodeProvider is not covered by the licence restrictions (but script engine only). Thus feel free to modify it any way you wish. 

Cheers,
Oleg 

jdun...@gmail.com

unread,
May 31, 2012, 5:26:51 PM5/31/12
to cs-s...@googlegroups.com, osh...@gmail.com
Oleg,

It appears you've solved this one.  I didn't know you could use reflection to get access to private objects, let alone remote private ones.  I think your solution would make a great example on your website or in the docs.  Also, I think a new version of AsmHelper would be in order with a public read only reference to AsmBrowser as you pointed out. 

By the way, for my test application I was passing a reference to my main form to the remote script to allow calling methods on the host from the script.  I found that I had to obtain a lifetime lease on the transparent proxy that goes from the script back to the host.  Here is what I had to do on the script in order to keep this proxy from timing out (my script inherits from ScriptBase).

    public class ScriptBase : MarshalByRefObject
    {
        //The following lines are required for script communication:
        public nsCSScript.myCSScript Host;
        ClientSponsor sponsor;

        public void Initialize(myCSScript myhost)
        {
            System.Runtime.Remoting.Proxies.RealProxy RealProxy = System.Runtime.Remoting.RemotingServices.GetRealProxy(myhost);
            object TransparentProxy = RealProxy.GetTransparentProxy();

            //Create a ClientSponsor on this object and the transparent proxy to communicate back to the host:
            sponsor = new ClientSponsor();
            sponsor.RenewalTime = TimeSpan.FromSeconds(6);
            sponsor.Register((MarshalByRefObject)TransparentProxy);
            Host = myhost;
        }
//...
}


Thanks for the information on your license. 

To avoid being one of those people that say "I'm working on xyz project and I'll post it when it's finished" and then you never see another post, I've decided to post what I have right now.  It is not complete and it probably has some bugs, but it is a decent example of what I'm trying to accomplish.  Perhaps someone else out there will be able to make use of this.  I am open for comments if you would like.
By the way, the Open in VS button only works for C# code right now.  This version has the Lifetime fixes as described above.
This supports VB.NET scripts with the css_include command.  (I put an extremely primitive VB parser in a customized version of CSSCodeProvider).

Thanks for all your help.

Joe

CSScriptTest x027.zip

Oleg Shilo

unread,
Jun 1, 2012, 8:12:36 AM6/1/12
to cs-s...@googlegroups.com
Hi Joe,

I am glad you managed to get the code working for you.

I have also published the HotFix containing the adjustment for the AsmHelper as well as the new extension method for convenient life time renewal. 

AsmHelper helper = new AsmHelper(...
helpwer.RemoteObject.ExtendLifeFromMinutes(6);

You can find the HotFix patch here: http://csscript.net/LatestBuild.html

Cheers,
Oleg Shilo

jdun...@gmail.com

unread,
Jun 1, 2012, 9:32:01 AM6/1/12
to cs-s...@googlegroups.com, osh...@gmail.com
Oleg,

Thanks for the hotfix.  I'm using the new version and it works great, though I haven't tried the new extension method.  Getting the scriptHelper.RemoteObject is much cleaner than the reflection based approach.

Joe

jdun...@gmail.com

unread,
Jun 1, 2012, 11:59:14 AM6/1/12
to cs-s...@googlegroups.com, osh...@gmail.com
Oleg,

By the way, I keep seeing you're setting the renewal time to 6 minutes.  Just so you know, the actual renewal time is irrelevant.  You could set it to 5 seconds if you wanted, or just omit setting the time at all (which will result in the default timeout of 5 minutes).  Basically what happens is once you register an object with a ClientSponsor, the ClientSponsor will automatically renew the object every time it is about to expire.  The period of this renewal is what is set by the renewaltime parameter.  When the ClientSponsor is closed it will no longer renew it's registered objects, and they will expire when the timeout runs out. 

You might have been aware of that, but I wasn't sure from all of your examples illustrating 6 minutes, so I thought I'd share.

Thanks again.

Joe

Oleg Shilo

unread,
Jun 1, 2012, 10:13:24 PM6/1/12
to cs-s...@googlegroups.com
The value 6 minutes was random. I just wanted it to be different to the default tiomeout (5 min).

But thanks anyway for the clarification.

Cheers, 
Oleg
Reply all
Reply to author
Forward
0 new messages