Search code examples
javajna

Structure loaded using JNA defining fields wrong


I haven't used JNA before, but I figured I would give it a shot with a 3rd party DLL I am working with. I wish I could give the source code for the functions that I call with JNA, but I sadly don't own this software so I'm sure what I am allowed to distribute.

The structure I need is rather long/complex, just a warning. I did not include the functions I needed to load this struct, as they appear to be working and have no issue. Here is the original C struct:

struct TSurfObjectInfos
{
  unsigned long ulSize;           // size of the structure

  int   Type;                     // studiable type

  char  strName[31];              // name of the studiable

  char  strOperatorName[31];      // name of the operator who
                                  // measured the studiable

  short nAcquisitionType;         // which kind of sensor has been used
                                  // for the measure

  short nTracking;                // 0: normal tracking
                                  // 1: extended tracking
  
  short nSpecialPoints;           // 0: normal
                                  // 1: non-measured points

  int   bAbsolute;                // FALSE: relatives values
                                  // TRUE: absolutes values
  
  float fGaugeResolution;         // 0: resolution not set

  long  nZMin;                    // min (resampled) value
  long  nZMax;                    // max (resampled) value

  long  nXCount;                  // number of points by column
  long  nYCount;                  // number of points by row
  long  nWCount;                  // number of points by depth (for hyperpectral measurements)

  float fXStep;                   // step
  float fYStep;                   // step
  float fZStep;                   // step

  float fXOffset;                 // offset
  float fYOffset;                 // offset
  float fZOffset;                 // offset

  char  strXAxisName[17];         // name of the X axis
  char  strYAxisName[17];         // name of the Y axis
  char  strZAxisName[17];         // name of the Z axis

  TUNIT tXAxisUnit;               // X axis unit
  TUNIT tYAxisUnit;               // Y axis unit
  TUNIT tZAxisUnit;               // Z axis unit

  char  strXAxisUnknownUnit[17];  // if unknown X unit, unit is present in this field
  char  strYAxisUnknownUnit[17];  // if unknown Y unit, unit is present in this field
  char  strZAxisUnknownUnit[17];  // if unknown Z unit, unit is present in this field

  int   bInverted;                // are the values inverted ?
  
  short nRectified;               // 


  short nSecond;                  // date-time of the measure
  short nMinute;
  short nHour;
  short nDay;
  short nMonth;
  short nYear;
  float fMeasureLength;           // length (in seconds) of the measure

  char  ClientInfo[128];          // client informations

  short nCommentSize;             // size in bytes of the comment

  // *** T Axis ******************
  // *** only used with series ***
  
  float fTStep;                   // step
  float fTOffset;                 // offset
  int   tTAxisUnit;               // T axis unit
  char  strTAxisUnknownUnit[14];  // if unknown T unit, unit is present in this field
  char  strTAxisName[14];         // name of the T axis

  // *** T Axis ******************
};
typedef struct TSurfObjectInfos TSurfObjectInfos;

And here is my Java implementation:

@FieldOrder({ "unsignedSize",
              "type",
              "name",
              "operator",
              "sensorType",
              "trackingType",
              "specialPointType",
              "absolute",
              "gaugeResolution",
              "zMin", "zMax",
              "xCount", "yCount", "wCount",
              "fXStep", "fYStep", "fZStep",
              "xOffset", "yOffset", "zOffset",
              "xAxisName", "yAxisName", "zAxisName",
              "xAxisUnit", "yAxisUnit", "zAxisUnit",
              "strXAxisUnknownUnit", "strYAxisUnknownUnit", "strZAxisUnknownUnit",
              "inverted",
              "rectified",
              "second", "minute", "hour", "day", "month", "year", "fMeasureLength",
              "clientInfo",
              "commentSize",
              "tStep", "tOffset", "tAxisUnit", "strTAxisUnknownUnit", "tAxisName"})
public class StudiableInfo extends Structure {

  private static final int DATA_SIZE = 339;

  /**
   * Unsigned byte size of structure.
   */
  @Getter public NativeLong unsignedSize = new NativeLong(DATA_SIZE, true);

  /**
   * Type of surface.
   */
  @Getter public int type;

  /**
   * Name of surface (maximum length of 31).
   */
  @Getter public char[] name = new char[31];

  /**
   * Name of operator who measured the surface (maximum length of 31).
   */
  @Getter public char[] operator = new char[31];

  /**
   * Type of sensor used for measuring the surface.
   */
  @Getter public short sensorType;

  /**
   * Type of tracking used in measurements.
   */
  @Getter public short trackingType;

  /**
   * Special point type, and whether there are non-measured points.
   */
  @Getter public short specialPointType;

  /**
   * Defines if surface has absolute values.
   */
  @Getter public boolean absolute;

  /**
   * Gauge resolution value. Defined as 0 if resolution not set.
   */
  @Getter public float gaugeResolution;

  /**
   * Minimum (resampled) value.
   */
  @Getter public NativeLong  zMin = new NativeLong();
  /**
   * Maximum (resampled) value.
   */
  @Getter public NativeLong  zMax = new NativeLong();

  /**
   * Number of points by column.
   */
  @Getter public NativeLong xCount = new NativeLong();
  /**
   * Number of points by row.
   */
  @Getter public NativeLong  yCount = new NativeLong();
  /**
   * Number of points by depth (for hyperpectral measurements).
   */
  @Getter public NativeLong  wCount = new NativeLong();

  /**
   * X-axis step value.
   */
  @Getter public float fXStep;
  /**
   * Y-axis step value.
   */
  @Getter public float fYStep;
  /**
   * Z-axis step value.
   */
  @Getter public float fZStep;

  /**
   * X-axis offset.
   */
  @Getter public float xOffset;
  /**
   * X-axis offset.
   */
  @Getter public float yOffset;
  /**
   * X-axis offset.
   */
  @Getter public float zOffset;

  /**
   * Name of the X-axis (max size of 17 bytes).
   */
  @Getter public char[] xAxisName = new char[17];
  /**
   * Name of the Y-axis (max size of 17 bytes).
   */
  @Getter public char[] yAxisName = new char[17];
  /**
   * Name of the Z-axis (max size of 17 bytes).
   */
  @Getter public char[] zAxisName = new char[17];

  /**
   * X-axis measurement units.
   */
  @Getter public int xAxisUnit;
  /**
   * Y-axis measurement units.
   */
  @Getter public int yAxisUnit;
  /**
   * Z-axis measurement units.
   */
  @Getter public int zAxisUnit;

  /**
   * If unknown X-axis unit from enum, unit is present in this field (max size of 17 bytes).
   */
  @Getter public char[] strXAxisUnknownUnit = new char[17];
  /**
   * If unknown Y-axis unit from enum, unit is present in this field (max size of 17 bytes).
   */
  @Getter public char[] strYAxisUnknownUnit = new char[17];
  /**
   * If unknown Z-axis unit from enum, unit is present in this field (max size of 17 bytes).
   */
  @Getter public char[] strZAxisUnknownUnit = new char[17];

  /**
   * Defines if the studiable values are inverted.
   */
  @Getter public boolean inverted;

  /**
   * Defines if the studiable is levelled.
   */
  @Getter public short rectified;

  /**
   * Seconds value at recorded time of measurement.
   */
  @Getter public short second;
  /**
   * Minutes value at recorded time of measurement.
   */
  @Getter public short minute;
  /**
   * Hour value at recorded time of measurement.
   */
  @Getter public short hour;
  /**
   * Day at recorded time of measurement.
   */
  @Getter public short day;
  /**
   * Month at recorded time of measurement.
   */
  @Getter public short month;
  /**
   * Year at recorded time of measurement.
   */
  @Getter public short year;
  /**
   * Length (in seconds) of the measure.
   */
  @Getter public float fMeasureLength;

  /**
   * Client information (max size of 128 bytes).
   */
  @Getter public char[] clientInfo = new char[128];

  /**
   * Size in bytes of the comment.
   */
  @Getter public short commentSize;

  // *** T Axis ******************
  // *** only used with series ***
  /**
   * Step value of T-axis.
   */
  @Getter public float tStep;
  /**
   * Offset of T-axis.
   */
  @Getter public float tOffset;
  /**
   * T-axis measurement units.
   */
  @Getter public int tAxisUnit;
  /**
   * If unknown T-axis unit from enum, unit is present in this field (max size of 14 bytes).
   */
  @Getter public char[] strTAxisUnknownUnit = new char[14];
  /**
   * Name of the T-axis (max size of 14 bytes).
   */
  @Getter public char[] tAxisName = new char[14];

  // *** T Axis ******************
}

Everything on their own looks fine to me, but when actually loading and looking at the structure, the only field that is correct is the type member. From then on, every other field appears to be invalid or garbage data. However, it is consistent and always outputs the same set of invalid data.

I'm worried that this could be because of the import function works internally. You will notice there is a ulSize field that I'm guessing could be used for mapping the memory in the structure? So maybe the function internally is not mapping the memory to the correct location once JNA or Java's memory handling is being used. Although since I am still new to JNA, I am not quite sure if that is even a possible issue.

I'm also thinking that maybe there is a conversion issue between the native C long type and the JNA NativeLong? But I'm still not quite sure. I even tried replacing the NativeLong's with longs and it still didn't seem to work.

Let me know if there is more information I can give. I didn't want to overload with too much information that could've been useless.


Solution

  • The most likely source of your problem is using Java's char mapping for the C char. They aren't the same. Java's char is a 2-byte UTF-16 mapping of characters, while in C, a char is a single byte.

    See JNA's Type Mapping reference in the overview for more.

    Your choice of NativeLong for C's long is fine in general for cross-platform code.

    I don't know what your TUNIT maps to, but if it's a 4-byte type, the int mapping is fine.