LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Call library function not finding DLL in directory where my LLBs are

Solved!
Go to solution

I am using LabVIEW 8.6.  I have a set of VIs in several LLBs.  All the LLBs are in one directory.  Most of my VIs are wrappers for the functions in one DLL.  I was told to put my DLL in the directory where the LLBs are, and supposedly this is how the previous programmer worked (using an earlier version of LabView). 

 

In the Call Library configuration, I have specified <dllname>.dll without a path.  (This is how we want it as our VIs are an API which other programmers will integrate, so I don't know where they will put things and I can't use any absolute paths).

 

When I load the VIs in LabVIEW, LabVIEW can't find the DLL and asks me to located it.  It is right there in the directory with the LLBs and when I double click on it everything works fine.  However my absolute path to the DLL now appears in the Call library configuration, and we don't want that.

 

Does anyone know how to make this work?  I would assume that the location of the VIs (or LLBs, in this case) would be the current directory and thus Windows would look there for the DLL.  However, it seems that this is not the case (at least, in the current version of LabVIEW).

 

Thank you.

Batya

0 Kudos
Message 1 of 12
(7,044 Views)

Batya,

 

Will your .dll always be in the same relative path to your VI?  If so we can build a relative path to it.  I even already have a VI built that will do this for you with minimal tweaking. 😉

ColeR
Field Engineer
0 Kudos
Message 2 of 12
(7,024 Views)

As far as I know it would be the same relative path, unless it is in the windows system directory, in which case it loads ok since it is in the regular DLL search path (but we don't want to mess that up). 

 

In the case where we have not installed the DLL in the windows system directory, I just wanted to do what I would do normally; put the DLL with the rest of my files, in the same directory.  It seems, though (which is counterintuitive for me) that the directory of my LLBs is not in the DLL search path unless I manually add it -- it is not the current directory or the directory of the current executable or any other such thing. 

 

I was hoping that LabVIEW would have an option or a setting on the VI or the call library node which would automatically look for the DLL in the same directory as the LLB, but I could not find it.

 

If you have a VI which would do it, I would be interested in seeing it.  I would much prefer, though, a general setting, since we already have several different libraries each with a dozen or more functions!

 

Thank you.

Batya

 

0 Kudos
Message 3 of 12
(7,001 Views)

BPerlman wrote:

I was hoping that LabVIEW would have an option or a setting on the VI or the call library node which would automatically look for the DLL in the same directory as the LLB, but I could not find it.


I think you are overanalyzing all this. While the path in the call dll node seems absolute, LabVIEW will actually load the dll from where it makes sense.

 

For example If you move your VI and dll to a new folder, and open the moved VI, the path in the call libary node will have magically changed to the current folder. It will not even report a changed loading location. Similary, if you now move the dll into a subfolder from where the VI is located, the call library function will still automatically find it but tell you that it was loaded from elsewhere.

 

If you specify a dll with only the filename (no absolute path), the dll needs to be in the search path of the system so you typically don't want to do that.

 

I searched around a little bit but could not find a very clear description on how dll paths are handled in terms of search locations and their relative priorities. Maybe somebody from NI could chip in and provide the definite answer. 🙂

Message Edited by altenbach on 03-08-2009 12:56 AM
Message 4 of 12
(6,997 Views)

Altenbach has summarized it up already. The DLL name alone tells LabVIEW to let Windows resolve everything. If you point it to the DLL, the path displayed is absolute but it stores it internally relative to the VI as long as both are on the same drive. And LabVIEW will normally search the subdirectories of such a location too, and once it finds a file with that name, it passes the absolute path to Windows. Windows does not attempt to load a DLL from the search locations when the caller specifies an absolute path but instead fails if the DLL could not be loaded for some reasons (invalid DLL, missing dpendant DLLs, etc). If LabVIEW can't load it that way or can't find it with an absolute path it tries it once more just with the DLL name to let Windows resolve it if it is in one of the search paths. If this also fails you get the broken arrow.

 

Rolf Kalbermatter

Rolf Kalbermatter
My Blog
Message 5 of 12
(6,984 Views)

Thank you altenbach and Rolf Kalbermatter.  You have helped me to understand how the paths in call library work.  I tried your suggestions and saw them work.

 

I have another problem now.  I have a different post on the forum looking for a way to use the same VI with two different underlying DLLs.  As suggested by those who answered my post, I chose the "specify path on diagram" option and read the DLL name and path from the registry.


For my usual situation it works fine. 


However, there is one case which is not covered -- and I am getting results which I completely do not understand. 


I was hoping to combine the two features (specify path on diagram and finding the DLL in the search path if it is not found where specified).  I did not know if it would work, so I tried it as an experiment.  What I found is very puzzling.  It seems to load the DLL (and tells me the DLL is loaded from a new path) when I open the VI.  Indeed, if I then try to delete or overwrite the copy of the DLL in the windows system directory I get an error from Windows that it is in use.  So it looks as if it has properly loaded the DLL from there.  I was happy -- maybe I'll have a beautiful solution!

 

However, when I run the VI, it looks like it is running properly, and the call library function returns 0 (which is the value I expect).  However, if I use that value (which is supposed to be a handle) as input to another VI it is clear that something went wrong.  So I passed an incorrect input to my VI and ran it.  I did not get the expected error return -- it again looked as though it ran properly and succeeded.

 

I did some other debug which I'll skip here....then made a version of the DLL in which that function ALWAYS returns an error, right away when called.  Still, the call library function node returns zero as if everything had gone fine; but now zero is NOT an expected value.

 

So it looks as if it is somehow loading the DLL and not calling it properly, but failing to inform me of the matter.

 

I had a similar effect in another situation.  In that situation the called function was "crashing" (accessing a bad pointer).  But here I am not managing to execute the first line of code in the function.  Also, in this case, the same VI, same input, and same DLL work fine when the DLL is "found" differently (either in the expected directory or with the path not specified on the diagram).

 

Any one out there who can help me?  (Please keep in mind while answering that I am a newbie and won't necessarily understand the implications of an answer with some of the information implied.  I'll get there 🙂 )

 

Thank you.

Batya Perlman

 

Message Edited by BPerlman on 06-07-2009 03:21 AM
0 Kudos
Message 6 of 12
(6,794 Views)

When you specify that the CLN should load the DLL LabVIEW does pure dynamic loading. It remembers the previous path in order to cache an already loaded DLL but on loading of the VI the DLL always needs to be loaded on the first run. And this is where you run into your problem. The DLL is only attempted to get loaded on the first run, not on loading the VI.

 

The CLN also got an error terminal since then and when it fails to load the DLL and find the function to call, it returns default default values for all wired parameters (can't do without, as the data flow paradigma mandates all functions provide values for all outputs that are wired) and it ALSO outputs an according error in the error cluster output.

 

So you do need to do proper error handling in your CLN if you use the dynamic load feature. For the static load feature error handling was unneccessary since the DLL and function could either be located or not in which case the entire VI got a broken arrow.

 

Rolf Kalbermatter

 

Message Edited by rolfk on 06-07-2009 11:16 AM
Rolf Kalbermatter
My Blog
Message 7 of 12
(6,789 Views)

Thank you Rolf Kalbermatter!  I did not know that the difference in how the path was specified translated into dynamic and static loading.  Especially since even when the path is specified on the diagram it prompts you, on loading the DLL, to find the DLL if it can not, just like the other way, and gives a broken arrow if you don't specify it.  So I had no idea.  It explains a lot.  (By the way, there is an "error out" output there even when not specifying the path on the diagram, so I didn't know that its use was related to the change.)

 

I now handle the error and I don't get any confusing behavior.

 

Does this mean, though, that what I want to do (use "specify path on diagram" but if the DLL is not found there find it in the Windows search path) can not be done?  (I guess I could check the output of the call library function node and try again with just the DLL name stripped of path if it failed, but repeating the call library function node is a maintenance headache as if I have to change anything going in or out I have to do it twice for each VI...and someone else touching the code might not even realize they had to!)

 

So, can I do it somehow or not?

 

Also, this is getting very complicated.  I really want to clean up our use of DLL path and DLL name, but the change once it works has to be duplicated in something like 200+ VIs!

 

Is there a simpler way?  Or a way to automate the change?

 

Thank you again for all the help.  This forum is a lifeline for a newbie isolated LabVIEW programmer!

 

Batya Perlman

0 Kudos
Message 8 of 12
(6,782 Views)
Solution
Accepted by topic author BPerlman

Well somebody using your library should not have to dig into your VIs and do all this on his own. Instead your library should wrap that and hide the troubles of this entirely.

 

The error cluster was added when the dynamic path option was added. It is not useful to hide that error output so it is always there. Together with the dynamic path there was improved error handling added to the CLN. One of them is that the level of error checking during the function call (exception handling) can be specified. I would assume that some of those options can generate an error code instead of popping up a dialog as they did before and for that the error code output can be useful even in the static call case.

 

As to what you want to do, I would long ago have handled that with a wrapper DLL that has basically the same functions as your other DLLs and some initiliasation function that returns a pointer to a function dispatch structure based on the actual DLL you want to call. Quite like what an object oriented function dispatch table is. Then when initilising your interface you call that initialise function and specify the interface/device type you want to use and after that all the other functions take one additional function dispatch pointer parameter as first parameter in addition to the parameters the actual function has. This function dispatch pointer would be just a pointer to a structure containing the table of function pointers for that interface and for the sake of LabVIEW would be simply a pointer sized integer.

 

The wrapper function then verifies the function dispatch structure pointer for validity and calls the actual function with the remaining parameters. This is some C programming and might require some planning and desigining of the different interfaces to facilitate this kind of dispatch technique but it will for sure pay of in the long run, and make your library even usable in earlier LabVIEW versions, as well as VB etc. without tricky dynamic loading in the high level programming environment.

 

Rolf Kalbermatter

Rolf Kalbermatter
My Blog
Message 9 of 12
(6,778 Views)

rolfk wrote:

Well somebody using your library should not have to dig into your VIs and do all this on his own. Instead your library should wrap that and hide the troubles of this entirely.

 That is EXACTLY what I want to do.

 

The error cluster was added when the dynamic path option was added. It is not useful to hide that error output so it is always there. Together with the dynamic path there was improved error handling added to the CLN. One of them is that the level of error checking during the function call (exception handling) can be specified. I would assume that some of those options can generate an error code instead of popping up a dialog as they did before and for that the error code output can be useful even in the static call case.

 

As to what you want to do, I would long ago

 

I would long ago have done a lot of things differently on this project -- but no one asked me then 🙂  (Good thing it wasn't me, or maybe I'd have taken offense 😉  )

have handled that with a wrapper DLL that has basically the same functions as your other DLLs and some initiliasation function that returns a pointer to a function dispatch structure based on the actual DLL you want to call.

 

OK, I am getting the feeling that this will be a brilliant and elegant solution when I fully understand it.  I understand what a wrapper DLL is.  But do you mean that each version of my product would include a different wrapper DLL (with the same name so that CLN would always work), or the same one and it would somehow be told which actual DLL I want to call?  Who would decide which DLL, the VI or the wrapper DLL?

 

By a function dispatch structure do you mean a table of function pointers gotten by GetProcAddress, one such entry for each function?

 

Quite like what an object oriented function dispatch table is. Then when initilising your interface you call that initialise function and specify the interface/device type you want to use and after that all the other functions take one additional function dispatch pointer parameter as first parameter in addition to the parameters the actual function has. This function dispatch pointer would be just a pointer to a structure containing the table of function pointers for that interface and for the sake of LabVIEW would be simply a pointer sized integer.

 

The wrapper function then verifies the function dispatch structure pointer for validity and calls the actual function with the remaining parameters.

 

I think I understand what the wrapper functions need to do.  But I am missing some pieces for the initialize function.  It would have to do LoadLibrary and lots of GetProcAddresses -- that much I get.  But I still don't understand who decides which DLL.  Maybe you mean that the wrapper DLL's initialize function will get the DLL name and path from the registry, load it, try a different path if the load fails, etc.  Is that what you mean?  That could work very nicely.   But from where would it get the registry key?  From the VI?  This is the part that would clearly be different for each product.  I suppose that you had something in mind when you described your solution. 

This is some C programming

 

Great!  That's what I do for a living.  At least I know how to do that!  Hey, I should have thought of this idea! 

and might require some planning and desigining of the different interfaces to facilitate this kind of dispatch technique but it will for sure pay of in the long run, and make your library even usable in earlier LabVIEW versions, as well as VB etc. without tricky dynamic loading in the high level programming environment.

 

Ah, see how much I don't know in LabVIEW?  I didn't know this was a new feature...

 

Thank you for the great idea.

 

Batya Perlman

0 Kudos
Message 10 of 12
(6,755 Views)