Calling C/C++ DLLs Containing Simple and Complex Datatypes from LabVIEW

by Active Participant Jervin Justin ‎01-18-2010 03:10 PM - edited ‎01-30-2017 09:50 AM

» Calling External Code » Calling C/C++ DLLs » Simple and Complex Datatypes

Overview

LabVIEW developers often have to call C/C++ DLLs from LabVIEW and because of the differences between LabVIEW and C/C++ datatypes, passing and receiving data from these DLLs can be intimidating at first.

This example seeks to show developers how to pass and receive different types of data ranging from simple datatypes like numerics (int, float, double, etc), arrays and strings to more complex data types like pointers, struct (cluster), arrays of structs, and even arrays of structs that have arrays of structs in them.

In addition, this example seeks to show developers the different ways to pass and receive data in LabVIEW with a DLL – when functions pass data by value, return data using a return statement, or return data using pass by reference.

This example covers the following use cases:

DataTypeCall Type
1. Numeric (Integer)a) Returning a value (return statement)
2. Array of Numericsb) Returning a pointer (return statement)
3. Stringsc) Passing a parameter (Pass by value)
4. 2 Dimensional Arrayd) Returning values by reference (pass by ref)
5. Simple Struct (with basic datatypes)
6. Complex Struct (with structs & arrays)

Attached Files

  • LabVIEWWrapper.zip: LabVIEW Library (.lvlib) containing examples on how to call each individual function. 
  • PassingDataSampleDLL.zip: Source code for the ANSI C DLL as well as the compiled DLL. Written in CVI, but tested to compile in Visual Studio as well. 
  • CTestApplication.zip: Examples of calling the C DLL from ANSI C. Written in Visual Studio.

Example Hierarchy

The majority of VIs in the example were automatically generated using the Import Shared Library Wizard. The wizard does not cover all cases, and could not generate VI wrappers for some functions, and some VI wrappers were incomplete. However, it gives us a really good starting point for calling our DLL.

For an example on using the Import Shared Library Wizard, refer to:

Tutorial: Creating Wrapper VIs for C/C++ DLL functions using the Import Shared Library Wizard

For an explanation on what the Import Shared Library Wizard missed, refer to the section titled Caveats with the Import Shared Library Wizard.

The VIs in the example are arranged in the following virtual folders in the library:

  • Import Shared Library Wizard Generated VIs: These are the auto-generated VIs that were the output of the Import Shared Library Wizard 
  • Dereferencing Pointers: VIs that assist with dereferencing pointers. They use either MoveBlock or GetValueByPointer. See:
    Dereferencing Pointers from C/C++ DLLs in LabVIEW
  • Custom Controls For Structs: Strict Typedef controls that represent the struct datatypes in the DLL 
  • Completed, Corrected and Added VIs: Additional VIs that were added in order to work around the caveats encountered using the Import Shared Library Wizard

Caveats with the Import Shared Library Wizard

The following cases were not completely handled by the Import Shared Library Wizard:

VI Not Generated

In the following case, VIs were not generated at all:

  • When a function returns anything other than a numeric, string or void. Pointers are fine because they are treated as numerics.
    In these cases, the data should be treated as a pointer and the data type in LabVIEW for the return value should be set as Unsigned Pointer-Sized Integer

In the example lvlib, these VIs were manually created and given the suffix “Added” to the name.

Incomplete VIs

In the following cases, the VIs that were generated were incomplete in some fashion:

In the example lvlib, these VIs completed in a new VI that has the suffix “Complete” in the name.

Incorrect VIs

In the following cases, the VIs that were generated had some incorrect behavior:

  • When a function returned a pointer to a pointer (strings and 2D Arrays)
    These must be dereferenced twice.
  • When a function took a struct (cluster) with an array/string in it
    The individual elements should be dereferenced individually.

In the example lvlib, these VIs were corrected in a new VI and given the suffix “Corrected” to the name.

List of Functions and VIs (by Data Type and Call Type)

The following is a list of functions exposed by the C DLL as well as the VI that shows how to properly call the particular function.

  1. Numeric (Integer)    
    1. Returning a value (return statement)
      Function: int ReturningAValue_Integer (void);
      VI:
      Returning A Value Integer.vi
      Auto Generated VI Status: Working
            
    2. Returning a pointer (return statement)
      Function:
      int* ReturningAValue_PointerToInteger (void);
      VI: Returning A Value Pointer To Integer Complete.vi
      Auto Generated VI Status: Incomplete
            
    3. Passing a parameter (Pass by value)
      Function:
      int PassingParameters_Integer (int x, int y);
      VI: Passing Parameters Integer.vi
      Auto Generated VI Status:
      Working      
    4. Returning values by reference (pass by ref)
      Function:
      void ReturningValuesByReference_Integer (int x, int y, int *sum);
      VI:
      Returning Values By Reference Integer.vi
      Auto Generated VI Status:
      Working     
  2. Array of Numerics    
    1. Returning a value (return statement)
      Function:
      int* ReturningAValue_ArrayOfIntegers (int length);
      VI:
      Returning A Value Array Of Integers Complete.vi
      Auto Generated VI Status:
      Incomplete      
    2. Returning a pointer (return statement)
      Function: N/A (arrays variables are already pointers)
      VI: N/A
      Auto Generated VI Status: N/A
            
    3. Passing a parameter (Pass by value)
      Function: int PassingParamters_ArrayOfIntegers (int x[], int length);
      VI:
      Passing Paramters Array Of Integers.vi
      Auto Generated VI Status:
      Working      
    4. Returning values by reference (pass by ref)
      Function: void ReturningValuesByReference_ArrayOfIntegers (int *x, int length, int **newArray, int *newLength);
      VI:
      Returning Values By Reference Array Of Integers Complete.vi
      Auto Generated VI Status: Incomplete
            
  3. Strings    
    1. Returning a  value (return statement)
      Function:
      char* ReturningAValue_String (void);
      VI:
      Returning A Value String.vi
      Auto Generated VI Status: Working
            
    2. Returning a pointer (return statement)
      Function: N/A (arrays variables are already pointers)
      VI:N/A
      Auto Generated VI Status: N/A
            
    3. Passing a parameter (Pass by value)
      Function:
      int PassingParamters_String (char *str);
      VI:
      Passing Paramters String.vi
      Auto Generated VI Status: Working
            
    4. Returning values by reference (pass by ref)
      Function:
      void ReturningValuesByReference_String (char *str, char **newString);
      VI:
      Returning Values By Reference String Corrected.vi
      Auto Generated VI Status: Incorrect
            
  4. 2 Dimensional Array    
    1. ReturningAValue_2DArrayOfIntegers
      Function: int** ReturningAValue_2DArrayOfIntegers (int rows, int cols);
      VI:
      Returning A Value 2D Array Of Integers Complete.vi
      Auto Generated VI Status: Incomplete
            
    2. Returning a pointer (return statement)
      Function: N/A (arrays variables are already pointers)
      VI: N/A
      Auto Generated VI Status: N/A
            
    3. Passing a parameter (Pass by value)
      Function:
      int PassingParamters_2DArrayOfIntegers (int *x, int rows, int cols);
      VI:
      Passing Paramters 2D Array Of Integers Corrected.vi
      Auto Generated VI Status: Incorrect
            
    4. Returning values by reference (pass by ref)
      Function:
      void ReturningValuesByReference_2DArrayOfIntegers (int rows, int cols, int ***newArray);
      VI:
      Returning Values By Reference 2D Array Of Integers Complete.vi
      Auto Generated VI Status: Incomplete
           
  5. Simple Struct (with basic datatypes)    
    1. Returning a  value (return statement)
      Function:
      struct simpleStructCircle ReturningAValue_SimpleStruct(void);
      VI:
      Returning A Value Simple Struct Added.vi
      Auto Geerated VI Status: Not Generated
            
    2. Returning a pointer (return statement)
      Function:
      struct simpleStructCircle* ReturningAValue_PointerToSimpleStruct(void);
      VI:
      Returning A Value Pointer To Simple Struct Added.vi
      Auto Generated VI Status: Not Generated
            
    3. Passing a parameter (Pass by value)
      Function:
      float PassingParamters_SimpleStruct (struct simpleStructCircle circle);
      VI:
      Passing Paramters Simple Struct.vi
      Auto Generated VI Status: Working
            
    4. Returning values by reference (pass by ref)
      Function:
      void ReturningValuesByReference_SimpleStruct (struct simpleStructCircle circle, struct simpleStructCircle *largerCircle);
      VI:
      Returning Values By Reference Simple Struct.vi
      Auto Generated VI Status: Working
            
    5. Returning array of struct
      Function: void ReturningValuesByReference_ArrayOfSimpleStruct (struct simpleStructCircle **circleArray, int length);
      VI:
      Returning Values By Reference Array Of Simple Struct Complete.vi
      Auto Generated VI Status: Incomplete
        
  6. Complex Struct (with structs and arrays)    
    1. Returning a  value (return statement)
      Function:
      struct complexStructPolygon ReturningAValue_ComplexStruct (void);
      VI:
      Returning A Value Complex Struct Added.vi
      Auto Generated VI Status: Not Generated
            
    2. Returning a pointer (return statement)
      Function:
      struct complexStructPolygon* ReturningAValue_PointerToComplexStruct (void);
      VI:
      Returning A Value Pointer To Complex Struct Added.vi
      Auto Generated VI Status: Not Generated
            
    3. Passing a parameter (Pass by value)
      Function:
      int PassingParamters_ComplexStruct (struct complexStructPolygon triangle);
      VI:
      Passing Paramters Complex Struct Corrected.vi
      Auto Generated VI Status: Incorrect
            
    4. Returning values by reference (pass by ref)
      Function:
      void ReturningValuesByReference_PointerToComplexStruct (struct complexStructPolygon* triangle);
      VI:
      Returning Values By Reference Pointer To Complex Struct Corrected.vi
      Auto Generated VI Status: Incorrect
            
    5. Returning array of struct
      Function: void ReturningValuesByReference_ArrayOfComplexStruct (struct complexStructPolygon **triangles, int length);
      VI:
      Returning Values By Reference Array Of Complex Struct Corrected.vi
      Auto Generated VI Status: Incorrect
            
    6.    

For a spreadsheet view, refer to:
ListOfFunctionsAndVIs.xlsx

Conclusion / Next Steps

Passing data between LabVIEW and C/C++ DLLs can be challenging especially when dealing with complex data types and pointers. Use the VIs in this example as a reference when you need to call DLLs from LabVIEW.