Search code examples
cstructdllimportlabview

Problem creating a VI importing a struct from DLL to LabVIEW


I want to import a DLL to LabVIEW and create VIs from my functions, but for simple functions with common data types, the import works good. My problem is when the function have a struct data type.

I follow this tutorial to import.

My DLL files:

test.h:

#ifndef TEST_H_
#define TEST_H_

typedef struct
{
    int r_sum;
    int r_minus;
    int r_multiply;
    float r_divide;
}CALC_t;

int sum(int a, int b);
int minus(int a, int b);
int multiply(int a, int b);
float divide(int a, int b);
CALC_t calc_all(int a, int b);

#endif /* TEST_H_ */

test.c:

#include "test.h"

int sum(int a, int b)
{
    return a+b;
}

int minus(int a, int b)
{
    return a-b;
}

int multiply(int a, int b)
{
    return a*b;
}

float divide(int a, int b)
{
    return (float)a/b;
}

CALC_t calc_all(int a, int b)
{
    CALC_t result;
    result.r_sum = sum(a, b);
    result.r_minus = minus(a, b);
    result.r_multiply = multiply(a, b);
    result.r_divide = divide(a, b);
    return result;
}

When I import the DLL, the VIs of the functions sum, minus, multiply and divide is successfully created, and works good. But the function calc_all isn't created and the LabVIEW show the warning message:

Cannot create VI

The following VI cannot be created. This might indicate that the associated function contains
parameters of a data type that cannot be converted directly. To work around this issue, you can 
create a custom control or typedef control to represent a complex structure or multidimensional 
array, then re-run the Import Shared Library wizard and select Update VIs from a shared library.
Using the same shared library, step through the wizard again. On the Configure VIs and Controls 
page, assign the custom control or typedef to the associated parameter(s). Complex structures 
include nested structures, structures containing arrays, arrays of strings, and multidimensional 
arrays.

    dll_calc_all.vi

I tried to change on the Configure VIs and Controls page, assign the custom control, cluster and another data type but without success.

I used cygwin32 to compile the library with Eclipse IDE and my LabVIEW is LabVIEW 2021 32 bits.


Solution

  • The import functionality of LabVIEW struggles with non-trivial types and given that it cannot deduce things like memory allocation it is best avoided for any but the most simple calls.

    This typically means that users looking to integrate C/C++ libraries into LabVIEW have to create their own VIs to perform the library calls and sometimes create a wrapper in C/C++ to convert the library's types to LabVIEW friendly ones.

    In this case, I would advise modifying the calc_all function to take a CALC_t type pointer that can act as the result. On the LabVIEW-side we can create a cluster with exactly the same data structure as a CALC_t type and pass a pointer to it to the library call.

    In addition, for 32-bit systems, it is advisable to enforce the packing alignment using the #pragma pack compiler directive.

    Putting this all together:

    test.h

    #ifndef TEST_H_
    #define TEST_H_
    
    // enforce packing alignment for LabVIEW <-> C/C++ types when compiling for Windows 32-bit
    // see https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019YsYSAU
    #pragma pack(push) 
    #pragma pack(1)
    
    typedef struct
    {
        int r_sum;
        int r_minus;
        int r_multiply;
        float r_divide;
    }CALC_t;
    
    #pragma pack(pop)
    
    int sum(int a, int b);
    int minus(int a, int b);
    int multiply(int a, int b);
    float divide(int a, int b);
    void calc_all(int a, int b, CALC_t* );
    
    #endif /* TEST_H_ */
    

    test.c

    #include "test.h"
    
    .
    .
    .
    
    void calc_all(int a, int b, CALC_t* result_ptr)
    {
        result_ptr->r_sum = sum(a, b);
        result_ptr->r_minus = minus(a, b);
        result_ptr->r_multiply = multiply(a, b);
        result_ptr->r_divide = divide(a, b);
    }
    

    LabVIEW Snippet (Drag and Drop onto blank VI block diagram and point the call library node to your .dll)

    LabVIEW code which calls a library function which returns a struct