LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Difficulty with 3rd-party .dll

I'm trying to incorporate a .dll which is the core component of a vendor-provided SDK to interface with these RFID scanners we're evaluating. The vendor provides working examples using this .dll in a few languages, and since I use Python actively for other projects I already tried that one and got it working. However, their header file is not straightforward to parse, and it seems the LabVIEW .dll wizard is flailing. 

 

(This SDK can be downloaded, apparently free of charge, though you're also supposed to buy a seat license to actually use it, so I don't think I am free to share the entire thing here. However, I think it's reasonable to share the header file so you can see what we're working with. If you know how to help me, but accessing the actual .dll and .lib files will make the difference, I'm sure we can figure that out.)

 

If I try to (roughly) follow the examples and simply use the Call Library Function Node, the program immediately crashes. I'll attach the .vi so you can take a look, though I suspect there are probably quite a few things missing from/wrong with this attempt (I wouldn't be surprised if I was told it was entirely wrong).

 

So I tried the wizard, and it does not seem to like that header file (not that I blame it in the slightest). Here's the error for the USBConnect function, which is the first call I need to make:

USBWEJAPI_API BSHRT  USBWEJAPICC usbConnect(void);

Undefined symbols can prevent the wizard from recognizing functions and parameters. To correct this problem, check the header file to determine if you must add preprocessor definitions. Click the Back button to return to the previous page of the wizard to add a preprocessor definitionsl (for example, "NIAPI_stdcall = __stdcall" or "NIAPIDefined = 1").
STATIC_API;USBWEJAPI_EXPORTS;__APPLE__;__GNUC__;__cplusplus;__linux__;__x86_64__;

The following header file was not found in the specified header file or one of the referenced header files:
-  windows.h

To fix, click the Back button to go to the previous page and add the header file path to the Include Paths list

I gather that it's not happy with the preprocessor definitions in that monstrosity of a header file, though frankly my experience with C does not extend this deep into preprocessor/compiler/linker territory, so I personally am at a bit of a loss as to exactly what I need to provide it that isn't already somewhere in that header file (or if it's a matter of reformatting some of that information into a more LabVIEW-parsable mode, what exactly that entails).

 

Also, if I explicitly add the complete path of Windows.h on my system to the "Include Paths" section, it still complains about windows.h being missing. (Maybe Windows.h is not the same as windows.h? At a total loss on that one, though it seems like the lesser of my problems at this point.)

 

I don't suppose anybody has any help or ideas on this one?

 

For reference, here's the minimal snipped of Python code which achieves the .dll call sequence I'm trying to replicate:

from __future__ import print_function
import ctypes
import sys
import platform
import os
from ctypes import *
import time

lib_home = "./lib/64"
lib_name = "pcProxAPI.dll"
pcprox_lib_name = lib_home + "/" + lib_name

if os.path.exists(pcprox_lib_name):
    if platform.system() == "Linux" or platform.system() == "Darwin":
        pcproxlib = ctypes.CDLL(pcprox_lib_name)
    else:
        pcproxlib = ctypes.WinDLL(pcprox_lib_name)
else:
    print ("\ndependency %s does not exist, RFI_DLL_HOME \
environment variable can used to set DLL home\n" % pcprox_lib_name)

pcproxlib.usbConnect.restype = ctypes.c_short
rc = pcproxlib.usbConnect()
if rc == 1:
    rawData = ""
    buffer_size = ctypes.c_short(32)
    pcproxlib.GetActiveID32.restype = ctypes.c_short
    # create a buffer of given size to pass it
    # to get the raw data.
    raw_data_tmp = (ctypes.c_ubyte * buffer_size.value)()
    #as per documentation 250 millisecond sleep is required
    # to get the raw data.
    time.sleep(250/1000.0)
    nbBits = pcproxlib.GetActiveID32(raw_data_tmp , buffer_size)

    bytes_to_read = int((nbBits + 7) / 8);
    # will read at least 8 bytes
    if bytes_to_read < 8:
        bytes_to_read = 8

    for i in range(0, bytes_to_read):
        temp_buf = "%02X " % raw_data_tmp[i]
        rawData = temp_buf + rawData
    print(rawData)
pcproxlib.USBDisconnect.restype = ctypes.c_short
pcproxlib.USBDisconnect()
Download All
0 Kudos
Message 1 of 10
(279 Views)

I don't have the library to test with, but can see you have made a mistake calling the USBConnect function in your VI because you haven't included the plDID parameter in your call library function node: short USBConnect(long* plDID)

 

That probably explains the crash because the function attempts to write to the plDID address, which you haven't set, so couldn't be anything and probably results in an access violation.

 

Regarding your problems with the Import Shared Library wizard, I too have experienced it failing and so quickly stopped trying to use it. Nowadays I always interpret header files myself, which has been more robust.

 

 

 

0 Kudos
Message 2 of 10
(172 Views)

@banksey255 wrote:

I don't have the library to test with, but can see you have made a mistake calling the USBConnect function in your VI because you haven't included the plDID parameter in your call library function node: short USBConnect(long* plDID)

 

That probably explains the crash because the function attempts to write to the plDID address, which you haven't set, so couldn't be anything and probably results in an access violation.

 

Regarding your problems with the Import Shared Library wizard, I too have experienced it failing and so quickly stopped trying to use it. Nowadays I always interpret header files myself, which has been more robust.


The Import Library Wizard seems like a God-send thing to the uninitiated LabVIEW user wanting to interface to an existing DLL, but it's not! It can NOT replace the experienced programmer who knows how to call a DLL in a C program, it can only make the monotonous creation of the VI wrappers a bit easier by doing much of the mechanical work and doing some of the more complex things of mapping C variable types to LabVIEW types. But the C languages is in many things pretty low level and ambiguous. What a specific parameter really is, is not just defined by its datatype but also some other attributes, including the original intention of the programmer who created that code. This intention MAY be communicated in the variable name or it may not. In either case it's not something that the Import Library Wizard could even start to interpret without creating even more uncertainty.

 

Basically, if you do not know how to call the API in a C program that you write yourself, AND how to configure the Call Library Node yourself too to be right, the use of the Import Library Wizard is more likely a hazard than a help unless you deal with extremely simple and self contained APIs that do not involve system headers and complicated parameters like pointers and structures.

 

And once you know how to do that configuration yourself the Import Library Wizard is not that interesting anymore at least for me. I hate the generic icons it generates and it can not deal at all with parameters in a C function that are related such as a pointer parameter and its related size parameter. Wiring them to individual control elements on the front panel is the absolute worst thing you can do for a LabVIEW library intended for other users as it requires specific knowledge about the API from the end user that a normal LabVIEW user is not expecting to have to deal with. LabVIEW arrays and strings inherently have their size as part of the datatype and your VI to interface to the underlaying C API absolutely must deal with the necessary translation between what a normal LabVIEW user is expecting and what the C API requires to do.

 

To the OP, I was seeing your post earlier including on LavaG but not having readily access to a machine with a newer version of LabVIEW than 2020, I could not checkout the code. So I refrained from answering. If you had downsaved the code to 2020 or 2019 I could have looked at it and given you some information.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 3 of 10
(153 Views)

I don't think it's that simple. Basically every function has two prototypes, one of which I think(?) is a callback (though I admit I'm lost in the syntax of the second set of prototypes). 

 

But if you look at the Python example code, the version of usbConnect which the sdk user actually calls does not require a parameter. 

 

FWIW, I almost never like or even bother trying to use anything described as a "Wizard". I only thought it might help since I seemed to have hit a wall with the Node. Needless to say, it did not help. 

0 Kudos
Message 4 of 10
(137 Views)

Not sure what you consider a callback parameter.

 

There are really two functions in that library:

 

USBWEJAPI_API BSHRT USBWEJAPICC USBConnect(long* plDID);

 

This is a function that uses the ASBWEJAPI_API declaration, returns a BSHRT and uses the USBWEJAPICC calling convention, AND takes a pointer to a long variable (but could be also a pointer to an array of longs, the C syntax is ambiguously unhelpful in this respect and only additional function documentation can clarify that).

 

ASBWEJAPI_API is defined to be __declspec(dllimport) when the header is used to link to an existing DLL. (it's differently defined when the header is used to compile that DLL)

 

USBWEJAPICC is defined to be equal to APIENTRY, which is a Windows define that is equivalent to extern "C" BOOL WINAPI which means that it is a global C function exported with standard C naming convention (no C++ name mangling) and uses __stdcall calling convention under 32-bit.

 

There is a second exported function

 

USBWEJAPI_API BSHRT USBWEJAPICC usbConnect(void);

 

which has no parameter but note that it is called usbConnect() not USBConnect(..)

 

The later function pointer definition is only valid if the header file is used for some obscure compilation mode that probably only has any relevance for the person who created that DLL.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 5 of 10
(128 Views)

I don't really "consider" it anything; I honestly gave up trying to parse those prototypes once I concluded they were probably "used for some obscure compilation mode that probably only has any relevance for the person who created that DLL" or thereabouts. They aren't used by my example so I was hoping I could simply ignore them. 

 

Ok, so I kind of forgot about the issue with the different cased names, which is dumb because that may be the core problem here. Right off the bat, I tried selecting "usbConnect" out of the list, however it always overrides my selection to "USBConnect". Whether I type "usbConnect" or select it from the list, it immediately replaces it with "USBConnect". 

 

Eventually I assumed it wanted it that way and kind of forgot about all that. But now that you bring it up again, I'm thinking, no, it needs to be the correct handle. And so the problem must be that it won't let me select the correct one (for some reason).

 

Any ideas on that?

0 Kudos
Message 6 of 10
(104 Views)

Are you using LabVIEW 32-bit? If so did you select stdcall calling convention? (there is no calling convention selection in 64-bit LabVIEW as 64-bit Windows always uses the fastcall calling convention for non-class functions.

 

Using two functions with different cases as only difference is highly unusual and also problematic. LabVIEW's Call Library Node does try to be helpful in remapping the entered name to the actually available ones (cdecl functions for instance have typically an underscore prepended, and stdcall functions have a @# appended to the name. The Call Library Node tries to alleviate the user of such trivialities. Apparently part of that is also to make a case insensitive match with the available functions. Not ideal but I would not see a quick way of circumventing that. So you may have to configure the Call Library Node to match the USBConnect() prototype.

 

The entire API of this DLL is for sure contrived and "unconventional" in several ways.

 

And those attribute(packed) statements at the beginning of all structs declarations makes for 99% of them not a yota of a difference, since the actual structure has no alignment differences no matter what packing the compiler would apply to them, not even when one would be creating arrays of those structs.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 7 of 10
(97 Views)

Well, that's fun. Problem is I have no documentation for the other functions, so I'll have to spend even more time trying to figure out what parameter to pass, with no guarantee it will ever actually work.

 

What's really fun is, while this "SDK" is pretty dumb in just about every respect and I'm basically convinced to go with the HID version of these devices simply to avoid dealing with it ever again, it actually works in every other language, trivially. 

0 Kudos
Message 8 of 10
(85 Views)

I've attached a modified version of your VI. Give it a run and see how you go.

 

When you tested adding the plDID parameter, did you ensure to pass it as a pointer to value?

0 Kudos
Message 9 of 10
(75 Views)

@marshaul wrote:

 

What's really fun is, while this "SDK" is pretty dumb in just about every respect and I'm basically convinced to go with the HID version of these devices simply to avoid dealing with it ever again, it actually works in every other language, trivially. 


That's a bold statement. Python is not EVERY other programming language. 😁

 

But trying to interface to a DLL without proper documentation is about as easy as trying to fly an airplane with a blindfold. You may get it in the air, but landing it will be a true challenge!

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 10 of 10
(38 Views)