NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

How to set or retrieve a .Net reference from a property object

Hi!

 

I am trying to figure out how to set or retrieve a Csharp reference from a PropertyObject.

 

My obejct is created inside a .Net call that is inside a sequence ... but I need to pass the result back to the custom TestStand UI that I built.

 

I have tried

po.GetValVariant   (I get a null reference) 

po.GetValInterface   (I get an exception)

 

What is the right way to retrieve a reference from a property object?

 

Thanks

Tom M.

0 Kudos
Message 1 of 4
(3,923 Views)

You can use SetValInterface and GetValInterface, but there are some issues you will need to workaround.

 

The .NET adapter creates a separate appdomain in which it loads and runs assemblies. This is necessary in order to be able to unload them later. The only way to unload .NET assemblies is to unload the appdomain in which they reside. Thus:

 

1) Because your UI is in a different appdomain than the .NET code called from the .NET adapter, any objects you pass between them must either be derived from MarshalByRefObject or must be serializable (serializable will result in a copy being made). If you use MarshalByRefObject, which is nice because it allows you to pass the actual reference between appdomains rather than making a copy, you should also add the following override to your class object to give it infinite lifetime (Remote objects have timeouts for their lifetime. Not 100% sure this is necessary, but it sounds like it is from Microsoft's documentation):

 

        public override object InitializeLifetimeService()
        {
            // MSDN documentation for ILease:
            // "If the InitialLeaseTime property is set to TimeSpan.Zero, then the lease will never time out
            // and the object associated with it will have an infinite lifetime."
            System.Runtime.Remoting.Lifetime.ILease lease = base.InitializeLifetimeService() as System.Runtime.Remoting.Lifetime.ILease;
            if (lease.CurrentState == System.Runtime.Remoting.Lifetime.LeaseState.Initial)
            {
                lease.InitialLeaseTime = TimeSpan.Zero;
            }

            return lease;
        }
 

 

2) The .NET adapter is not currently aware of objects that are stored using SetValInterface thus it does not maintain the lifetime of the appdomain for such objects. Thus it's possible the execution appdomain could get unloaded out from underneath an object you store using SetValInterface from a .NET module. There are two ways to workaround this.

     a) Create the object in the UI's appdomain rather than from a .NET step module. The .NET step will we able to access it using GetValInterface and since your UI's appdomain doesn't ever get unloaded you can know it's safe to keep it in the teststand variable as long as you would like.

     b) I'd strongly recommend solution 'a', but if you really must create the object from your .NET step's module, you can keep the .NET adapter from unloading the execution appdomain by letting the dotnet adapter store a reference to a .NET object in an station global. In order to do this you must pass back a .NET object from a .NET module call either as a return value or an output parameter. It doesn't matter what the object is as long as it's a .NET object and not a COM object. This will keep you from being able to unload .NET modules though (and thus from rebuilding the dlls) until you release the .NET object stored in the global. Also you cannot currently get a .NET object stored by the adapter using GetValInterface. You must use SetValInterface for object's you want to get back using GetValInterface.

 

Hope this helps. Let me know if you have any questions related to this or need something explained in more detail.

-Doug

Message 2 of 4
(3,904 Views)

Hi Dug,

 

Thanks for the great answer.  I hope to try it shortly.  My workaround was in fact to serilalize the object to XML and pass it via a String, and deserialize it on the other end.  Not elegant, but it seems to work.

 

I think I should be able to do a), but I wonder if it will work considering the object itself has references to other obects.  At best, I could create the top level object in the UI AppDomain but it's up to my csharp dll to populate the members of that object ... so the creation would then be hybrid .... partly in the UI app domain and partly in the step's app domain.

 

Regards,

Tom MacLean

0 Kudos
Message 3 of 4
(3,898 Views)

Yes, if you are creating objects in both appdomains that are MarshalByRefObject and storing them as a hybrid of sorts then you still have the issue where the .NET adapter doesn't know that you are holding onto objects from the execution appdomain and thus might unload the appdomain before you are done with them, so you might want to go with solution b) instead. Or, if you don't mind making copies of the objects instead you can use the [Serializable] attribute on your classes and they will be serialized (i.e. copied) across appdomain boundries instead.

 

-Doug

0 Kudos
Message 4 of 4
(3,894 Views)