From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Method of tracking and closing ActiveX references in reverse order

Solved!
Go to solution

I am using a third party ActiveX dll.The object model of the application it controls is complex and to accomplish a task requires creating numerous references spread out through out different VIs.  I want to create a set of VIs to load (using Enqueue From Opposite End) and unload references to and from the LIFO. I plan to place the load vi following any place I create a reference. At the "end" of my code before exiting the vi I would call the unload vi. The unload uses the CloseReference vi to close all the reference in reverse order.  This is a straightforward LIFO implmentation. My problem is that I have many types of references and the LabVIEW Enqueue function require the datatype to all be the same.  So my question:  Is there a better way to store mixed data types in a LIFO structure?

0 Kudos
Message 1 of 10
(5,102 Views)

One possible option - is there a base class to every ActiveX reference? if so, that can be your queue type.

0 Kudos
Message 2 of 10
(5,096 Views)

If you don't have too many different types, perhaps you could store a cluster containing an enum describing the type and a variant? Then your unload could use a case structure to specify the type to the VTD conversion before calling Close Reference.

 

If each reference type only has one use, this probably isn't very helpful.

 

tyk's idea is definitely better, but I couldn't find a base class, so it might be impossible.


GCentral
0 Kudos
Message 3 of 10
(5,069 Views)

Unfortunately, there is only a common base class in some cases but not most. I had considered creating a cluster with the Ref type as an enum just as @cbutcher suggested.  It would work, and I may do this, but seems like there should be some better method to do ActiveX reference counting.

 

0 Kudos
Message 4 of 10
(5,049 Views)

Will this simple method work?

close activex.png

First it was ActiveX container with browser inserted (see LabVIEW example ActiveX Event Callback for IE.vi).

Generic conversion is ActiveX container on Front panel (nothing inserted). You can convert it to constant.

0 Kudos
Message 5 of 10
(5,040 Views)

An ActiveX refnum is simply a reference to an ActiveX object and those are based on Microsoft COM. Basically each COM object already does its own internal reference counting. So once you know that an ActiveX reference won't be needed anymore you should be fine to Close it in LabVIEW. If any other object has opened a reference to it internally it SHOULD have called AddRef(object) or more likely QueryInterface(object, ..) on that object itself before storing a reference to it internally. So even if you close the Page object refnum in the LabVIEW diagram, that does not mean automatically that the Page will be gone as long as you keep the Document object open. That should have created a Page object in the first place that you queried through some method and when LabVIEW retrieves the object refnum it makes sure to correctly AddRef(object) that object. Closing an ActiveX refnum in LabVIEW only means a call to Release(object) for that ActiveX object, and cleaning up its own refnum related bookkeeping. The COM Release(object) function decrements the internal refcount and when that reaches 0, the object is deallocated, but not earlier.

Now, correct ref count implementation in COM is pretty hard and there are quite a few ActiveX components who go pretty wrong in that. So there might be indeed a reason to keep a specific object refnum opem, in order for the parent object not trying to reference a discarded object later on. So sometimes its necessary to keep a refnum open in LabVIEW despite that you don't need it yourself anymore, but that should be the exception and is in fact a bug in the ActiveX component then.

 

Rolf Kalbermatter
My Blog
0 Kudos
Message 6 of 10
(5,029 Views)

@rolfk wrote:

An ActiveX refnum is simply a reference to an ActiveX object and those are based on Microsoft COM. Basically each COM object already does its own internal reference counting.

...

Closing an ActiveX refnum in LabVIEW only means a call to Release(object) for that ActiveX object, and cleaning up its own refnum related bookkeeping. The COM Release(object) function decrements the internal refcount and when that reaches 0, the object is deallocated, but not earlier.

 


This is really interesting to know - I'm a little confused by what COM objects are and how they work - they are 'installed' in the registry. Are their properties/methods also stored, or does reading the methods always involve 'loading' the object? (Apologies if I use the wrong terms - please correct me if they are confusing/obfuscating!)

 

When I right click an ActiveX container and choose to 'Insert' an object, what happens? NI documentation tells me (I think) that for controls that I load into a container, I don't need to call either the Automation Open or Close primitives. Their lifetimes are governed by the front panel's lifetime. Did I understand this right?

 

Conversely, when I want to use an ActiveX object without a container, I need to use the Open and Close primitives. If I open more than one reference, do I have multiple copies of the installed object? Or just multiple references to the same object? Is the same answer true if I open a reference to an object inserted into a container without the Open primitive?

 

My general understanding of LV references is anything I open, I should close, and things I don't open I should leave alone - but perhaps for third-party COM objects the rule is different?


GCentral
0 Kudos
Message 7 of 10
(5,013 Views)

COM stands for Component Object Model. It's the basis on which ActiveX is build. The "object server" is normally a DLL which gets registered in the registry under a GUID. When an application wants to create a specific object it needs to know this GUID (or alternatively the human readable name alias for this GUID that the component installer registered in the registry, which an installer might not do for internal object that it does not want other applications to use).

The COM system then loads the necessary DLL into the actual process, and instantiates (creates in memory) the required object. Each COM (and in fact ActiveX) object implements a basic interface that an application can invoke to discover the abilities of that object and also two functions to increment and decrement the internal refcount. When the refcount reaches 0, the object destroys itself and is from there on not existent anymore. So an application that receives an ActiveX object from somewhere and wants to store it somewhere for use later on, has to call the AddRef() method to increment the refcount. LabVIEW does this automatically whenever it creates an ActiveX refnum. The actual server component should do that also when it creates internal objects that it wants to keep alive, such as the previously mentioned page object that could represent a specific page in a multipage document. So when you call in LabVIEW a method or property that returns an ActiveX refnum, that might be a reference to an already existing object it the server component. LabVIEW creates an ActiveX refnum for this object and calls AddRef() to make sure that the object can't go invalid for the lifetime of the ActiveX refnum. Therefore in ActiveX you HAVE to close every refnum you receive from any method or property, not only the ones you explicitly open with the Autiomation Open function. This will call the Release() method to decrement the refcount for the object, so that when the component server itself closes its last reference, the object will be properly released. If you don't do that the ActiveX object that LabVIEW has in its refnum, will stay alive even if everything else has been closed until your LabVIEW program stops executing.

 

If you use an ActiveX container, the things are slightly different but not illogical either. Here by placing an ActiveX object into the container, the container does an implicit Automation Open for the object and maintains the object refnum internally. The object gets instantiated at load time of the front panel, which contains the container (or when you assign a specific ActiveX component to the container for the first time), and gets released as soon as the front panel is unloaded.

 

So the lifetime of an ActiveX component in a container is managed by the container itself internally. You should not use Close Reference on such an ActiveX refnum although I think it's not bad to do so. LabVIEW remembers internally if a specific refnum is dynamic and needs to be closed explicitly, and a refnum which is static and whose lifetime is maintained by some other entity, such as the frontpanel which contains the container. For such static refnums the Close Reference node is really simply an empty operation. So it's better to be safe and close a static reference anyhow if you are not exactly sure, than to forget to close a dynamic reference.

Rolf Kalbermatter
My Blog
Message 8 of 10
(5,005 Views)

@rolfk wrote:

Now, correct ref count implementation in COM is pretty hard and there are quite a few ActiveX components who go pretty wrong in that. So there might be indeed a reason to keep a specific object refnum opem, in order for the parent object not trying to reference a discarded object later on. So sometimes its necessary to keep a refnum open in LabVIEW despite that you don't need it yourself anymore, but that should be the exception and is in fact a bug in the ActiveX component then.

 


In my case I am having the opposite problem. I need to ensure the references are closed.  The 3rd party VIs that wrap the vendor's dll appears to not be closing their references despite the inclusion of an 'ExitApp.vi' that is supposed to close all of their references. This is evident because their Exe stays running in Windows Task Manager As such, I wanted to keep track of all of the references returned from their ActiveX dll calls and explicitly close them with the LabVIEW Close Reference vi. So the reason for my post - I am searching for a somewhat elegant way to track and close these reference, keeping in mind that the references are not all of the same type.  The structure is much like Excel, with workbooks, pages, sheets, column, row objects but with other object types too.

0 Kudos
Message 9 of 10
(4,976 Views)
Solution
Accepted by topic author EduNI

@EduNI wrote:

@rolfk wrote:

Now, correct ref count implementation in COM is pretty hard and there are quite a few ActiveX components who go pretty wrong in that. So there might be indeed a reason to keep a specific object refnum opem, in order for the parent object not trying to reference a discarded object later on. So sometimes its necessary to keep a refnum open in LabVIEW despite that you don't need it yourself anymore, but that should be the exception and is in fact a bug in the ActiveX component then.

 


In my case I am having the opposite problem. I need to ensure the references are closed.  The 3rd party VIs that wrap the vendor's dll appears to not be closing their references despite the inclusion of an 'ExitApp.vi' that is supposed to close all of their references. This is evident because their Exe stays running in Windows Task Manager As such, I wanted to keep track of all of the references returned from their ActiveX dll calls and explicitly close them with the LabVIEW Close Reference vi. So the reason for my post - I am searching for a somewhat elegant way to track and close these reference, keeping in mind that the references are not all of the same type.  The structure is much like Excel, with workbooks, pages, sheets, column, row objects but with other object types too.


Well yes, you need to close EVERY ActiveX refnum you receive through a LabVIEW method or property or that you open explicitedly by Automation Open. Otherwise that open refnum will keep the refcount of the ActiveX object greater than 0 and therefore it will never get deallocated until you shutdown your entire LabVIEW app. LabVIEW does autodispose all its refnums when the top level VI in whose VI hierarchy the refnum was created (or returned from a method or property node) goes idle but not before that!

But you should NOT need to keep ActiveX refnums alive and store them in some buffer for later cleanup unless you need them explicitly at a later moment. Simply close all the refnums as soon as you don't need them anymore and don't store them in some obscure buffer queue for some weird manual deallocation operation later on. That serves no purpose!

Basically you receive a refnum and do whatever you need to do with it and then you close it! The exception would be things like your application refnum or possibly individual document refnums etc that you need at some other time during your library operation, but these you will store somewhere (your LVOOP object private data for instance) as fully typed refnum, to use at a later moment to operate on that object again. As soon as you consider to typecast a refnum into some generic refnum to store it like that, you basically are doing something wrong! That is not how ActiveX (and COM) are supposed to work inside a strongly typed system like LabVIEW.

Rolf Kalbermatter
My Blog
Message 10 of 10
(4,950 Views)