Search code examples
javajava-native-interfacejna

Java JNA get and pass native pointer


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?


Solution

  • 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();
        }
    }