LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Call function in LabView from a DLL, then access global variable from DLL

Solved!
Go to solution

I have created a DLL in LabWindows with a function and a structure.  I want to call the function from within LabView and then access the global structure.  I am able to call the function from the DLL with a "Call Library Function Node" and can access the return value, but I cannot figure out how to access the global structure.  The structure is declared in the DLL header file with __declspec(dllimport) struct parameters.

 

Is there any way of accessing this structure without using the Network Variable Library?

0 Kudos
Message 1 of 9
(4,703 Views)

You'll need to write functions in the DLL that provide access to that global variable, or at the very least the address of that global variable.

0 Kudos
Message 2 of 9
(4,696 Views)

When you say "access to" or "the address of" the global variable, do you mean to pass the variable as an argument to the function call in the DLL?  If so, then I was not really sure how to pass a cluster from LabView by using the "Call Library Function Node".

0 Kudos
Message 3 of 9
(4,663 Views)

@dblok wrote:

When you say "access to" or "the address of" the global variable, do you mean to pass the variable as an argument to the function call in the DLL?  If so, then I was not really sure how to pass a cluster from LabView by using the "Call Library Function Node".


Yes, that's exactly right.  I would include a pair of helper functions in the DLL to read and write the global variable.  Alternatively you might write separate helper functions for each field inside the global structure, depending on the number of fields and whether you want to do any validation on the values.

 

You can pass a cluster by reference to a function that expects a struct by setting the parameter to Adapt to Type, so long as the cluster does not contain any variable-length elements (strings or arrays).  The cluster needs to match the struct exactly, and sometimes that involves adding extra padding bytes to make the alignment work.  Variable-length elements in LabVIEW need to be converted to clusters containing the same number of elements as the struct definition (for example, if your struct contains char name[12], you would create a cluster of 8 U8 values, and you could use String to Array of Bytes followed by Array to Cluster to convert a LabVIEW string into that format).  If the struct contains pointers it gets more complicated, and it may be easier to write functions in the DLL to access those specific elements individually.

 

If you can't get this working or need help, post your code and an explanation of the error or problem you're seeing.

 

EDIT: it is also possible to include a single function in the DLL that returns the address of the global variable, which LabVIEW can then use to access and modify the data, but that's more complicated and likely to lead to crashes if you don't get the memory addressing exactly right.

Message 4 of 9
(4,657 Views)

Thanks, I will try it out tomorrow.

0 Kudos
Message 5 of 9
(4,654 Views)

I seem to have gotten the cluster passed into the DLL now that I converted my strings into clusters of U8's.  The one problem I am having now is that some of the values in my cluster are getting shifted down somehow.  I've checked all my data types in the cluster vs. the structure in the header file and they all match up correctly.  Do I need to do some sort of padding to get everything aligned properly?

 

Here is my structure:

 

struct parameters
{
 char product[26];
 char uC_type[26];
 char bom_rev[4];
 unsigned short int prod_gen;
 unsigned short int nominal_battv;
 unsigned short int fet_qty;
 short int fwd_rev;
 short int qkstp;
 short int brk_rel;
 short int in_out;
 int throttleh_res;
 int throttlel_res;
 short int anin;
 int horn_res;
 unsigned short int fwd_pkcur_hi;
 unsigned short int fwd_pkcur_lo;
 unsigned short int rvs_pkcur_hi;
 unsigned short int rvs_pkcur_lo;
 unsigned short int fwd_avgcur_hi;
 unsigned short int fwd_avgcur_lo;
 unsigned short int rvs_avgcur_hi;
 unsigned short int rvs_avgcur_lo;
};

0 Kudos
Message 6 of 9
(4,634 Views)
Solution
Accepted by topic author dblok

My guess is that you need two bytes of padding after "in_out", and another two bytes of padding after "anin."  The reason for this is that ints are 4 bytes, and most C compilers will align them on 4-byte boundaries.  The struct will naturally start at such a boundary (actually, in Windows, it probably starts at an 8-byte boundary).  If you then count bytes in your struct, you're at byte 70 after "in_out."  70 isn't divisible by 4, so you need 2 more bytes to reach the next 4-byte boundary.  Alternatively you could rearrange your struct so that "anin" follows "in_out" and that's probably the best option if it won't cause you any other problems.

 

Unlike most C compilers, LabVIEW packs structures as tightly as possible, with no padding.  I don't know enough about LabVIEW history and internals to explain the reasons and efficiency penalty for doing this, but, like LabVIEW's choice of endianness, it's probably a holdover from early versions of LabVIEW that ran on the Mac.

 

If for some reason you wanted to force your C struct to match LabVIEW's packing, you could use the #pragma pack(x) compiler directive, but I wouldn't recommend that here since you have control over both the C and LabVIEW code.

 

EDIT: In case it wasn't clear, to add padding to your LabVIEW cluster, insert appropriately sized element(s) at the desired location in the cluster.

Message 7 of 9
(4,632 Views)

Thanks, that worked!

 

My only remaining question is what is the better way to add 2 bytes of padding?  I added two dummy U16's and that worked, then I removed the dummy U16's and changed the size of in_out & anin to U32's and that also worked.  Aside from rearranging my struct, which of these two options of adding padding is correct and less likely to cause issues?  I assumed that making the integer size larger than what is declared in the struct will not cause any issues because I would never be sending anything larger than 16-bits anyways.

0 Kudos
Message 8 of 9
(4,611 Views)

dblok wrote:

My only remaining question is what is the better way to add 2 bytes of padding?  I added two dummy U16's and that worked, then I removed the dummy U16's and changed the size of in_out & anin to U32's and that also worked.  Aside from rearranging my struct, which of these two options of adding padding is correct and less likely to cause issues?  I assumed that making the integer size larger than what is declared in the struct will not cause any issues because I would never be sending anything larger than 16-bits anyways.


It's better to add dummy values, so that the C and LabVIEW data types match.  Also, LabVIEW uses the opposite endianness from Windows.  LabVIEW swaps bytes for you automatically, but this may not work properly if you're passing a U32 value where you should have a U16 and two bytes of padding - you might get your data in the two padding bytes.  I don't know that that will happen, but adding the padding bytes explicity avoids the potential for a problem.

0 Kudos
Message 9 of 9
(4,608 Views)