jna_jnative - CSDN
精华内容
参与话题
  • 该资源中,包含jna.jar 和一个jna的函数文档。并且有一个完整的使用例子。该例子是访问本地的IC卡读卡器,其中因为原始厂商提供的本地代码函数过于发杂,为了便于JNA调用,又对本地代码进行了封装。这是一个很好的...
  • JNA简介及使用

    千次阅读 2013-09-12 20:04:53
    JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构...

    JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架(https://github.com/twall/jna)。JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Windowdll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。

    JNA包:

    https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar

    https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna-platform/4.0.0/jna-platform-4.0.0.jar

     

    JNA在线帮助文档:http://twall.github.io/jna/4.0/javadoc/

    JNA入门示例:https://github.com/twall/jna/blob/master/www/GettingStarted.md

    1dllsoC函数的集合和容器,这与Java中的接口概念吻合,所以JNAdll文件和so文件看成一个个接口。在JNA中定义一个接口就是相当于了定义一个DLL/SO文件的描述文件,该接口代表了动态链接库中发布的所有函数。而且,对于程序不需要的函数,可以不在接口中声明。

    2JNA定义的接口一般继承com.sun.jna.Library接口,如果dll文件中的函数是以stdcall方式输出函数,那么,该接口就应该继承com.sun.jna.win32.StdCallLibrary接口。

    3Jna难点:编程语言之间的数据类型不一致。

    JavaC的数据类型对照表

    Java 类型

    类型

    原生表现

     boolean

     int

     32位整数 (可定制)

     byte

     char 

     8位整数

     char

     wchar_t

     平台依赖

     short

     short

     16位整数

     int

     int

     32位整数

     long

    long long, __int64

     64位整数

     float

     float

     32位浮点数

     double

     double

     64位浮点数

     Buffer/Pointer

     pointer

     平台依赖(32 64位指针)

     <T>[] (基本类型的数组)

     pointer/array

    32 64位指针(参数/返回值)

    邻接内存(结构体成员)

     String

     char*

    /0结束的数组 (native encoding or jna.encoding)

     WString

     wchar_t*

     /0结束的数组(unicode)

     String[]

     char**

     /0结束的数组的数组

     WString[]

     wchar_t**

     /0结束的宽字符数组的数组

     Structure

     struct*/struct

    指向结构体的指针 (参数或返回值) (或者明确指定是结构体指针)
    结构体(结构体的成员) (或者明确指定是结构体)

     Union

    union 

     等同于结构体

     Structure[]

     struct[]

     结构体的数组,邻接内存

     Callback

     <T> (*fp)()

     Java函数指针或原生函数指针

     NativeMapped

     varies

     依赖于定义

     NativeLong

     long

     平台依赖(3264位整数)

     PointerType

     pointer

      Pointer相同

     

    4,简单使用示例:

     

    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.Platform;
     
    interface HelloInter extends Library{
       int toupper(int ch);
       double pow(double x,double y);
       void printf(String format,Object... args);
    }
    public class HelloWorld {
      
       public static void main(String [] args){
          HelloInter INSTANCE =
               (HelloInter)Native.loadLibrary(
                     Platform.isWindows()?"msvcrt":"c",
                          HelloInter.class);
          INSTANCE.printf("Hello, World\n");
          String [] strs = new String[]{"芙蓉","如花","凤姐"};
            for (int i=0;i < strs.length;i++) {
                INSTANCE.printf("人物 %d: %s\n", i, strs[i]);
            }
            System.out.println("pow(2d,3d)=="+INSTANCE.pow(2d, 3d));
            System.out.println("toupper('a')=="+(char)INSTANCE.toupper((int)'a'));
       }
     
    }

     

    运行结果:

    pow(2d,3d)==8.0
    toupper('a')==A
    Hello, World
    人物 0: 芙蓉
    人物 1: 如花
    人物 2: 凤姐

     

     

    5,示例说明:

    HelloInter接口中定义的3个函数全是C语言函数库中的函数,其定义格式如下:

    int toupper(int ch)
    double pow( double x, double y )
    int printf(const char* format, ...)

     

    C语言函数库中有很多个函数,但是我们只用到了这3个函数,所以其他的函数不需要声明在接口中。

    注意:Java接口中的参数类型和C语言参数类型之间的对应关系(见第3条,对照表)

    6,结构体参数和结构体指针。

    (1)Structure子类中的公共字段的顺序,必须和C语言中的结构体的顺序保持一致,否则会出错。

    (2)Structure的使用参见:http://www.doc88.com/p-31975835542.html

    注意:跨平台调用函数会影响系统性能应尽量使用基本、简单的数据类型,而且,尽量少跨语言、跨平台传递数据。

     

    参考文章:

    深入浅出JNA—快速调用原生函数:http://www.doc88.com/p-31975835542.html

    JNA—JNI终结者: http://blog.csdn.net/shendl/article/details/3589676

    JNA的使用:http://xbgd.iteye.com/blog/1044864

    深入理解JNA—模拟C语言结构体:http://blog.csdn.net/shendl/article/details/3599849

     

     

     

     

    展开全文
  • 目录 1、简介 2、原理 3、配置环境,创建demo 3.1 搞清楚.dll/.so文件适用...3.2.2 pom.xml文件添加jna依赖 3.2.3 编写一个CLibrary接口,继承Library接口 3.2.4 编写一个测试类调用c++函数代码VixHz_InitSD...

    目录

    1、简介

    2、原理

    3、配置环境,创建demo

    3.1 搞清楚.dll/.so文件适用环境

    3.2 创建一个普通的maven项目

    3.2.1 将.dll/.so文件放在resources根路径下

    3.2.2 pom.xml文件添加jna依赖

    3.2.3 编写一个CLibrary接口,继承Library接口

    3.2.4 编写一个测试类调用c++函数代码VixHz_InitSDK()

    4、调用JNA过程遇到的问题及解决方法


    1、简介

    提到JNA 就不得不提一下JNI(Java Native Interface),有过不同语言间通信开发经历的一般都知道,它允许java和其他语言代码(尤其是C/C++)进行交互,只要遵守约定即可。首先看下JNA调用C/C++过程,注意写程序时自下而上,调用时自上而下:

    可见步骤之多,调用.dll/.so共享库之痛苦的过程。

    若已有编译好的.dll/.so文件—>需先用是C语言另外写一个.dll/.so共享库,使用SUN规定的数据结构代替C语言的数据结构,调用已有的dll/so中公布的函数—>java中载入这个库—>java编写Native函数作为链接库中函数的代理

    问题是很少有java程序员愿意编写调用.dll/.so库中原生函数的java程序,这也使java在客户端上乏善可言,是JNI的一大弱点!

    但是JNA不能完全替代JNI,JNI不仅可以实现java访问C,也可实现C调用java。

    而JNA只能实现Java访问C函数,作为一个Java框架,自然不能实现C语言调用Java代码。此时,你还是需要使用JNI技术。

    那什么是JNA呢?

    JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。

    JNA框架就是为了解决上述JNI弱点而开发的,它提供一组java工具类用于在运行期间动态访问系统本地共享类库,java开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射,而不需要编写任何Native/JNI代码,大大降低了Java调用本体共享库的开发难度。

    之所以说它是JNI的替代者,是因为JNA大大简化了调用本地方法的过程,使用很方便,基本上不需要脱离Java环境就可以完成。JNA调用C/C++的过程大致如下:

    可以看到步骤减少了很多,最重要的是我们不需要重写我们的动态链接库文件,而是有直接调用的API,大大简化了我们的工作量。

    2、原理

    JNA使用一个小型的JNI库插桩程序来动态调用本地代码。开发者使用java接口描述目标本地库的功能和结构,这使得它很容易利用本机平台的功能,而不会产生多平台配置和生成JNI代码的高开销。这样的性能、准确性和易用性显然受到很大的重视。此外JNA包括一个已与许多本地函数映射的平台库,以及一组简化本地访问的共用接口。

    注意:

    JNA是建立在JNI技术基础之上的一个java类库,它提供了一个动态的C语言编写的转发器,可以自动实现java和C的数据类型映射,不需要再编写C动态链接库,可方便使用java直接访问动态链接库中的函数,JNA性能上有些微损失。

    3、配置环境,创建demo

    3.1 搞清楚.dll/.so文件适用环境

    示例代码是根据不同的环境调用不同的动态链接库:

    public class JnaUtil {
    
        public static Logger logger = Logger.getLogger(JnaUtil.class);
        static CLibrary INSTANCE;
        public static CLibrary LED_INSTANCE;
    
        static {
            // 获取jdk位数
            String bits = System.getProperty("sun.arch.data.model");
            // 获取os名称
            String ops = System.getProperty("os.name");
            logger.info("jdk bits=" + bits);
            logger.info("option sysetm=" + ops);
            if (ops.startsWith("win") || ops.startsWith("Win"))//windows
            {
                if ("32".equals(bits)) {
                    logger.info("use CCR_SDKx32.dll");
                    INSTANCE = (CLibrary) Native.loadLibrary("CCR_SDKx32.dll", CLibrary.class);
                }
                if ("64".equals(bits)) {
                    logger.info("use CCR_SDKx64.dll and LEDControl_x64.dll");
                    INSTANCE = (CLibrary) Native.loadLibrary("CCR_SDKx64.dll", CLibrary.class);
                    LED_INSTANCE = Native.loadLibrary("LEDControl_x64.dll", CLibrary.class);
                }
            } else {
                if ("32".equals(bits)) {
                    logger.info("use libCCR_SDKx64-x86_32.so");
                    INSTANCE = (CLibrary) Native.loadLibrary("libCCR_SDKx64-x86_32.so", CLibrary.class);
                }
                if ("64".equals(bits)) {
                    logger.info("use libCCR_SDKx64-x86_64.so and libLEDControl-x86_64.so");
                    INSTANCE = (CLibrary) Native.loadLibrary("libCCR_SDKx64-x86_64.so", CLibrary.class);
                    LED_INSTANCE = Native.loadLibrary("libLEDControl-x86_64.so", CLibrary.class);
                }
            }
        }
    }

    java环境的jdk位数要与.dll/.so位数相同,否则会报类似错误:

     java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序。

    3.2 创建一个普通的maven项目

    3.2.1 将.dll/.so文件放在resources根路径下

    (若放在子包中,也是可以加载库文件的,不放心可在Native.loadLibrary()中制定下库文件路径)

    3.2.2 pom.xml文件添加jna依赖

    建议jna包的版本不要太老旧:

    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna</artifactId>
        <version>4.5.1</version>
    </dependency>

    3.2.3 编写一个CLibrary接口,继承Library接口

    public interface CLibrary extends Library {
        CLibrary INSTANCE = Native.loadLibrary("iNetSDK.dll", CLibrary.class);
    
        /**
         * 初始化SDK 注意:调用SDK其他接口前必须先调用此接口!
         *
         * @return TRUE表示成功,FALSE表示失败
         */
        boolean VixHz_InitSDK();
    }

    3.2.3.1 在上面的代码中,我们定义了一个接口,继承自Library 或StdCallLibrary,默认的是继承Library ,
    如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary,比如众所周知的kernel32库。

    接口中使用的函数必须与链接库中的函数原型保持一致。

    3.2.3.2 接口内部定义

    接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部dll/so的函数。
    该常量通过Native.loadLibrary()这个API函数获得,该函数有2个参数:

    • 第一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀(此处加了后缀,不同系统和环境已经适用了不同的库文件),这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链接库路径的顺序是:当前类当前文件夹,找不到——再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,找不到——到WINDOWS下面去搜索,再找不到——抛异常。

    • 第二个参数是本接口的Class类型。JNA通过这个Class类型,根据指定的.dll/.so文件,动态创建接口的实例。该实例由JNA通过反射自动生成。

    3.2.4 编写一个测试类调用c++函数代码VixHz_InitSDK()

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class DeviceSdkApplicationTests {
    
        /**
         * 初始化SDK
         */
        @Test
        public void testVixHz_InitSDK() {
            boolean initSDKResult = CLibrary.INSTANCE.VixHz_InitSDK();
            System.out.println("initSDKResult = " + initSDKResult);
        }
    }
    

    运行结果,类型简单的JNA调用成功:

    4、调用JNA过程遇到的问题及解决方法

    4.1 调用JNA报的错java.lang.Error: Invalid memory access:很大可能是JNA调c++接口类型映射的问题;

    类型复杂涉及到指针和结构体,接下来总结下JNA实战笔记汇总(二)——JNA和C / C ++的数据类型映射

     

    展开全文
  • JNA的使用

    万次阅读 多人点赞 2017-02-14 13:17:35
    JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java...
    项目地址:http://jna.java.net/
    API:http://jna.java.net/javadoc/overview-summary.html


    JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架(https://github.com/twall/jna)。JNA提供一组Java工具类用于在运行期动态访问系统本地库(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。


    JNA包:

    https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna/4.0.0/jna-4.0.0.jar

    https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna-platform/4.0.0/jna-platform-4.0.0.jar

     

    JNA在线帮助文档:http://twall.github.io/jna/4.0/javadoc/

    JNA入门示例:https://github.com/twall/jna/blob/master/www/GettingStarted.md

    1,dll和so是C函数的集合和容器,这与Java中的接口概念吻合,所以JNA把dll文件和so文件看成一个个接口。在JNA中定义一个接口就是相当于了定义一个DLL/SO文件的描述文件,该接口代表了动态链接库中发布的所有函数。而且,对于程序不需要的函数,可以不在接口中声明。

    2,JNA定义的接口一般继承com.sun.jna.Library接口,如果dll文件中的函数是以stdcall方式输出函数,那么,该接口就应该继承com.sun.jna.win32.StdCallLibrary接口。

    3,Jna难点:编程语言之间的数据类型不一致


    Java和C的数据类型对照表

    Java 类型

    类型

    原生表现

     

     boolean

     int

     32位整数(可定制)

     

     byte

     char 

     8位整数

     

     char

     wchar_t

     平台依赖

     

     short

     short

     16位整数

     

     int

     int

     32位整数

     

     long

    long long, __int64

     64位整数

     

     float

     float

     32位浮点数

     

     double

     double

     64位浮点数

     

     Buffer/Pointer

     pointer

     平台依赖(3264位指针)

     

     <T>[] (基本类型的数组)

     pointer/array

    3264位指针(参数/返回值)

    邻接内存(结构体成员)

     

     String

     char*

    /0结束的数组 (native encoding or jna.encoding)

     

     WString

     wchar_t*

     /0结束的数组(unicode)

     

     String[]

     char**

     /0结束的数组的数组

     

     WString[]

     wchar_t**

     /0结束的宽字符数组的数组

     

     Structure

     struct*/struct

    指向结构体的指针(参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)

     

     Union

    union 

     等同于结构体

     

     Structure[]

     struct[]

     结构体的数组,邻接内存

     

     Callback

     <T> (*fp)()

     Java函数指针或原生函数指针

     

     NativeMapped

     varies

     依赖于定义

     

     NativeLong

     long

     平台依赖(3264位整数)

     

     PointerType

     pointer

     Pointer相同

     

     

    通用入门案例:

    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.Platform;
    
    /** Simple example of JNA interface mapping and usage. */
    public class HelloWorld {
    
        // This is the standard, stable way of mapping, which supports extensive
        // customization and mapping of Java to native types.
    
        public interface CLibrary extends Library {
            CLibrary INSTANCE = (CLibrary)
                Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
                                   CLibrary.class);
    
            void printf(String format, Object... args);
        }
    
        public static void main(String[] args) {
            CLibrary.INSTANCE.printf("Hello, World\n");
            for (int i=0;i < args.length;i++) {
                CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
            }
        }
    }
    运行程序,如果没有带参数则只打印出“Hello, World”,如果带了参数,则会打印出所有的参数。

    很简单,不需要写一行C代码,就可以直接在Java中调用外部动态链接库中的函数!

     

    下面来解释下这个程序。

    (1)需要定义一个接口,继承自Library StdCallLibrary

    默认的是继承Library ,如果动态链接库里的函数是以stdcall方式输出的,那么就继承StdCallLibrary,比如众所周知的kernel32库。比如上例中的接口定义:

    public interface CLibrary extends Library {
    
    }
    
    

    (2)接口内部定义

    接口内部需要一个公共静态常量:INSTANCE,通过这个常量,就可以获得这个接口的实例,从而使用接口的方法,也就是调用外部dll/so的函数。

    该常量通过Native.loadLibrary()这个API函数获得,该函数有2个参数:

    • 第一个参数是动态链接库dll/so的名称,但不带.dll或.so这样的后缀,这符合JNI的规范,因为带了后缀名就不可以跨操作系统平台了。搜索动态链接库路径的顺序是:先从当前类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。比如上例中printf函数在Windows平台下所在的dll库名称是msvcrt,而在其它平台如Linux下的so库名称是c。
    • 第二个参数是本接口的Class类型。JNA通过这个Class类型,根据指定的.dll/.so文件,动态创建接口的实例。该实例由JNA通过反射自动生成。
    CLibrary INSTANCE = (CLibrary)
                Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"),
                                   CLibrary.class);

    接口中只需要定义你要用到的函数或者公共变量,不需要的可以不定义,如上例只定义printf函数:

    void printf(String format, Object... args);

    注意参数和返回值的类型,应该和链接库中的函数类型保持一致。

    (3)调用链接库中的函数

    定义好接口后,就可以使用接口中的函数即相应dll/so中的函数了,前面说过调用方法就是通过接口中的实例进行调用,非常简单,如上例中:

    CLibrary.INSTANCE.printf("Hello, World\n");
            for (int i=0;i < args.length;i++) {
                CLibrary.INSTANCE.printf("Argument %d: %s\n", i, args[i]);
            }

    这就是JNA使用的简单例子,可能有人认为这个例子太简单了,因为使用的是系统自带的动态链接库,应该还给出一个自己实现的库函数例子。其实我觉得这个完全没有必要,这也是JNA的方便之处,不像JNI使用用户自定义库时还得定义一大堆配置信息,对于JNA来说,使用用户自定义库与使用系统自带的库是完全一样的方法,不需要额外配置什么信息。比如我在Windows下建立一个动态库程序:

    #include "stdafx.h"
    
    extern "C"_declspec(dllexport) int add(int a, int b);
    
    int add(int a, int b) {
        return a + b;
    }

    然后编译成一个dll文件(比如CDLL.dll),放到当前目录下,然后编写JNA程序调用即可:
    public class DllTest {
    
        public interface CLibrary extends Library {
            CLibrary INSTANCE = (CLibrary)Native.loadLibrary("CDLL", CLibrary.class);
    
            int add(int a, int b);
        }
    
        public static void main(String[] args) {
            int sum = CLibrary.INSTANCE.add(3, 6);
    
            System.out.println(sum);
        }
    }





    简单案例
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    import com.sun.jna.Platform;
    
    interface HelloInter extends Library{
     int toupper(int ch);
     double pow(double x,double y);
     void printf(String format,Object... args);
    }
    public class HelloWorld {
    
     public static void main(String [] args){
     HelloInter INSTANCE =
    (HelloInter)Native.loadLibrary(
    Platform.isWindows()?"msvcrt":"c",
    HelloInter.class);
     INSTANCE.printf("Hello, Worldn");
     String [] strs = new String[]{"芙蓉","如花","凤姐"};
     for (int i=0;i < strs.length;i++) {
     INSTANCE.printf("人物 %d: %sn", i, strs[i]);
    }
     System.out.println("pow(2d,3d)=="+INSTANCE.pow(2d, 3d));
    System.out.println("toupper('a')=="+(char)INSTANCE.toupper((int)'a'));
    }
    
    }

    显示结果:
    pow(2d,3d)==8.0
    toupper('a')==A
    Hello, World
    人物 0: 芙蓉
    人物 1: 如花
    人物 2: 凤姐

    说明:

    HelloInter接口中定义的3个函数全是C语言函数库中的函数,其定义格式如下:

    int toupper(int ch)
    double pow( double x, double y )
    int printf(const char* format, ...)

    C语言函数库中有很多个函数,但是我们只用到了这3个函数,所以其他的函数不需要声明在接口中。


    JNA模拟结构体

    例:使用 JNA调用使用 Struct的 C函数
    假设我们现在有这样一个C 语言结构体
    struct UserStruct{
    long id;
    wchar_t* name;
    int age;
    };
    使用上述结构体的函数
    #define MYLIBAPI extern "C" __declspec( dllexport )

    MYLIBAPI void sayUser(UserStruct* pUserStruct);
    对应的Java 程序中,在例1 的接口中添加下列代码:

    public static class UserStruct extends Structure{
        public NativeLong id;
        public WString name;
        public int age;
        public static class ByReference extends UserStruct
    implements Structure.ByReference { }
        public static class ByValue extends UserStruct implements
    Structure.ByValue
    { }
    }
    public void sayUser(UserStruct.ByReference struct);
    

    Java中的代码
       UserStruct userStruct=new UserStruct ();
       userStruct.id=new NativeLong(100);
       userStruct.age=30;
       userStruct.name=new WString("奥巴马");
       TestDll1.INSTANCE.sayUser(userStruct);

    Structure说明

    现在,我们就在Java 中实现了对C 语言的结构体的模拟。这里,我们继承了Structure 类,用这个类来模拟C 语言的结构体。

    必须注意,Structure 子类中的公共字段的顺序,必须与C 语言中的结构的顺序一致。否则会报错!因为,Java 调用动态链接库中的C 函数,实际上就是一段内存作为函数的参数传递给C函数。动态链接库以为这个参数就是C 语言传过来的参数。同时,C 语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA 中模拟的结构体的变量顺序绝对不能错。如果一个Struct 有2 个int 变量。Int a, int b如果JNA 中的次序和C 中的次序相反,那么不会报错,但是数据将会被传递到错误的字段中去。
    Structure 类代表了一个原生结构体。当Structure 对象作为一个函数的参数或者返回
    值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构
    体本身。
    另外,Structure 类有两个内部接口Structure.ByReference 和Structure.ByValue。这两个接
    口仅仅是标记,如果一个类实现Structure.ByReference 接口,就表示这个类代表结构体指针
    如果一个类实现Structure.ByValue 接口,就表示这个类代表结构体本身
    使用这两个接口的实现类,可以明确定义我们的Structure 实例表示的是结构体的指针
    还是结构体本身。
    上面的例子中,由于Structure 实例作为函数的参数使用,因此是结构体指针。所以这

    里直接使用了UserStruct userStruct=new UserStruct ();

    也可以使用UserStruct userStruct=new UserStruct.ByReference ();
    明确指出userStruct 对象是结构体指针而不是结构体本身。

    JNA模拟复杂结构体
    C 语言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模
    拟任何类型的对象。
    JNA 也可以模拟这类复杂的结构体。

    struct CompanyStruct{
    long id;
    wchar_t* name;
    UserStruct users[100];
    int count;
    };


    JNA 中可以这样模拟:
    public static class CompanyStruct extends Structure{
    public NativeLong id;
    public WString name;
    public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
    public int count;
    }


    这里,必须给users 字段赋值,否则不会分配100 个UserStruct 结构体的内存,这样JNA
    中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。


    例:结构体内部可以包含结构体对象的指针的数组

    struct CompanyStruct2{
    long id;
    wchar_t* name;
    UserStruct* users[100];
    int count;
    };


    JNA 中可以这样模拟:
    public static class CompanyStruct2 extends Structure{
    public NativeLong id;
    public WString name;
    public UserStruct.ByReference[] users=new
    UserStruct.ByReference[100];
    public int count;
    }


    测试代码:
    CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();
    companyStruct2.id=new NativeLong(2);
    companyStruct2.name=new WString("Yahoo");
    companyStruct2.count=10;
    UserStruct.ByReference pUserStruct=new
    UserStruct.ByReference();
    pUserStruct.id=new NativeLong(90);
    pUserStruct.age=99;
    pUserStruct.name=new WString("杨致远");
    // pUserStruct.write();
    for(int i=0;i<companyStruct2.count;i++){
    companyStruct2.users[i]=pUserStruct;
    }
    
    TestDll1.INSTANCE.sayCompany2(companyStruct2);
    执行测试代码,报错了。这是怎么回事?
    考察JNI 技术,我们发现Java 调用原生函数时,会把传递给原生函数的Java 数据固定
    在内存中,这样原生函数才可以访问这些Java 数据。对于没有固定住的Java 对象,GC 可以
    删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果原生函数访问没有被固
    定住的Java 对象,就会导致调用失败。
    固定住哪些java 对象,是JVM 根据原生函数调用自动判断的。而上面的CompanyStruct2
    结构体中的一个字段是UserStruct 对象指针的数组,因此,JVM 在执行时只是固定住了
    CompanyStruct2 对象的内存,而没有固定住users 字段引用的UserStruct 数组。因此,造成
    了错误。
    我们需要把users 字段引用的UserStruct 数组的所有成员也全部固定住,禁止GC 移动
    或者删除。
    如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。
    Structure 类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。


    案例一:获取本地时间(Get local time)

    如果你在Java Native Access 首页 看过“JNA如何入门”,你就会知道一个很简单的关于调用Windows 平台下的API函数:GetSystemTime() 的JNA示例。这个不完整的例子只是展示了JNA的基本特点。(在例子的基础上,我做了一个更完整的基于Windows的例子来介绍JNA)我在Windows平台下完善了这个例子来介绍JNA。

    第一例子基于Windows GetLocalTime() API函数返回本地当前的时间和日期。和GetSystemTime()不同的是,返回的时间/日期是协调通用时间(UTC)格式的,GetLocalTime()返回的时间/日期信息的格式是根据当前时区来表示。

    在一个Java程序中使用JNA调用GetLocalTime,你需要知道这个函数所在的Windows平台下的动态链接库(DLL)的名称(和可能所在的地理区域)。我们发现GetLocalTime()和GetSystemTime在同一个DLL文件中:kernel32.dll。你还需要知道GetLocalTime()在C语言环境中的申明。申明如下Listing 1:

    Listing 1. GetLocalTime在C语言中的申明

    typedef struct
    {
       WORD wYear;
       WORD wMonth;
       WORD wDayOfWeek;
       WORD wDay;
       WORD wHour;
       WORD wMinute;
       WORD wSecond;
       WORD wMilliseconds;
    }
    SYSTEMTIME, *LPSYSTEMTIME;
    
    VOID GetLocalTime(LPSYSTEMTIME lpst);

    这个基于C语言的申明表明传到这个函数的参数数目和类型。在这个例子中,只有一个参数---一个指向Windows SYSTEMTIME结构体的指针。而且,每个结构体成员的类型是16bit长度的无符号整型。根据这些信息,你能够创建一个完全描述GetLocalTime()函数的接口,如Listing 2中所示:

    Listing 2. Kernel32.java
    
    // Kernel32.java
    
    import com.sun.jna.*;
    import com.sun.jna.win32.*;
    
    public interface Kernel32 extends StdCallLibrary
    {
       public static class SYSTEMTIME extends Structure
       {
          public short wYear;
          public short wMonth;
          public short wDayOfWeek;
          public short wDay;
          public short wHour;
          public short wMinute;
          public short wSecond;
          public short wMilliseconds;
       }
    
       void GetLocalTime (SYSTEMTIME result);
    }
    




    Kernel32 接口(The Kernel32 interface)

    因为JNA使用通过一个接口来访问某个库中的函数,Listing 2表示了一个描述GetLocalTime()的接口。根据约定,我把接口命名为Kernel32是因为GetLocalTime()在Windows的kernel32.dll库。

    这个接口必须继承com.sun..jna.Library接口。因为Windows API函数遵循stdcall调用协议(stdcall calling convention),为Windows API申明的接口也必须继承com.sun.jna.win32. StdCallLibrary接口。因此这个接口共继承了Library 和 com.sun.jna.win32.StdCall两个接口。

    在前面,你已经知道了GetLocalTime() 需要一个指向SYSTEMTIME结构体的指针作为它唯一的参数。因为Java不支持指针,JNA是通过申明一个com.sun.jna.Structure的子类来代替的。根据java文档中抽象类的概念,在参数环境中,Structure相当于C语言的struct*。

    在SYSTEMTIME类中的字段和C结构体中的相对应的属性字段的顺序是一一对应的。保证字段顺序的一致性是非常重要的。例如,我发现交换wYear和wMonth会导致wYear和wMonth值互换。

    每个字段在java中是short integer类型的。按照JNA首页上 “默认类型映射”章节给出的提示,这个short integer分配类型是正确。然而,我们应该知道一个重要的区别:Windows平台下的WORD类型等同于C语言环境中的16-bit的无符号的short integer,而java中short integer是16-bit有符号的short integer。

    一个类型映射的问题

    通过比较一个API 函数返回的整型值,你会发现Windows/C 语言的无符号整型和Java语言的有符号整型的JNA类型映射是有问题的。在比较的过程中,如果你不细心,那么错误的执行过程可能导致决定性情况。导致这种后果是因为忘记任何数值的符号位的确定是根据:在无符号整型的情况下会被解释为正号,而在有符号整型的进制中被理解为负号的。

    通过Kernel32获取本地时间(Access the local time with Kernel32)

    JNA首页上的GetSystemTime()示例已经表明必须使用预先申明的接口为本地库分配一个实例对象。你可以通过com.sun.jna.Native类中静态公用方法loadLibrary(String name, Class interfaceClass)来完成上述的目标。Listing 3 所示:

    Listing 3. LocalTime.java
    
    // LocalTime.java
    
    import com.sun.jna.*;
    
    public class LocalTime
    {
       public static void main (String [] args)
       {
          Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32",
                                                        Kernel32.class);
          Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();
          lib.GetLocalTime (time);
          System.out.println ("Year is "+time.wYear);
          System.out.println ("Month is "+time.wMonth);
          System.out.println ("Day of Week is "+time.wDayOfWeek);
          System.out.println ("Day is "+time.wDay);
          System.out.println ("Hour is "+time.wHour);
          System.out.println ("Minute is "+time.wMinute);
          System.out.println ("Second is "+time.wSecond);
          System.out.println ("Milliseconds are "+time.wMilliseconds);
       }
    }
    
    



    Listing 3 执行Kernel32 lib = (Kernel32) Native.loadLibrary ("kernel32", Kernel32.class);来分配一个Kernel32实例对象并且装载kernel32.dll。因为kernel32.dll是Windows平台下标准的dll文件,所以不要指定访问这个库的路径。然而,如果找不到这个dll文件,loadLibrary()会抛出一个UnsatisfiedLinkError异常。

    Kernel32.SYSTEMTIME time = new Kernel32.SYSTEMTIME ();创建了一个SYSTEMTIME结构体的示例。初始化后下面是lib.GetLocalTime (time);,这句话使用本地的时间/日期来给这个实例赋值。几个System.out.println()语句是输出这些值。

    编译和运行这个应用(Compile and run the application)

    这部分很容易。假设jna.jar、Kernel32.java和LocalTime.java是放在当前文件夹中,调用java –cp jna.jar;. LocalTime.java来编译这个应用的源代码。如果在Windows平台下,调用invoke java –cp jna.jar;. LocalTime 来运行这个应用。你可以得到类似与Listing 4的输出结果:

    Listing 4. 从LocalTime.java生成的输出

    Year is 2007
    Month is 12
    Day of Week is 3
    Day is 19
    Hour is 12
    Minute is 35
    Second is 13
    Milliseconds are 156

    案例二:调用本地的
    使用JNA的调用本地方法的时候需要自定义数据结构,下面我们通过调用Windows提供的的锁定工作站方法来了解一下JNA。

        1、首先查询Windows API知道锁定工作站的方法在user32.dll中定义,接下来定义一个接口来继承JNA的Library.java接口,用作声明DLL库文件,这里我们就把它命名为User32:

        public interface User32 extends Library {}

    复制代码
    2、查询user32.dll提供的API得知锁定工作方法是LockWorkStation,返回类型是boolean型,在User32.java中新增相应的方法:

        boolean LockWorkStation();

    复制代码
    这样我们的User32.java这个类就定义好了。接下来我们写测试程序进行调用。

        3、编写测试类比如LockWorkStation.java,首先通过JNA的Native类加载对应的dll:

        User32 user32 = (User32) Native.loadLibrary("user32", User32.class);

    复制代码
    然后就可以调用LockWorkStation方法了,完整代码如下:

     
       public class LockWorkStation {
            public static void main(String[] args) {
              User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
              user32.LockWorkStation();
            }
        }



    复制代码
    这里说明一下loadLibrary方法中第一个参数是需要加载的dll文件名称,第二个参数的作用是让JNA使用这个类的加载器去加载DLL文件,加载顺序是,先从Users.class类的当前文件夹找,如果没有找到,再在工程当前文件夹下面找win32/win64文件夹,找到后搜索对应的dll文件,如果找不到再到WINDOWS下面去搜索,再找不到就会抛异常了。以TWAINDSM.dll将文件放到工程的根文件夹可以按照下面这个格式放:

    附件: jnaexplorer.JPG

      上面的User32定义的是dll库文件,有时会碰到比如HANDLE、POINT、WORD和MSG等数据类型,有些数据类型JNA中没有提供,需要自己定义,根据作用的不同,定义的时候继承的父类也不一样,比如HANDLE定义方法是:

      
     class HANDLE extends PointerType {
                private boolean immutable;
                public HANDLE() { }
                public HANDLE(Pointer p) { setPointer(p); immutable = true; }
              public Object fromNative(Object nativeValue, FromNativeContext context) {
                    Object o = super.fromNative(nativeValue, context);
                    if (INVALID_HANDLE_VALUE.equals(o))
                        return INVALID_HANDLE_VALUE;
                    return o;
                }
                public void setPointer(Pointer p) {
                    if (immutable)
                        throw new UnsupportedOperationException("immutable reference");
                    super.setPointer(p);
                }
            }


            HANDLE被定义为类型安全的指针。而POINT用作表示坐标,不需要这么复杂,定义方式为:
      
     class POINT extends Structure {
                public int x, y;
                public POINT() { }
                public POINT(int x, int y) { this.x = x; this.y = y; }
          }


    使用JNA的过程中也不一定会一帆风顺,比如会抛出”非法内存访问”,这时候检查一下变量是否==null。还有内存对齐的问题,当从内存中获取图片信息进行保存的时候,如果内存对齐处理不好,就会抛出很严重的异常,导致JVM异常退出,JNA提供了四种内存对齐的方式,分别是:ALIGN_DEFAULT、ALIGN_NONE、ALIGN_GNUC和ALIGN_MSVC。ALIGN_DEFAULT采用平台默认的对齐方式(推荐);ALIGN_NONE是不采用对齐方式;ALIGN_GNUC为针对linux/gcc操作系统的对齐方式。ALIGN_MSVC为针对win32/msvc架构的内存对齐方式。

        JNA也提供了一种保护机制.比如防止JNA出现异常不会导致JVM异常退出,默认是开启这个功能的,开启方式为 System.setProperty(“jna.protected”,”true”); 记得要在JNA加载dll文件之前调用,然后try {...} catch(Throwable e)异常,不过你也不要期望过高,不要以为加上这个就万事大吉,出现”非法内存访问”的时候还是会束手无策。JNA也提供了一种保护机制.比如防止JNA 出现异常不会导致JVM异常退出,默认是开启这个功能的,开启方式为 System.setProperty(“jna.protected”,”true”); 记得要在JNA加载dll文件之前调用,然后try {...} catch(Throwable e)异常,不过你也不要期望过高,不要以为加上这个就万事大吉,出现”非法内存访问”的时候还是会束手无策。


    参考文章:

    深入浅出JNA—快速调用原生函数:http://www.doc88.com/p-31975835542.html

    JNA—JNI终结者:http://blog.csdn.net/shendl/article/details/3589676

    JNA的使用:http://xbgd.iteye.com/blog/1044864

    深入理解JNA—模拟C语言结构体:http://blog.csdn.net/shendl/article/details/3599849


    展开全文
  • Java通过JNA方式调用DLL

    万次阅读 热门讨论 2018-04-07 09:26:33
    Java调用C++动态链接库的方式很多,有jnative,jna等。这里介绍如何通过jna的方式调用动态链接库。调用代码很简单,就是需要注意几个问题。 1、动态链接库编译时选择的平台。如果通过x86平台编译,那么只能使用32位...

    Java调用C++动态链接库的方式很多,有jnative,jna等。这里介绍如何通过jna的方式调用动态链接库。

    调用代码很简单,就是需要注意几个问题。

          1、动态链接库编译时选择的平台。如果通过x86平台编译,那么只能使用32位jdk环境加载,如果要使用64位jdk,必须使用x64平台编译。

               这一点很重要,如果设置的不对,即使后面编码,动态链接库的路径均没有问题,调用的时候仍然会报错。

    java.lang.UnsatisfiedLinkError: Unable to load library 'Dll1': ÕҲ»µ½ָ¶¨

           2、动态链接库路径。一般放在项目根路径下就可以了。

    上面的两个注意事项搞清楚了,那么后面编码其实很容易。

    1、创建动态链接库,并采用x64位平台生成。这里使用的是visual studio 2017开发工具。

        1、创建动态链接库空项目

         2、手工新建Dll1.def模块定义文件

                 LIBRARY Dll1

                 EXPORTS
                 add
                 substract

                 printHello

         3、编写源文件

              #include "stdafx.h"
              #include <iostream>
              using namespace std;
              int add(int a, int b) {
            return a + b;
              }
              int substract(int a, int b) {
            return a - b;
              }
              void printHello() {
             cout << "Hello,JNA" << endl;

              }

        4、右键项目->属性->链接->输入->右侧模块定义文件->选择Dll1.def

        5、采用release作为活动解决方案配置,x64作为活动解决方案平台生成dll。确保后面64位jdk能够顺利加载dll。

         

    2、下载jna-3.1.0.jar放入到java项目类路径下。

    3、编码,dll文件放在项目根路径下之后,代码里面指定dll就可以不用写绝对路径,而且不用指定后缀".dll"。

    package com.xxx.jna;
    import com.sun.jna.Library;
    import com.sun.jna.Native;
    public class DllTest {
    
    	public interface Dll extends Library{		
    		Dll instance = (Dll)Native.loadLibrary("Dll1", Dll.class);
    		public int add(int a,int b);
    		public int substract(int a,int b);
    		public void printHello();
    	}
    
    	public static void main(String[] args) {
    		int sum = Dll.instance.add(5, 3);
    		int sub = Dll.instance.substract(5, 3);
    		System.out.println("add(5,3) = "+sum);
    		System.out.println("substract(5,3) = "+sub);
    		Dll.instance.printHello();
    	}
    
    }

    4、运行结果截图。

    展开全文
  • JNA

    2014-10-23 14:01:00
    给大家介绍一个最新的访问本机代码的Java框架—JNAJNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。 JNA项目地址:https://jna.dev.java.net/ ...
  • 前言: 公司最近在做摄像头相关的业务,摄像头配套的软件,一般都是C++写的,windows的...现在项目开发告一段落(前前后后对接了三个摄像头设备的sdk,百来个方法),对jna有一定程度上的掌握了,整理成笔记。 ...
  • JNA 相关所有jar 资源

    2019-02-22 13:59:27
    Download jna-4.0.0.jar Download jna-jnaerator-0.10-sources.jar Download jna-3.0.3-patch-1.jar Download jna-3.4.0-sources.jar
  • JNA示例

    2019-03-22 02:14:01
    NULL 博文链接:https://wen866595.iteye.com/blog/974826
  • JNA简单示例

    千次阅读 2019-10-12 11:57:06
    JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。非常强大、易用,功能上类似与.NET的P/Invoke。 我们知道,使用JNI调用.dll/.so共享类库是非常非常...
  • jna

    2020-05-12 19:42:21
    https://github.com/lovedabaozi/Jna
  • JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。使用JNI调用共享类库(.dll/.so文件)是非常麻烦的事情,既需要编写java代码,又要编写C语言的代理...
  • JNA

    2010-10-09 09:25:23
    介绍 给大家介绍一个最新的访问本机代码的Java框架—JNA。...JNA(Java Native Access)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。 JNA项目地址:https://jna...
  • jna-3.5.2jar

    2018-10-31 17:36:50
    JNA(JavaNativeAccess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在 经典的JNI的基础之上的一个框架。 JNA项目地址:https://jna.dev.java.net/ JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、...
  • JNA探索之路系列】之二:JNA API

    千次阅读 2015-07-12 15:20:33
    作者:郭嘉 ...github:https://github.com/AllenWell工欲善其事,必先利其器,本篇文章就来详细探讨在JNA开源项目的工程中提供了哪些应用接口,它们分别提供了...JNA4.1.0的包结构如下所示: com.sun.jna com.sun.jna.p
  • jna-4.2.2 官方原版下载

    2016-08-29 10:49:43
    Most simple questions will be answered on the list, but more complicated work, new features or target platforms can be negotiated with any of the JNA developers (this is how several of JNA's features...
  • 初学JNA

    2012-03-26 15:20:00
    JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。 JAVA通过JNI调用本地方法,而本地方法是以库...
  • jna-4.5.1 , jna-4.5.1-sources , jna-platform-4.5.1 jar包 JNA全称Java Native Access,是一个建立在经典的JNI技术之上的Java开源框架(https://github.com/twall/jna)。JNA提供一组Java工具类用于在运行期动态...
  • 最新JNA JAR包

    热门讨论 2012-03-21 16:49:30
    Java Native Access (JNA) has a single component, jna.jar; the supporting native library (jnidispatch) is included in the jar file. JNA is capable of extracting and loading the native library on its ...
  • jna-4.5.0.jar

    2017-11-21 21:05:20
    项目是要求用JAVA来调用delphi提供的动态链接库,而且硬件厂商已经提供了库以及接口文档.由于以前没有接触过这样的问题,所以考虑到要跟硬件设备打交道,首先通过查相关的资料,得出在JAVA里面用JNI...将JNA的资源分享下
1 2 3 4 5 ... 20
收藏数 9,014
精华内容 3,605
关键字:

jna