NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

Storing Teststand ActiveX reference in LV shift register

Solved!
Go to solution

This is cross-posted here 

 

I am trying to store TestStand ActiveX references in an uninitialized shift register of a VI.  In my case, the references are passed into the VI from TestStand (not created from within the VI).  If I call the same VI in the very next step (same sequence and execution as the previous step), the ActiveX references from the shift register are invalid.

 

The VI remains reserved to run over the course of both steps and is not unloaded from memory, so the shift register data should remain intact (in fact, the numeric values of the references are in fact preserved).  Does LabVIEW still attempt to close any ActiveX references even if they weren't created from within the VI?  Is there a way around this issue?  Or am I just doing something wrong?

 

 

 

Message Edited by jsiegel on 01-13-2009 12:28 PM
0 Kudos
Message 1 of 13
(4,963 Views)

Hi,

 

I dont know why you want to do this when all you need to do is pass the SequenceContext in. Everything else can be obtained from the SequenceContext. There is no need to store them in LabVIEW. If you have to store any ActiveX references then store them in TestStand and pass them into the LV that needs them.

I suspect that its the PropertyObject that is invalid. What is the PropertyObject a reference of?

 

Regards

Ray Farmer

Regards
Ray Farmer
0 Kudos
Message 2 of 13
(4,945 Views)

In the initial call to the VI, I pass in RunState.Engine and ThisContext to the Engine and PropertyObject inputs, respectively.  The inital call to the VI results in a valid dialog box appearing; the second call (during which I retrieve the references from the uninitialized shift register), both the Engine and PropertyObject references are invalid ("Not a Refnum").

 

I don't actually intend to use the VI twice in the sequence as I described previously--that was just an example to illustrate the fact that the reference becomes invalid after the first call to the VI.  What I am trying to do is hold a reference to the model options object so that it may be accessed from other code modules.  What I am trying to do is what the Parallel Model UUT dialog CVI code (paralleluutdlg.c, used to generate modelsupport.dll) also does--it stores references to the engine, sequence context, and model options as user data in the panels.

 

Whether I need to use this scheme in the end or not, I would still like to understand why the references don't persist in subsequent VI calls.  There is some fundamental knowledge about ActiveX references that I don't understand, and that LabVIEW probably hides from me.  I have found that I can get the behavior I want by storing references obtained from subproperties of the input sequence context, rather than storing the input references directly.

Can you explain what is the difference between my initial and modified code?  I suspect it is related to something along the lines of passing by-value vs by-reference.  Thanks!

Message Edited by jsiegel on 01-14-2009 08:06 AM
0 Kudos
Message 3 of 13
(4,936 Views)
Solution
Accepted by jsiegel

jsiegel - 

In general, when code is passed a COM reference and the code wants to hold onto the reference in a global or shift register, even after returning from the call, the code must add a reference to the object so that the server of the object knows that the object must be not destroyed. It is also the responsibility of the code that is holding onto the reference in the global or shift register to release the reference to the object when it is no longer needed.  LabVIEW is no different than any other language.

 

So here is more details. When TestStand asks LabVIEW to run the VI, TestStand passes the reference as a paramater to LabVIEW server method to run the VI. COM creates a proxy to the reference and gives the proxy reference to the code module. Your VI then stores the value of the proxy reference in the global or shift register. When your VI completes and returns to TestStand, COM releases the proxy reference, so the value in the global or shift register is no longer valid.

 

Basically you need to add or duplicate the reference to the object passed into LabVIEW by calling the VariantToData function. Pass in the existing reference, set the type input to the same type of the reference, and the output will be the duplicate reference. You can assign the duplicate reference to the global or shift register.

 

Normally you must properly release the reference later by reading the value from the global or shift register, explicitly calling the Close Reference function with that reference, and then assign a Not A Refnum Constant to the global or shift register to invalidate it. In the case of a code module, I believe that when TestStand unloads the VI, that LabVIEW will release the reference properly. If that is not the case, you will get a unreleased object debug message when shutting down TestStand if you have that option enabled.

Scott Richardson
https://testeract.com
Message 4 of 13
(4,929 Views)

Scott, thanks for that explanation.  That helps a lot.  I think the bottom line is that if I want to store a reference input from TestStand, I should just duplicate it in LabVIEW using the VariantToData function.

 

If I understand correctly, in my modified code, the references I am storing are not proxy references generated for the call to the code module; instead, they are output from TS API functions.  And the reason that the stored references in the modified code persist between calls is because they are not released by COM when the VI completes.  This explains why my reference to the PropertyObject persists even though I didn't use the VariantToData function.  Is this all correct?

 

One question about the VariantToData function: when I use it to convert the variant output of the GetValIDispatch() method, am I not also making a copy?  If so, shouldn't I somehow be closing the variant version of the reference so I don't have two copies?  Or does VariantToData not make a copy when it is converting the data type?

 

Thanks

 

Jeff

 

0 Kudos
Message 5 of 13
(4,922 Views)

Yes. If you duplicate the reference, you are responsible to close it. A caller is required to release the reference passed to the called.

 

I believe that LabVIEW will properly release the IDispatch pointer contained in the variant when it goes out of scope. If you create a reference to a specific interface using VariantToData, you are responsible to release the reference. If you do not use the variant, I suspect that LabVIEW cleans this up.

Scott Richardson
https://testeract.com
0 Kudos
Message 6 of 13
(4,908 Views)

When does the variant go out of scope?  Immediately after the data flows through the wire and is "consumed" by the VariantToData function?  Typically, LabVIEW doesn't clean up a reference until the VI it is no longer reserved to run (and no other VIs are using the reference).  If that is the case here, it would mean that unless the TestStand step that calls the VI is set to unload when the step is complete, the variant reference may be active for a long time.  But there is no way (as far as I can tell) to close a variant reference without first converting it to a normal reference using VariantToData.

0 Kudos
Message 7 of 13
(4,891 Views)

jsiegel -

I was incorrect. When LabVIEW has an variant wire, the memory for that data is not freed until the VI leaves memory. We hit a similiar issue with event callbacks in UIs and the issue and the workaround is documented in the following KB: http://digital.ni.com/public.nsf/allkb/C340060ECAFAA169862573C900723541. So for your case you could call a VI to dynamically convert the variant to a reference and return the reference to the VI that needs it. When the dynamically called VI completes and is unloaded, the variant is released.

 

We hope to address the variant LabVIEW limitation in the future, and the TestStand group is tracking this issue with the following bug #101510.

 

However, why do you not just access the Engine property on the Sequence Context object directly? The Engine property will return a reference and not a variant.There is a lot of other specific properties on the Sequence Context interface that can be accessed without using AsPropertyObject.GetValIDispatch().

 

Scott Richardson
https://testeract.com
0 Kudos
Message 8 of 13
(4,882 Views)

jsiegel -

In the posting that you gave for the second diagram, it appeared that you were storing ThisContext on the shift register. Keep in mind that this object is only valid while the current running sequence is using it. Once the running sequence returns to its caller, the called sequence context is cleared. The engine caches unused contexts and reuses them, so if you accessed the context after the called sequence returned to the caller, the context might be cleared and still in the cache, or it might be used to execute a different sequence. In general holding onto a context in a code module is not recommended.

Message Edited by Scott Richardson on 01-14-2009 02:50 PM
Scott Richardson
https://testeract.com
0 Kudos
Message 9 of 13
(4,877 Views)

Scott,

 

Thanks, that's good to know.  Basically any place I convert an ActiveX reference as a variant to a regular refnum, I will have a memory leak.  I would expect this is a fairly common occurrence.

 

I am trying to recreate in LV the UUT serial number entry dialog code for the parallel process model, which is written in CVI.  I am just duplicating the CVI functionality in LV, and the CVI code stores those references internally (in the panels' user data).  Those references aren't used in many places, and I'm not really sure why the references can't just be passed as inputs from TS when they are needed.  I might try that later, but for now, I'm just trying to stay true to the CVI code.

 

I agree, accessing the Engine property directly from the Sequence Context object is a better way to go; but after my initial attempt at storing the engine reference passed in from the TS step failed, I followed the method used in paralleluutdlg.c (line 168 in TS 4.1).  The code also gets the sequence context from the "ThisContext" object of the PropertyObject passed into the function from TS, which is also "ThisContext".  Thus, the code is actually storing a reference to ThisContext.ThisContext.  It's a mystery to me why these methods were used instead of simpler ones.

0 Kudos
Message 10 of 13
(4,851 Views)