LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

multiple call library function nodes with different paths

Solved!
Go to solution

Hello out there,

I'm using a DLL to connect to a camera. Now I try to connect to two cameras simultaneously, but unfortunatly the DLL can only handle one.

 

Now I have a workaround succesfully implemented:

  • Created two copies of the DLL A and B
  • Created two Libs A and B with Wrapper VIs which call the functions of the DLL, one points to DLL A the other to DLL B
  • Created a Test.vi where I can connect to both cameras and call getImage() in a loop

Well, that is not nice. I want that to be more dynamic. I don't want to have multiple librarys which do the exact same job, but are only linked to different DLLs. So I

  • Created new Lib D
  • There the Wrapper VIs which call the functions of the DLL have a path input for the Call Library Function Nodes
  • in the CLFN there is no name or path specified
  • Created two copies of the DLL A and B in Lib D Folder
  • Created a Test.vi which crashes Labview 😕
  • Here I bundle the paths to both DLLs in an array and iterate them in a loop

 

DllPath.PNG

 

 

The first iteration of the loop is okay, but in the second LV dies, it just dissapears or hangs forever. As the paths are correct and the first approach is working, it seems to me, that it is not a DLL-Problem, but a LV one. Like caching or something...

On the other Hand: When I call a Terminate() in the first iteration, the second iteration works fine. So maybe an issue with simultaneity? But all VIs are configured to execute non-reentrant, CLFN run in UI-Thread. So everything should run sequential.

 

Any hints or suggestions?

 

Using LV 2019.0f2 (64bit)

0 Kudos
Message 1 of 9
(1,580 Views)

Most likely your DLL doesn't like to be unloaded while having resources opened. And there is nothing you can do about that other than making sure that every resource is properly closed before you initiate a new loop with a different DLL name. Every Call Library Node will check the incoming path and if different to the previous, free the library handle and load the new library.

Once no Call Library Node has a handle left to a library Windows will unload it and if the library is not diligent about properly cleaning up on unload it can crash.

So if this is the problem with your library you will need at some point in your program a call library node for each DLL that is holding a reference to the individual DLL.

Rolf Kalbermatter
Averna BV
0 Kudos
Message 2 of 9
(1,549 Views)

Hello rolfk,

 

thanks for your response.

 

@rolfk wrote:

And there is nothing you can do about that other than making sure that every resource is properly closed before you initiate a new loop with a different DLL name.

 

As I need to have a connection to both cameras at the same time, I cannot close the connection with the Terminate() function call.

 


@rolfk wrote:

Every Call Library Node will check the incoming path and if different to the previous, free the library handle and load the new library.

Once no Call Library Node has a handle left to a library Windows will unload it and if the library is not diligent about properly cleaning up on unload it can crash.

So if this is the problem with your library you will need at some point in your program a call library node for each DLL that is holding a reference to the individual DLL.


I don't know if I understood that right. Is there a way of getting a reference to the library handle out of the CLFN? Then I could use it later on...

I only have the path to the DLL. The DLL itself doesn't offer me a reference to a specific camera.

 

How can I prevent the CLFN to free the library handle?

0 Kudos
Message 3 of 9
(1,538 Views)
Solution
Accepted by topic author AlexElb

The reference is held by each Call Library Node instance. That is why it works in your first case since you have for each camera individual VI instances that hold onto the DLL handle.

 

Just make sure you have at some point in your program before you do any dynamic camera calls, a call to some function in your DLL with an individual VI for each DLL name.

Rolf Kalbermatter
Averna BV
Message 4 of 9
(1,529 Views)

@rolfk wrote:

Just make sure you have at some point in your program before you do any dynamic camera calls, a call to some function in your DLL with an individual VI for each DLL name.


Wow, easy solution: I've build a InitLibD Vi which calls DLL A and B with two CLFN statically binded and called that at the beginning.

Thank you very much for the quick help!

0 Kudos
Message 5 of 9
(1,518 Views)

@rolfk wrote:

Once no Call Library Node has a handle left to a library Windows will unload it


 

Just a short update: After loading was sucessfull, I had to reread this 🙂 Since I use the same Wrapper VIs for different DLL-calls I cannot unload them individually. Because every wrapper VI could have a handle left, I must call them all with an empty path to unload the DLL.

 

UnloadUnload

 

The lower Picture shows the VI used to load and unload multiple DLLs.

With the Load Action it will pass two DLL Paths into two CLFN.

Then in my programm all wrapper VIs can be uses with one of thwose DLL Paths. LV won't unload the DLL because the two CLFN in the lower picture still hold a handle to the dll.

When my programm wants to unload all DLLs it calls that VI with the Unload Action, which first calls the VI in the upper picture:

All wrapper VIs are called with an emtpy path, so that none CLFN in them has a handle to the DLL.

Finally the two CLFN which initially loaded the DLL get called with an empty path, so no CLFN with a handle to the DLLs is left and it will be unloaded.

 

I thought I document here some more information because it was a bit tricky, at least for me. May be interesting for other users, too?

 

Other solutions welcome 🙂 It would be nice to load and unload one dll without interfering with the others, but I don't see that to be possible...?

0 Kudos
Message 6 of 9
(1,032 Views)

Actually since it is the instance of every Call Library Node that maintains a handle to the DLL there might be an easier way to initially reserve the DLLs.

 

Instead of using a Call Library Node that calls into some (dummy) function and then later needs to be called again at a different location, you could also simply call the Windows LoadLibraryA() function in the beginning of your program and FreeLibrary() at the end for each DLL. Note that this is only for the initial loading of the DLLs to make sure they stay in memory even though you call later the Call Library Nodes with different paths so each instance will load and unload the respective DLL on each call! Yes that is what happens. Each Call Library Node will check if the new passed in path is the same as the previous one. If not it will FreeLibrary() the internally stored handle (if valid) and load the new path with LoadLibrary() and then use it.

 

While this is only a small overhead on each call because FreeLibrary() on a library whose refcount hasn't reached 0 is almost a NOP and LoadLibrary() on a library that is already loaded in memory is a minor overhead it is of course some overhead that you have to be aware off. Also the comparison of the passed in path with the internally stored one is also something you shouldn't completely forget. All in all it will probably be a few extra microseconds per Call Library Node call.

 

Also this whole thing will fatally go wrong if you ever happen to set any of the VIs in which a Call Library Node is placed to be reentrant (clone or shared clone). Because each instance of a reentrant VI has its own data space so each Call Library Node inside such a reentrant VI will be a separate instance for each use of the reentrant VI.

 

The big question is: Do you really need to make sure all DLL references are cleared explicitedly? Because if you unload your VIs each Call Library Node will free its maintained handle anyhow. Same when you build an executable and the program terminates.

 

WINAPI Shared Library.png

Attached VIs are back-saved to LabVIEW 2009 for the forum users maximum benefit.

Rolf Kalbermatter
Averna BV
Message 7 of 9
(1,023 Views)

Hey Rolfk,

 

thanks for the additional information. I am not shure if I got your suggestion correctly, did you mean:

  • have load & unload VIs with kernel32 function LoadLibraryA() for loading and FreeLibrary() for unloading my DLL at the beginning and end of my programm
  • have all wrappers VIs which call my DLL using FreeLibrary after the call?

In that case each CLFN, which would all be in my wrapper VIs, would never have a handle after it has run. So I wouldn't have to call every Wrapper VIs with an empty path at the end of my programm. That sounds nice.

Also the load & unload VI could be a simple for loop - no need to have e.g. 8 CLFN when using 8 DLLs. Sounds nice to me again.

 

Looking at the overhead I understand, that "FreeLibrary() on a library whose refcount hasn't reached 0 is almost a NOP". But in the Wrapper Vis I would neither need to call LoadLibrary() nor compare it input path. As I understood, that is the standard behaviour for a CLFN, so that wouldn't make any difference....?

 

Couldn't I avoid using CLFN in the Wrapper VIs with the path to the DLLs? The kernel32 function LoadLibrary() returns a handle to the dll, so I have a reference. Is there a function in kernel32 that could call the functions in the dll? Than we could bind all CLFN in the Wrappers to kernel32 and use that with the DLL-reference. That way the overhead in the Wrapper VIs would be minimal. Probably not possible when I think about parameter handover.

0 Kudos
Message 8 of 9
(989 Views)

The idea is to use the Loaibrary once in the beginning of your program for each DLL instead of those dummy Call Library Nodes and free them at the end?

 

No FreeLibrary in tbe Vi’s containing Call Library Nodes that could mess up Windows refcounting and unload the DLL when LabVIEW believes it’s dtill valid and -> crash!!

The explanation I gave you is what LabVIEW does internally in the CLN when you pass a path to it.You can’t chance that in any way!

 

 

There is only GetProAddress() which returns you a function pointer. LabVIEW has nothing that can be used as function pointer. Creating it yourself is not possible in LabVIEW as you need to setup a stack frame, copy the parameters into the stack (and CPU registers for 64-bit code) and then jump to the function pointer. Only doable directly on CPU assembly level yourself. A C compiler can create this code and LabVIEW does it internally in the Call Library Node for you but you can’t do it yourself in LabVIEW, that’s several layers below the abstraction level of a LabVIEW diagram!

Rolf Kalbermatter
Averna BV
0 Kudos
Message 9 of 9
(984 Views)