`

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

阅读更多

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

 

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

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

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

 

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

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

如,上面

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

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

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

 

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

NativeLong   id

WString  name ;

                    

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

           public int count ;

4 个元素占用的内存的总和。

 

由于 Java 的数组是一个对象, users 中实际保存的也应该是一个引用,也就是指针, 32bit

 

那么 CompanyStruct 类占用的内存就比对应的 C 结构体:

struct CompanyStruct{

    long id;

   wchar_t *  name;

   UserStruct   users[100];

   int count;

};

 

小很多。内存少用很多。

我在例 3 的说明中曾经说过:

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

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

那么, JNA 怎么还能正确调用 C 函数呢。

 

事实上,在调用 C 函数时, JNA 会把 UserStruct 类的实例的所有数据全部 *100 倍。(我的例子里数组是 100 个,你的例子当然可以有不一样的数值)

这样, Java 中的结构体的内存量就和 C 语言的 CompanyStruct 结构体占据相同大小和结构的内存,从而正确调用 C 函数。

 

5   使用 JNA 调用使用嵌套 Struct 的指针的数组的 C 函数

现在给大家看看最复杂的 Struct 的例子。

Struct 中嵌套的是一个结构体的指针的数组。

 

C 语言代码

struct CompanyStruct2{

    long id;

   wchar_t *  name;

  UserStruct*  users[100];

  // UserStruct   users[100];

   int count;

 

};

MYLIBAPI void sayCompany2(CompanyStruct2* pCompanyStruct);

 

这里,把刚才使用的 UserStruct 数组换成 UserStruct 指针的数组。

 

 

JNA 代码

   

  public static class CompanyStruct2 extends Structure{

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

           public NativeLong id ;

           public WString  name ;

      

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

           public int count ;

          

          

       }

      public void sayCompany2(CompanyStruct2.ByReference  pCompanyStruct);

 

   

测试代码:

 

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

    //  TestDll1.INSTANCE.sayUser(pUserStruct);

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

           companyStruct2. users [i]=pUserStruct;

              }

      

       TestDll1. INSTANCE .sayCompany2(companyStruct2);

 

 

程序说明 ----Pin 锁住 Java 对象:

因为是结构体的指针的数组,所以,我们使用了

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

来对应 C 语言中的

UserStruct*  users[100];

 

但是,有问题,如果去除

pUserStruct.write();

这一行代码,就会报错。

如果去除 pUserStruct.write();

但是使用     TestDll1.INSTANCE.sayUser(pUserStruct);

也不会有问题。

 

这是怎么回事?

 

    原来,错误的原因就是内存锁定!

 

java 内存锁定机制和 JNI JNA 调用

我们知道, java 的内存是 GC 管理的。它会自动管理 JVM 使用的堆内存。删除不再使用的内存,并把 Java 对象使用的内存自动移动,以防止内存碎片增多。

因此,虽然 Java 的引用实际上就是指针,但还是和指针不同。 Java 中不能直接使用指针。因为对象在内存中的地址都不是固定的。说不准什么时候 GC 就把它给移位了。

 

如果使用 JNI JNA 等技术调用 C 函数。那么就会有问题。因此, C 语言使用的是指针。如果想要获取 C 函数的返回值。那么我们必须提供一块内存给 C 语言访问。而 C 语言是不知道你 Java 的引用的。它只能访问固定的内存地址。

如果 GC Java 对象移来移去,那么 C 函数就没办法和 Java 交互了。

 

因此,在使用 JNI JNA 时,都会把 Java 对象锁住。 GC 不再管理。不删除,也不移动位置。由此出现的内存碎片,也不管了!

这种技术的术语是 PIN   .NET 也有同样的概念。

 

 

上面 TestDll1.INSTANCE.sayUser(pUserStruct); 这个调用是 JNA 调用。这个操作就把 pUserStruct 这个 Java 对象锁住了。

 

4 中,嵌套的是结构体,因此也会直接把 CompanyStruct 类的实例锁住。

 

但是例 5 中,嵌套的是结构体的指针的数组。   CompanyStruct 2 类的实例 companyStruct2 在执行

TestDll1.INSTANCE.sayCompany2(companyStruct2);

时是被锁住了,可以 companyStruct2 users 指针数组 的成员:

pUserStruct   都没有被锁住。

 

怎么办呢?  

难道每一个 UserStruct.ByReference 的实例都先调用一遍不需要的

TestDll1.INSTANCE.sayUser(pUserStruct);  方法?

 

没事! JNA 开发人员早已想到了:

Structure   类中有方法:

write

public void write
()

Writes the fields of the struct to native memory


writeField

public void writeField
(
String


 name)

Write the given field to native memory. The current value in the Java field will be translated into native memory.

Throws:

IllegalArgumentException - if no field exists with the given name

 

    这些 write 方法,会把 Java 的内存 Pin 住。    就是 C 语言可以使用。 GC 不再管理它们。

 

现在只要调用

pUserStruct.write();

java 模拟结构体实例给 Pin 住就可以了。

 

 

题外话, C# 定义了语法,可以使用关键字 pin 锁住 .NET 对象。

 

 

 

结论:

结构体是 C 语言模拟 OOP 开发中经常使用的一种数据组织形式。搞定了结构体 Struct ,我们就可以放心大胆、轻轻松松地把 C 程序随便拿来用了!

分享到:
评论

相关推荐

    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++动态链接库详细示例代码

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

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

    这些函数应该遵循C语言的调用约定,因为JNA主要支持C风格的接口。 2. **创建SWIG接口文件**:创建一个SWIG接口文件(.i文件),在这个文件中,你需要引用C++的头文件,并且声明哪些函数和类型需要被暴露给Java。 3...

    jna-4.4.0.jar

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

    jna控制键盘

    2. **定义接口**:JNA允许我们定义一个Java接口,该接口对应于要调用的本机库函数。例如,我们可以定义一个接口来表示Windows API的`SetWindowsHookEx`函数。 ```java public interface User32 extends Library { ...

    深入浅出JNA

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

    JNA实例 JNA实例 JNA实例

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

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

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

    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

    2. **模拟C语言数组**: C语言中,函数可能接受字符数组,如`char *data`。在JNA中,你可以使用Java的数组类型来模拟这些参数,如`void function1(char[] data)`。由于Java没有原始字符类型,`char[]`可以被替换为`...

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

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

Global site tag (gtag.js) - Google Analytics