Reputation: 35
I'm trying to interface a cash counter device. I have to use a struct and pass it to the method parameter which is defined in the cash counter's API documentation. The struct is given below:
typedef struct
{
unsigned int iCurrencyNum; // Number of acceptable currencies(The current version only supports single currency)
unsigned char acCurrencyIndex [64]; // Index of acceptable currencies,Reference appendix SNBC note coding standard
unsigned char cSortLevel; // Recognition level 0-low 1- medium 2-high
unsigned char acReserved[255]; // Reserve
} tInitSetting;
In my java application, I created the following class to be passed to the API in the method parameters.
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
public class TInitSetting extends Structure {
public int iCurrencyNum;
public byte[] acCurrencyIndex = new byte[64];
public byte cSortLevel;
// public byte[] acReserved = new byte[64];
@Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[]{
"iCurrencyNum",
"acCurrencyIndex",
"cSortLevel"
});
}
public static class ByReference extends TInitSetting implements Structure.ByReference {}
public static class ByValue extends TInitSetting implements Structure.ByValue {}
}
I run the method from the API and it works fine. The response is "Success" however the cash counter does not get initialized (which is what the method in the API does).
I suspect it's something to do with this class. Is this correct?
Upvotes: 1
Views: 305
Reputation: 9091
The definition of "correctly" is dependent on the API, which you haven't shown.
However, I will comment on your structure implementation and what could possibly make it incorrect, in a more general sense that may help others mapping structures.
Your choice of Java int
for Native (unsigned) int
and Java byte
for Native (unsigned) char
is correct, and you have correctly mapped the first three elements. This includes using the public
modifier and having their order declared in the getFieldOrder()
method.
Of note, since JNA 5 you can declare the field order in an annotation, so you could just put this annotation above your class to accomplish the same thing with less boilerplate:
@FieldOrder ({"iCurrencyNum", "acCurrencyIndex", "cSortLevel"})
You have not mapped acReserved
which would just be a byte[255]
similar to your mapping of acCurrencyIndex
. This is sometimes OK but highly dependent on the API. Is there a particular reason you chose to exclude it?
Assuming the function has a * tInitSetting
argument, all you're really passing to that argument is a pointer to the beginning of the structure. It doesn't know where the structure ends, unless there is also an argument for the size. Many functions respect the size argument and write "up to size bytes" to the structure. Some functions use the size argument to determine a "version" of the structure and write different things based on that.
Ultimately you need to read the API documentation to know if it's acceptable to pass a smaller structure than it asks for.
These are generally not needed if using the default behavior (ByReference
in function/method arguments where the native signature is * tInitSetting
, and ByValue
as a nested structure where the native structure includes the type tInitSetting
(without the *
).)
You haven't indicated how you're using the structure, but if you're using these tags, you may be using them improperly. In particular, if you are passing ByValue
when it is expecting a pointer, it won't work.
Your symptom of the function call succeeding but your structure not being updated could be a case of autoRead()
not processing. The native function populates native memory with the structure results, but those still need to be copied over to Java using read()
.
This normally happens automatically when passing a structure by reference, but given you've added both ByReference
and ByValue
tags, it's possible the lack of update is a symptom of choosing the wrong one.
You could test by reading the structure value after manually calling read()
on it.
Upvotes: 1