Clusters in LabVIEW Do Not Line Up With Structures in Visual C++

Updated May 17, 2023

Reported In

Software

  • LabVIEW

Programming Language

  • C++

Issue Details

I am trying to pass a cluster from LabVIEW as a struct argument to a DLL function. I had LabVIEW generate the C skeleton code for me, so I am certain that my structure definition is correct. I keep noticing that some of the elements in the structure do not align properly, as if there were an offset of several bytes between objects in my structure. Sometimes this causes data to encroach on LabVIEW data pointers and often causes LabVIEW to crash. Why is this occurring?

Solution

In Win-32 environments, LabVIEW stores cluster elements back to back with a packing size of 1 byte, as demonstrated in the How LabVIEW Stores Data in Memory topic in the LabVIEW Help. Meaning, regardless of which members are in the cluster, there are no extra bytes between one element and the next. The default settings for Visual Studio, however, will pack items according to their natural alignment and a default packing size of 8 bytes. 

Excerpt from Microsoft MSDN
For types in memory, the default alignment is the same as its natural alignment
  • A base type such as short, float and _int64, and a pointer is aligned naturally if its representation starts at an address that is modulo its size. All of the currently supported base types have sizes 1,2,4, or 8 bytes. Pointers have a size of 4 bytes in 32-bit environments and 8 bytes in 64-bit environments.
  • A compound type is aligned naturally if each of its components is aligned naturally relative to the beginning of the type and if there are no unnecessary gaps (padding) between components.
So the natural alignment of a type is equal to the biggest alignments of its components. 

What this means is that if you have a struct with an Int16 and an Int32, there will be 2 bytes of padding after the Int16 because the Int32 is setting the natural alignment.

The compiler will pack data according to the packing size or the natural alignment, whichever is smaller. Therefore, specifying a packing size greater than the natural alignment will have no effect on the actual packing. If you use the default packing size of 8 bytes in Visual C++, items will generally be aligned naturally. 

There are two methods that you can use to work around this when passing data between LabVIEW and Visual C++.
     
    • If your DLL function is already compiled, you can add dummy variables to your cluster in LabVIEW to act as padding. If you are using a cluster with an Int16 and an Int32, you can place a second Int16 after the first as padding. In this manner, items in the LabVIEW memory space will properly line up with the what Visual C++ expects. 
    • Alternatively, you can force Visual C++ to use a packing size of 1 byte for structures being passed from LabVIEW. To do this, use the pack pragma to specify the packing order for these structures in your typedefs. For example:

      #pragma pack(1) //sets packing size to 1 for following structures
      typedef struct {
      int16 elt1;
      int32 elt2;
      } TD1;
      .
      .
      .
      #pragma pack() //returns packing size to that specified in command line options


      You can also use:

      #pragma pack(push) 
      #pragma pack(1)
      typedef struct {
      int16 elt1;
      int32 elt2;
      } TD1;
      .
      .
      .
      #pragma pack(pop)

       
    Visual Studio uses natural alignment because it allows for slightly faster memory access. 32-bit LabVIEW does not use natural alignment for reasons concerning cross platform and software compatibility. For more information about packing and alignment, consult Microsoft's MSDNStorage and Alignment of Structures.

    Note: 64-bit LabVIEW uses natural alignment, so the above steps are unnecessary for compiling a struct for usage with it. The natural alignment that the Visual Studio compiler uses is correct in that case.