`

深入解析JNA—模拟C语言结构体(1)

    博客分类:
  • jna
阅读更多

原文 http://blog.csdn.net/shendl/archive/2008/12/26/3599849.aspx

 

深入解析 JNA— 模拟 C 语言结构体

 

 

 

前言

前几天写《 JNA--JNI 终结者》一文介绍 JNA 框架。写完之后才发现,忘了写比较有难度的 C 语言 Struct 的模拟了。

今天就补上这篇文章,介绍 Struct

不写怎样模拟 C 语言结构体,就不能算是真正解决了调用动态链接库的问题。

C 语言的结构体用得实在是太广泛了。

 

 

首先说明一点,本文中大量把模拟 Struct 的类写作为接口的内部类。

这不是 JNA 规定的,而是一个编程习惯。

因为这些结构体( Structure 类的子类),一般没有重用的价值,因此写成内部类比较方便。自然,你也可以把结构体写成一般的类。

 

 

 

 

3   使用 JNA 调用使用 Struct C 函数

C 语言开发

继续使用例 2 中的那个 VSC++ dll 项目。

增加一个结构和使用该结构的函数。

头文件增加如下:

 

#define MYLIBAPI  extern    "C"      __declspec ( dllexport ) 

struct UserStruct{

   long id;

   wchar_t *  name;

   int age;

 

};

 

MYLIBAPI void sayUser(UserStruct* pUserStruct);

 

JNA 程序

对应的 Java 程序中,在例 2   接口

 

/*

         * 定义一个类,模拟 C 语言的结构

         * */

       public static class UserStruct extends Structure{

          

          

 

           public NativeLong id ;

           public WString name ;

           public int age ;

       }

      

       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);

 

Struct 说明

    现在,我们就在 Java 中实现了对 C 语言的结构的模拟。

这里,我们继承了 Structure 类,用这个类来模拟 C 语言的结构。

 

必须注意, Structure 子类中的公共字段的顺序,必须与 C 语言中的结构的顺序一致。否则会报错!

 

因为, Java 调用 dll 中的 C 函数,实际上就是一段内存作为函数的参数传递给 dll

Dll 以为这个参数就是 C 语言传过来的参数。

同时, C 语言的结构是一个严格的规范,它定义了内存的次序。因此, JNA 中模拟的结构的变量顺序绝对不能错。

如,一个 Struct 2 int 变量。   Int a, int b 

如果 JNA 中的次序和 C 中的次序相反,那么不会报错,但是得到的结果是相反的!

 

 

 

4   使用 JNA 调用使用嵌套 Struct 数组的 C 函数

如果 C 语言中的结构体是复杂的嵌套的结构体,该怎么办呢?

继续在上面例 3 的基础上扩充。

 

C 语言开发

头文件增加如下:

struct CompanyStruct{

    long id;

   wchar_t *  name;

   UserStruct   users[100];

   int count;

 

};

 

MYLIBAPI void sayCompany(CompanyStruct* pCompanyStruct);

 

源文件:

   

void sayCompany(CompanyStruct* pCompanyStruct){

 

  std::wcout.imbue(std::locale( "chs" ));

 

   std::wcout<<L "ID:" <<pCompanyStruct->id<<std::endl;

   std::wcout<<L "公司名称:" <<pCompanyStruct->name<<std::endl;

    std::wcout<<L "员工总数:" <<pCompanyStruct->count<<std::endl;

      

     for ( int i=0;i<pCompanyStruct->count;i++){

        sayUser(&pCompanyStruct->users[i]);

    

     }

  

 

 

}

 

JNA 程序

Java 程序中,在原来的接口上加上如下代码:

 

    public static class CompanyStruct extends Structure{

                     public NativeLong id ;

           public WString  name ;

                    

           public UserStruct.ByValue[] users = new UserStruct.ByValue[100];

           public int count ;

          

          

       }

      

      public    void sayCompany(CompanyStruct pCompanyStruct);

 

对原来的 UserStruct 类进行改写:

/*

         * 定义一个类,模拟 C 语言的结构

         * */

       public static class UserStruct extends Structure{

          

             public static class ByReference extends UserStruct implements Structure.ByReference { }

             public static class ByValue extends UserStruct implements Structure.ByValue

  { }

 

           public NativeLong id ;

           public WString name ;

           public int age ;

       }

 

调用 JNA 程序:

 

CompanyStruct companyStruct= new CompanyStruct();

       companyStruct. id = new NativeLong(1);

       companyStruct. name = new WString( "Google" );

       companyStruct. count =9;

   

       UserStruct.ByValue userStructValue= new UserStruct.ByValue();

       userStructValue. id = new NativeLong(100);

       userStructValue. age =30;

       userStructValue. name = new WString( " 沈东良 " );

       for ( int i=0;i<companyStruct. count ;i++){

           companyStruct. users [i]=userStructValue;

              }

       TestDll1. INSTANCE .sayCompany(companyStruct);

说明

可以看到,程序正确输出了。

    读者也许会有一些疑问。

 

 

1 ,为什么我们要给 UserStruct 这个结构添加 2 个内部类呢?

Structure 类的 API 说明,我们知道,这个类内部有 2 个接口:

static interface

Structure.ByReference
          Tagging interface to indicate the address of an instance of the Structure type is to be used within a
Structure definition rather than nesting the full Structure contents.

static interface

Structure.ByValue
          Tagging interface to indicate the value of an instance of the
Structure type is to be used in function invocations rather than its address.

2 个内部接口是标记,内部什么都没有。

在运行时, JNA 的执行框架会使用反射查看你是否实现了这 2 个接口,然后进行特定的处理。

(这种技术在 java 标注 Annotation 之前很流行。现在可以使用运行时 Annotation 实现同样的效果。

JNA 项目据说 1999 年就启动了,使用这样的老技术不足为奇。只是很奇怪,为什么国内都没怎么听说过。我也是最近偶然在国外的网站上发现它的。一试之下,爱不释手,令我又对 Java 的桌面应用信心百倍!

 

 

如果你的Struct 实现 Structure.ByReference 接口,那么JNA 认为你的Struct 是一个指针。指向C 语言的结构体。

如果你的Struct 实现 Structure.ByValue 接口,那么JNA 认为你的Struct 是值类型,就是C 语言的结构体。

如果你不实现这 2 个接口,那么就相当于你实现了 Structure.ByReference 接口。

因此,在例3 中,我没有实现这2 个接口。

 

 

2 C 语言中,结构体内部必须进行数组定义。 Java 中最好也这样做。

C 语言的结构体是一段连续的内存,内存的大小是编译时确定的。

因此,数组必须定义。否则编译不会成功。

 

对应的 Java 类中,我们也应该在类定义时为数组定义。尽管实际上在使用时再赋值也可以。

但是,强烈不建议你这样做。

如,上面

public UserStruct.ByValue[] users = new UserStruct.ByValue[100];

定义 100 个元素的数组,如果你不再类内部定义。

而在使用时定义,如果你没有正确赋值,没有定义为 100 个元素,就会出错。

 

从表面上看, CompanyStruct 占用的内存是:

NativeLong   id

WString  name ;

 

分享到:
评论

相关推荐

    java通过jna返回结构体例子.rar

    Java通过JNA(Java ...通过理解JNA的工作原理,创建对应的Java结构体类,以及正确地传递和解析结构体指针,可以有效地在Java和C之间进行数据交换。对于需要跨平台或利用C库的Java开发者来说,这是一个重要的技能。

    JNA 转java接口以及指针结构体解析

    在"JNA 转java接口以及指针结构体解析"这个主题中,我们将深入探讨如何使用JNA来处理C语言中的结构体和指针。 首先,理解JNA的基本工作原理至关重要。JNA通过定义一个`Interface`,该接口中的方法对应于要调用的...

    JNA 复杂结构体传递

    本文将深入探讨JNA在处理复杂结构体传递,包括结构体数组和结构体指针方面的知识。 JNA是一种Java库,允许Java代码直接调用本机API,而无需编写JNI代码。相比于JNI,JNA的使用更为简单和直观,但可能在性能上稍逊...

    jna调用C语言函数库dll、so例子

    在标题中提到的“jna调用C语言函数库dll、so例子”,这里的"dll"是Windows系统中的动态链接库(Dynamic Link Library),"so"则是Linux和其他类Unix系统中的共享对象(Shared Object)。这两种都是C语言或其他编译...

    Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源

    Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源

    如何使用JNA调用本地C/C++动态链接库详细示例代码

    1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并释放空间 本资源包括三个工程: C++动态链接库; VC调用...

    C++头文件转JAVA JNA接口类

    1. **准备C++头文件**:确保你的C++头文件(.h文件)包含了所有需要调用的函数和结构体声明。这些函数应该遵循C语言的调用约定,因为JNA主要支持C风格的接口。 2. **创建SWIG接口文件**:创建一个SWIG接口文件(.i...

    jna-4.4.0.jar

    《深入解析JNA-4.4.0.jar:Java Native Access技术详解》 在Java编程领域,有时我们需要调用操作系统底层的功能,例如访问硬件设备、操作系统API或使用C/C++库。这时,Java Native Interface (JNI) 和 Java Native ...

    jna控制键盘

    1. **引入JNA库**:首先,你需要在项目中添加JNA库的依赖,这可以通过Maven或Gradle等构建工具来完成。 2. **定义接口**:JNA允许我们定义一个Java接口,该接口对应于要调用的本机库函数。例如,我们可以定义一个...

    深入浅出JNA

    ### 深入浅出JNA — 快速调用原生函数 #### 为什么需要JNA? 在软件开发过程中,特别是在需要与操作系统底层交互或利用高性能计算资源时,经常需要用到原生函数(通常指C/C++语言编写的库)。Java作为一种高级语言...

    深入浅出JNA—快速调用原生函数

    JNA通过映射机制支持常见的数据类型,包括整型、浮点型、字符串等基本数据类型的转换,并提供了一套丰富的API来模拟原生语言中的指针、结构体等复杂数据结构。通过JNA,开发者可以更轻松地调用原生函数,也更容易...

    JNA实例 JNA实例 JNA实例

    通过这个JNA实例,我们可以看到Java程序是如何利用JNA库来调用C语言编写的函数和处理结构体数据的。这种技术不仅简化了跨语言编程的过程,还提高了代码的可维护性和扩展性。对于需要与现有C/C++代码集成的项目来说,...

    java 控制鼠标*键盘的 jna 库32位

    这段代码展示了如何使用JNA加载`user32.dll`库并定义`SendInput()`函数,接着可以创建`INPUT`结构体实例,填充模拟的鼠标或键盘事件,调用`SendInput()`来发送这些事件。 通过这种方式,Java开发者可以利用JNA库在...

    利用jna在java下调用c语言库文件

    本示例将详细解析如何使用JNA在Java中调用C语言库文件。 首先,我们需要理解JNA的工作原理。JNA通过映射Java方法到本地函数来实现调用,这得益于其内建的类型映射系统。Java中的数据类型会自动转换为对应的C语言...

    jna资料3.0api

    在JNA3.09 API中,我们可以看到几个关键的概念和技术,用于模拟C语言中的各种数据类型和参数传递方式。 1. **模拟普通传值参数**: 在C语言中,函数接收基本类型参数,如`int`, `float`, `long`等。JNA允许你直接...

    深入浅出JNA—快速调用原生函数1.0

    当需要与C语言中的结构体或联合体交互时,JNA提供了一种称为`Structure`的抽象基类来帮助模拟这些复杂的原生数据结构。通过继承`Structure`类,我们可以定义自己的结构体模型,使得与原生代码的交互更为直观。 ####...

Global site tag (gtag.js) - Google Analytics