Java JNA get and pass native pointer

90 Views Asked by At

How using Java JNA get a pointer from a native method and pass it to another native method?

//C code .h
...
extern "C" HN2QCONN __stdcall N2QLibConnCreate(LPCTSTR lpszIniFile, LPCTSTR lpszSection, 
                                               N2Q_CALLBACK_PROC Callback);
extern "C" BOOL __stdcall N2QLibConnectQuik(HN2QCONN hConn);
...
//another C code .h
...
DECLARE_HANDLE (HN2QCONN);
...
//test C code .cpp
HN2QCONN hConn = N2QLibConnCreate("n2q.ini", "local", CallbackProc);
if (!N2QLibConnectQuik(hConn))                              
        printf("error connect to server.");

How can I do the same in Java? How do I correctly get the data from the native method and use the managed code to pass it back to the native method?

I'm trying to do this:

//java code
...
public interface N2q_lib extends Library {
    N2q_lib INSTANCE = (N2q_lib)Native.load("n2q_lib.dll", N2q_lib.class);
    Pointer N2QLibConnCreate(String lpszIniFile, String lpszSection, N2q_callback_proc Callback);
    boolean N2QLibConnectQuik(Pointer ptr);
}
...
public class App 
{
    public static void main( String[] args )
    {
        Pointer ptr = new Memory(N2q_lib.INSTANCE.N2QLibConnCreate("n2q.ini", "local", null));
        if (!N2q_lib.INSTANCE.N2QLibConnectQuik(ptr))
            System.out.println("error connect to server.");
    }
}

But I always get "false". Am I using pointers incorrectly?

2

There are 2 best solutions below

0
KUL On BEST ANSWER

The main problem was that the external library was compiled without UTF-8 support! Therefore, I needed to specify ASCII for JNA. Additionally, there were hard paths in the external native libraries, so it was necessary to place all the dll's in the same directory with the jar

For yourself, an example of working code:

package local.qapp.jnews.wrapper;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public interface N2QLib extends StdCallLibrary {

    N2QLib INSTANCE = (N2QLib) Native.load("n2q_lib", N2QLib.class, W32APIOptions.ASCII_OPTIONS);
    N2QLib SYNC_INSTANCE = (N2QLib) Native.synchronizedLibrary(INSTANCE);
        
    interface N2Q_CALLBACK_PROC extends StdCallCallback {
        int callback(Pointer msg);
    }

    Pointer N2QLibConnCreate(String lpszIniFile, String lpszSection, N2Q_CALLBACK_PROC Callback);

    boolean N2QLibConnectQuik(Pointer hConn);
    boolean N2QLibIsConnected(Pointer hConn);
    boolean N2QLibReconnect(Pointer hConn);
    boolean N2QLibMessage2Quik(Pointer hConn, MSG_TITLE MsgTitle, byte[] pszMsgBody);
    boolean N2QLibDisconnectQuik(Pointer hConn);
    boolean N2QLibConnDestroy(Pointer hConn);
}

package local.qapp.jnews.wrapper;

import java.io.UnsupportedEncodingException;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;;

@FieldOrder ({ 
    "m_szSourceAgencyIdent"
    , "m_lMsgSeqNumber"
    , "m_szMsgCategory"
    , "m_szMsgProductCode"
    , "m_szMsgKeywords"
    , "m_szMsgSubject"
    , "m_lMsgTime"
    , "m_lMsgDate"
    , "m_lMsgFlags"
    , "m_iBodyLength"
})
public class MSG_TITLE extends Structure {
    public byte[] m_szSourceAgencyIdent = new byte[13];
    public int m_lMsgSeqNumber;
    public byte[] m_szMsgCategory = new byte[13];
    public byte[] m_szMsgProductCode = new byte[21];
    public byte[] m_szMsgKeywords = new byte[61];
    public byte[] m_szMsgSubject = new byte[129];
    public int m_lMsgTime;
    public int m_lMsgDate;
    public int m_lMsgFlags;
    public int m_iBodyLength;
    
    public MSG_TITLE() {
        super(ALIGN_MSVC);
    }
    public MSG_TITLE (
            int m_iBodyLength
            , int m_lMsgDate
            , int m_lMsgFlags
            , int m_lMsgSeqNumber
            , int m_lMsgTime
            , String m_szMsgCategory
            , String m_szMsgKeywords
            , String m_szMsgProductCode
            , String m_szMsgSubject
            , String m_szSourceAgencyIdent
                        ) throws UnsupportedEncodingException {
        this();
        this.m_iBodyLength = m_iBodyLength;
        this.m_lMsgDate = m_lMsgDate;
        this.m_lMsgFlags = m_lMsgFlags;
        this.m_lMsgSeqNumber = m_lMsgSeqNumber;
        this.m_lMsgTime = m_lMsgTime;
        System.arraycopy(m_szMsgCategory.getBytes("windows-1251"), 0, this.m_szMsgCategory, 0, m_szMsgCategory.getBytes("windows-1251").length);
        System.arraycopy(m_szMsgKeywords.getBytes("windows-1251"), 0, this.m_szMsgKeywords, 0, m_szMsgKeywords.getBytes("windows-1251").length);
        System.arraycopy(m_szMsgProductCode.getBytes("windows-1251"), 0, this.m_szMsgProductCode, 0, m_szMsgProductCode.getBytes("windows-1251").length);
        System.arraycopy(m_szMsgSubject.getBytes("windows-1251"), 0, this.m_szMsgSubject, 0, m_szMsgSubject.getBytes("windows-1251").length);
        System.arraycopy(m_szSourceAgencyIdent.getBytes("windows-1251"), 0, this.m_szSourceAgencyIdent, 0, m_szSourceAgencyIdent.getBytes("windows-1251").length);
    }
};

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class App {
    
    public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {
        System.setProperty("jna.encoding", "windows-1251");
        System.setProperty("jna.library.path", System.getProperty("user.dir"));
        //debug
        //System.setProperty("jna.debug_load", "true");
        //System.setProperty("jna.debug_load.jna", "true");
        //System.setProperty("jna.dump_memory", "true");
        
        N2QLib lib = N2QLib.SYNC_INSTANCE;
        
        final String conf = new String(System.getProperty("user.dir")+"\\n2q.ini");
        final String serv = new String("local");
        
        String rawMessage = "Body text\0";
        byte[] message = new byte[20000];
        System.arraycopy(rawMessage.getBytes("windows-1251"), 0, message, 0, rawMessage.getBytes("windows-1251").length);
        
        MSG_TITLE title = new MSG_TITLE(
                rawMessage.getBytes("windows-1251").length
                , Integer.valueOf(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
                , 0
                , (int)Instant.now().getEpochSecond()
                , Integer.valueOf(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss")))
                , "\0"
                , "\0"
                , "\0"
                , "Subject\0"
                , "SUPPORT\0"
        );
        
        N2QLib.N2Q_CALLBACK_PROC callback = new N2QLib.N2Q_CALLBACK_PROC() {
            public int callback(Pointer msg) {
                return 1;
            }
        };
        
        Pointer hConn = lib.N2QLibConnCreate(conf, serv, callback);
        Thread.sleep(1000);
                
        System.out.println(LocalDateTime.now());
        if (!lib.N2QLibConnectQuik(hConn))
        {
            System.out.println("Error - connecting to server.");
        } else {
            System.out.println("Successfully connected.");
        }

        if (!lib.N2QLibIsConnected(hConn)) {
            System.out.println("Error - was not connected to server.");
        } 
        else {
            if (lib.N2QLibMessage2Quik(hConn, title, message))
            {
                System.out.println("message was send.");
            }
            if (lib.N2QLibDisconnectQuik(hConn))
            {
                System.out.println("Successfully disconnected.");
            }
            Thread.sleep(1000);
            if (lib.N2QLibConnDestroy(hConn))
            {
                System.out.println("Successfully connect destroy.");
            }
            Thread.sleep(1000);
        }
        hConn = null;
        Native.unregister(lib.getClass());
        System.gc();
    }
}
2
user2543253 On

When you compare your Java code to your C code, you'll see that in C you pass the return value of the first function call directly to the second one. In the Java example you're creating another pointer from it. But there's no need for that. Your first method call already returns a Pointer, pass that directly as you do in C.

I don't know what N2QLibConnCreate does and if it requires the callback argument. If so you'll also have to create an instance of N2q_callback_proc and pass that instead of null.