LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Trouble with IMPORT SHARED LIBRARY wizard

The header file is included in the zip I provided:

 

#include "extcode.h"
#pragma pack(push)
#pragma pack(1)

#ifdef __cplusplus
extern "C" {
#endif

/*!
 * Add_Calculate
 */
double __cdecl Add_Calculate(double X, double Y);

MgErr __cdecl LVDLLStatus(char *errStr, int errStrLen, void *module);

#ifdef __cplusplus
} // extern "C"
#endif

#pragma pack(pop)

 

I think the difference is that I perhaps chose a different "calling convention" in the VI prototype to you? Did you choose 'standard'? I think I left it at default which is 'C Calling Convention'?


LabVIEW Champion, CLA, CLED, CTD
(blog)
0 Kudos
Message 31 of 34
(1,944 Views)
One major difference is that Sam_Sharp is adding doubles, which are well-defined, and Steve is adding ints, which are not.

Calling convention is not the problem. That only make a difference at run time, not while importing.
Message 32 of 34
(1,932 Views)

There are a lot of issues coming up in this thread. I try to address some of them, although I'm sure there are some that I'm simply forgetting:

 

As to incorporating DLLs into LabVIEW through the Import Shared Library Wizard (ILW): This tool is not a magical thing despite its name making some people to believe otherwise. It works on a C header file which never was intended to create a fully descriptive interface definition for a C library. Nevertheless it is the best that is available in terms of C definitions that can be used by all C compilers. Nothing else has ever been really brought up and much less garnered any kind of noticable backing by any C compiler builder!

 

As such the header file generally requires certain C Preprocessor definitions. Here again no standard exists as to what preprocessor definitions should be used. Therefore the Import library Wizard can't really predefine these as if it uses the Microsoft C preprocessor definitions it will catastrophally fail to parse headers written for Borland C, GNU C or any other C compiler, and vv. It would even go pretty haywire if it would use Microsoft C 32 Bit preprocessor defintions but you are trying to import a 64 Bit DLL.

 

The only thing that is more or less universally accepted are the basic datatypes char, short, int, long, float, double and void as well as generic syntax elements like typedef, enum, struct. These are the datatypes and definition that Import Library Wizard knows out of the box. Anything else is always defined by compiler specific headers that come with the compiler. LabVIEW defines its datatypes in terms of the defintions in extcode.h in the cintools directory. But this header file is written to work for MSC on Windows, Xcode on Mac, and GCC on Unix platforms. In order for the header file to use the right definitions the preprocessor needs to know at least the platform defines, for Windows that would be at least _WIN32 and optionally _WIN64 as well as the architecture defines _M_IX86 or _M_IX64. With these two or three defines the Import Library Wizard could theoretically parse extcode.h and friends but will generally still fail if you do not also have at least some basic Windows SDK headers installed on your system, and tell the Import Library Wizard where it can find them! Basically the Import Library Wizard is a C proeprocessor and needs the according platform header files for the import to work with anything but trivial header files. However LabVIEW is not delivered with those headers as it is not really a C development environment and would make distribution of LabVIEW a lot more complicated.

 

It will work for trivial DLLs that only use datatypes in terms of the standard C datatypes char, short, int, long, float, double, enum, struct and void, but requiire substantial handholding from someone who knows how C compilers generally work for more complex DLL interfaces.

 

You could argue that the Import Library Wizard should be able to import LabVIEW generated DLLs. However while this could be made to work it is the least useful feature of all, as creating a DLL in LabVIEW to be called in LabVIEW is about the worst option in almost any departement of code development. So I rather have NI spend their time in other areas of LabVIEW than making this pretty useless use case seamless to work with. 

 

As to PPL, they do indeed solve this problem in a much better way than direct dynamic VI invocation or DLL interfaces, but have one disadvantage that they need to be compiled in the same LabVIEW version than the calling application. A PPL is a binary precompiled version of the VI and can't be loaded into a different LabVIEW runtime version and platform than what it was created in.

 

As to LV Refnums, they all seem to be still 32 bit magic cookies even in the 64 bit version of LabVIEW. However it is important to match the defintions on both sides (the export definition when building the DLL as well as the import definition when importing the DLL). LabVIEW supports passing native data to a shared library. For that you have to configure the parameter to "Adapt to Type". It does mean that any data is always passed to the DLL as reference so the parameter is really "LVRefNum *refnum". When you have a refnum in a VI interface and define in the Shared Library Build that this refnum needs to be part of the function interface, this is also how the parameter will be defined in the shared library interface. But the Import Library Wizard clearly has not been developed to import LabVIEW shared libraries. It will always attempt to map LabVIEW datatypes to generic C datatypes resulting for instance for a path into an empty cluster, since that is how the C datatype for a path is defined. A LabVIEW Path is definied as a pointer to an opaque memory location that the user of the API should not even attempt to interpret. Internally it is a pointer to a pointer to a fully defined structure that contains some flags, some lenght information and then the path components in a platform independent format. But by making it opaque NI is free to change the internel memory layout anytime they wish too, although they haven't really done that since the implementation of the Path dataype in LabVIEW 2.5! Smiley Very Happy But it is generally a good idea to hide the implementation details of data structures from the caller.

 

Attached is an example project that shows how a LabVIEW created shared library can use native LabVIEW datatypes and how to call them from a LabVIEW VI. However a few points should be noted:

 

1) This example as trivial as it seems shows once more how very much complicated this all is. 
2) The Import Library Wizard is not made to understand LabVIEW specific datatypes and will not allow them to be directly selected in the import steps. Instead it will attempt to map them to generic C definitions in terms of the C declarations of those datatypes. This may seem useless, but so could be argued is the whole point of trying to interface from LabVIEW to a LabVIEW specific DLL. A DLL defined with such an interface can't be called from anything but LabVIEW itself, since no other entity will understand how to treat a LabVIEW Path properly (or a LabVIEW file refnum, or any other refnum or even a simple string handle in terms of trying to resize it). In fact these things, except the string handle, with lots and lots of magic in terms of loading DLLs won't even work if the DLL is compiled in a different LabVIUW version than the LabVIEW version in which you try to load the DLL.

 

The attached archive contains the project for the DLL as well as the project for the shared library import VI. There are two VIs "Return File Refnum.vi", which is the one created by the Import Shared Library Wizard and very much non-functional and the "Return File Refnum Modified.vi" which is the one I adapted to actually interface to the LabVIEW DLL correctly. There was some glitch in the Import Library Wizard in the LabVIEW 2011 version I used for this (for no other reason than having that version open at the moment for some other work) in that it did not recognize the int32_t datatype used in the error cluster definition, despite that this should be defined by the cintools header files for Windows. So in addition to the _WIN32;_M_IX86 defines in the ILW dialog I also had to define the int32_t=long definition, making the string _WIN32;_M_IX86;int32_t=long for the ILW to import my function. Possibly this might be fixed in newer LabVIEW versions in the ILW.

 

Again the Import Shared Library Wizard is not a magic tool that can create perfect code. It can't do so by the very nature of the limitations of a C header file but there exists no better alternative. 

 

And even if the Import Library Wizard seems to successfully create non broken VIs, this is by no means an indication that the created VI is actually correctly invoking the DLL function. There are simply to many other things that can and often are important about the context of how a function is invoked, that can never sufficiently be captured by any interface definition, much less so by the very restricted C header file definition.

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 33 of 34
(1,905 Views)

@nathand wrote:
One major difference is that Sam_Sharp is adding doubles, which are well-defined, and Steve is adding ints, which are not.

Calling convention is not the problem. That only make a difference at run time, not while importing.

 

Actually int is a predefined datatype in all C compilers but you are right that it is not strictly defined by the C standard as to what size it should have. The only thing C mandates is that the size of a short must be equal or greater than char, the size of an int must be equal or greater than a short and the size of a long must be equal or greater than an int. Almost all current C compilers agree that a char is one byte, and a short is 2 bytes but it is not universally true. int is already a more complicated issue as most 16 bit compilers treat an int as 2 bytes while most 32 bit and 64 bit compilers treat it as 4 bytes. To make matters more interesting long is on Microsoft compilers always 4 bytes but on GCC only for 32 bit compilation. When compiling for 64 bit on GCC, a long is suddenly 8 bytes long. Microsoft didn't know long long for a long time but invented its own _int64 to implement 8 byte integers (and long long on most other compilers does mean 8 bytes integers independent on the bitness of the target code).

 

And no, float and double are not more strictly defined by the C standard. C only mandates that they need to support some form of floating point numbers IF they are even implemented by the C compiler. And that double can have more precise numbers than float. That they are usually implemented using the IEEE 754 specification is not something the C standard mandates, it's just that most C compiler builders choose that, since it is a complex matter and trying to implement a well known standard is always easier than coming up with your own cooked up implementation. But C is specifically allowing the freedom to implement such details in different ways, if that makes sense for a compiler builder because of special capabilities of the CPU architecture (or lack thereof).

 

The <type>_t datatypes however are all C99 or later extensions and almost all C compilers still implement them through header definitions they ship with, rather than as built in datatype. Strictly speaking a C compiler should not support them if it is not explicitedly set to understand C99 but most modern C compilers come with header files that define them anyways in stdint.h even if the C99 support is not enabled. But that seldom hurts unless you work with source code that defines exactly these definitions itself without accounting for the possibility that the C compiler might already come with definitions for them. But that would usually mean that you are dealing with source code that was last touched in the previous century! Smiley Wink

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