JNA behaving different then my C# counterpart, why?

34 views
Skip to first unread message

S B

unread,
Aug 25, 2021, 3:23:53 AM8/25/21
to Java Native Access
Hi Everyone,

I posted this earlier on stackoverflow with no success:

I read on the JNA documentation that support might be possible here. If anyone requires paid support please inform me what you charge for a working solution, so I can ask the boss for funds. Please reply to me on: sjoerd...@flex.com

I'm developing something for a piece of hardware and I got a C library to communicate with the hardware. I have methods that send a signal to the hardware (like light bulb turn on) and those work fine on both C# and Java using JNA.

The machine also has a pressable button and when that button is pressed it will log a signal which can be retrieved with a method called A.

The way this was intended to work is to create a new thread which keeps calling this method until it returns 1 in which case it will have information regarding the button press.

I've got this working in C# with the following code:

        while (true)
        {
            byte[] ccbdata = new byte[255];
            short TagCommand = -1, nMsg_type = -1;
            int ccblen = 0, nGwId = 0, nNode = 0;
            int ret = CWrapper.Wrapper.A(ref nGwId, ref nNode, ref TagCommand, ref nMsg_type, ref ccbdata[0], ref ccblen);
            if (ret > 0)
            {
               // do stuff
            }
            Thread.Sleep(100);
        }

Where the method is imported in C# like:

    [DllImport("Clibrary.dll")]
    public static extern int C(ref int Gateway_ID, ref int Node_Addr, ref short Subcmd, ref short msg_type, ref byte data, ref int data_cnt);

I want this code also to work on Java. Unfortunately when I run this with java it never returns 1 unlike the C# implementation.

I was wondering if I am doing something wrong as I'm not experienced using JNA. In Java I import the method like this:

    int C(LongByReference gatewayID, LongByReference tag_addr, LongByReference Subcmd, LongByReference msg_type,
                      ByteByReference data, LongByReference data_cnt);

And I try to run the code like this:

        while(run) {
            gatewayID.setValue(0);
            tag_addr.setValue(0);
            subcmd.setValue(-1);
            msg_type.setValue(-1);
            byte b = 0;
            data.setValue(b);
            data_cnt.setValue(0);

            int ref = CWrapper.INSTANCE.C(gatewayID, tag_addr, subcmd, msg_type, data, data_cnt);
            if (ref > 0) {
                System.out.println("hit");
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
In the documentation I have about this method it is defined in C like:

    C(ByRef gatewayID As Integer, ByRef tag_addr As Integer, ByRef subcmd As Integer, ByRef msg_type As Integer, ByRef data As Byte, ByRef data_cnt As Integer)

Does anyone know why the C# example works but not the Java one?

Daniel B. Widdis

unread,
Aug 25, 2021, 11:18:46 AM8/25/21
to Java Native Access
I did reply in the comments to the answer at StackOverflow and it seems you've made some changes since then, but I'm going to answer based on the code you've shown here.

Here is the C signature you've given:
C(ByRef gatewayID As Integer, ByRef tag_addr As Integer, ByRef subcmd As Integer, ByRef msg_type As Integer, ByRef data As Byte, ByRef data_cnt As Integer)

The Integer type you've defined isn't standard C.  I'm going on the assumption it's a 32-bit number.  In your C# implementation you've declared TagCommand (subcmd) and nMsg_type (msg_type) as short.  This doesn't seem to match the signature where an Integer is defined.  Is the C API more clear on differing bitness of the arguments?

Based on this, the first four arguments and last argument should be IntByReference, not LongByReference.  If Integer is a more broad superclass then you should use ShortByReference for those two arguments to match how you've declared those two arguments.

In your java code, declare a byte[255] array and pass that array as an argument, not a single byte by reference.

If you've altered your code already to try this, please post your updated code.


--
You received this message because you are subscribed to the Google Groups "Java Native Access" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jna-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jna-users/a3099768-a289-4632-af22-4b4173a90094n%40googlegroups.com.


--
Dan Widdis

S B

unread,
Aug 26, 2021, 3:50:30 AM8/26/21
to Java Native Access
Hi Dan, 

Thanks for all your help so far! I've asked the third party for the C signature. I will post it as soon as I get it.

Op woensdag 25 augustus 2021 om 17:18:46 UTC+2 schreef wid...@gmail.com:

S B

unread,
Aug 26, 2021, 5:08:59 AM8/26/21
to Java Native Access
Ok here is the question according the latest information AND the C signature:

I'm developing something for a piece of hardware and I got a C library to communicate with the hardware. I have methods that send a signal to the hardware (like light bulb turn on) and those work fine on both C# and Java using JNA.

The machine also has a pressable button and when that button is pressed it will log a signal which can be retrieved with a method called A.

The way this was intended to work is to create a new thread which keeps calling this method until it returns 1 in which case it will have information regarding the button press.

I've got this working in C# with the following code:

while (true) { byte[] ccbdata = new byte[255]; short TagCommand = -1, nMsg_type = -1; int ccblen = 0, nGwId = 0, nNode = 0; int ret = CWrapper.Wrapper.A(ref nGwId, ref nNode, ref TagCommand, ref nMsg_type, ref ccbdata[0], ref ccblen); if (ret > 0) { // do stuff } Thread.Sleep(100); }

Where the method is imported in C# like:

[DllImport("Clibrary.dll")] public static extern int C(ref int Gateway_ID, ref int Node_Addr, ref short Subcmd, ref short msg_type, ref byte data, ref int data_cnt);

I want this code also to work on Java. Unfortunately when I run this with java it never returns 1 unlike the C# implementation.

I was wondering if I am doing something wrong as I'm not experienced using JNA. In Java I import the method like this:

int C(IntByReference gatewayID, IntByReference tag_addr, ShortByReference Subcmd, ShortByReference msg_type,
byte data, IntByReference data_cnt);
(In C# it throws an error when I use a byte array).

And I try to run the code like this:

while(run) { gatewayID.setValue(0); tag_addr.setValue(0); subcmd.setValue(-1); msg_type.setValue(-1); byte b = 0; data.setValue(b); data_cnt.setValue(0); int ref = CWrapper.INSTANCE.C(gatewayID, tag_addr, subcmd, msg_type, data, data_cnt); if (ref > 0) { System.out.println("hit"); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } }

In the documentation I have about this method it is defined in C like:

C(ByRef gatewayID As Integer, ByRef tag_addr As Integer, ByRef subcmd As Integer, ByRef msg_type As Integer, ByRef data As Byte, ByRef data_cnt As Integer)

Does anyone know why the C# example works but not the Java one?

--EDIT 25-08-2021 12:04

The documentation says the parameters provided to the method are used. So I imagine if they are null somehow the method won't return anything. Perhaps the initialization is wrong for using the ByReference object?

--EDIT 26-08-2021

I've gotten the C signature which is:

typedef int (__stdcall *pC)(int& Gateway_ID, int& Node_Addr, short& Subcmd, short& Msg_Type, unsigned char* Data, short& Data_Cnt);

Op donderdag 26 augustus 2021 om 09:50:30 UTC+2 schreef S B:

Daniel B. Widdis

unread,
Aug 26, 2021, 12:51:35 PM8/26/21
to Java Native Access
I think your mapping is still incorrect for the field unsigned char* Data.

This is a pointer, and your other code has indicated that it's to a 255-byte block of memory.   

Your Java code doesn't seem to match the definition you've given.  You say the function mapping includes byte data, which is a single by-value byte.  

But your later java method indicates possibly you're doing ByteByReference, which only allocates a single byte:
byte b = 0; 
data.setValue(b); 

I don't know which of the two is in your final code because it wouldn't compile if both of those are actually defined.   But neither a single byte (by value) or a single byte by reference (ByteByReference) is correct here.  You need 255 bytes.

You can map data to a byte[255] here, or you can map the function to JNA using a Pointer for which you allocate the data buffer with new Memory(255).  There are a few other more obscure options. 





--
Dan Widdis
Reply all
Reply to author
Forward
0 new messages