- 浏览: 7325560 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (1546)
- 企业中间件 (236)
- 企业应用面临的问题 (236)
- 小布Oracle学习笔记汇总 (36)
- Spring 开发应用 (54)
- IBatis开发应用 (16)
- Oracle基础学习 (23)
- struts2.0 (41)
- JVM&ClassLoader&GC (16)
- JQuery的开发应用 (17)
- WebService的开发应用 (21)
- Java&Socket (44)
- 开源组件的应用 (254)
- 常用Javascript的开发应用 (28)
- J2EE开发技术指南 (163)
- EJB3开发应用 (11)
- GIS&Mobile&MAP (36)
- SWT-GEF-RCP (52)
- 算法&数据结构 (6)
- Apache开源组件研究 (62)
- Hibernate 学习应用 (57)
- java并发编程 (59)
- MySQL&Mongodb&MS/SQL (15)
- Oracle数据库实验室 (55)
- 搜索引擎的开发应用 (34)
- 软件工程师笔试经典 (14)
- 其他杂项 (10)
- AndroidPn& MQTT&C2DM&推技术 (29)
- ActiveMQ学习和研究 (38)
- Google技术应用开发和API分析 (11)
- flex的学习总结 (59)
- 项目中一点总结 (20)
- java疑惑 java面向对象编程 (28)
- Android 开发学习 (133)
- linux和UNIX的总结 (37)
- Titanium学习总结 (20)
- JQueryMobile学习总结 (34)
- Phonegap学习总结 (32)
- HTML5学习总结 (41)
- JeeCMS研究和理解分析 (9)
最新评论
-
lgh1992314:
[u][i][b][flash=200,200][url][i ...
看看mybatis 源代码 -
尼古拉斯.fwp:
图片根本就不出来好吧。。。。。。
Android文件图片上传的详细讲解(一)HTTP multipart/form-data 上传报文格式实现手机端上传 -
ln94223:
第一个应该用排它网关吧 怎么是并行网关, 并行网关是所有exe ...
工作流Activiti的学习总结(八)Activiti自动执行的应用 -
ZY199266:
获取不到任何消息信息,请问这是什么原因呢?
ActiveMQ 通过JMX监控Connection,Queue,Topic的信息 -
xiaoyao霄:
DestinationSourceMonitor 报错 应该导 ...
ActiveMQ 通过JMX监控Connection,Queue,Topic的信息
转载自http://blog.csdn.net/shendl/article/details/3599849
深入解析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个接口:
|
|
|
|
这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;
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中,嵌套的是结构体的指针的数组。 CompanyStruct2类的实例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程序随便拿来用了!
发表评论
-
【转】Django resources
2014-01-23 14:35 10794Django resources This page li ... -
使用国内镜像源来加速python pypi包的安装
2014-01-16 11:16 197771pipy国内镜像目前有: http://pypi.d ... -
[转 ]vagrant使用简介
2014-01-10 13:53 257161> 简介: vagrant提供了易于配置,重复性 ... -
[转]在Java中调用Python
2014-01-07 13:08 9202在执行之前都需要把jython对应的包加载进去,这个是必须的 ... -
[转]Eclipse配置PyDev插件
2014-01-02 14:25 2827安装python解释器 安装PyDev: 首 ... -
RestFuse的研究(五) Http请求的封装
2014-06-14 15:50 3611在RestFuse中封装了Http请 ... -
RestFuse的研究(四) Junit的Statement的分析
2013-12-06 11:46 1656在RestFuse提供了多种单 ... -
RestFuse的研究(三) Junit的Rule的使用和分析
2013-12-06 11:01 2232在junit中定义一些可以公用的规则(R ... -
RestFuse的研究(二) Junit的Runner的分类和模式
2013-12-06 10:40 1595在Junit4中的调用JunitCore可以采 ... -
RestFuse的研究(一) HttpJunitRunner的实现
2013-12-06 10:11 1739在RestFuse是一种针对Rest We ... -
[转]An open-source JUnit extension to test HTTP/REST APIs
2013-12-06 09:57 1097http://developer.eclipsesource ... -
TestNG简单的学习(十三)TestNG中Junit的实现
2013-12-04 09:00 3351TestNG和junit的整合 ... -
TestNG简单的学习(十二)TestNG运行
2013-12-03 09:08 51569文档来自官方地址: ... -
TestNG简单的学习(十一)TestNG学习总结
2013-12-03 09:08 14165最近一直在学习关于TestNG方面的知识,根 ... -
TestNG简单的学习(十)TestNG @Listeners 的使用
2013-12-03 09:07 8682TestNG官方网站: http://testng.or ... -
TestNG简单的学习(九)TestNG Method Interceptors 的使用
2013-12-03 09:07 2704TestNG官方网站: http://testng ... -
TestNG简单的学习(八)TestNG Annotation Transformers 的使用
2013-12-03 09:07 2802TestNG官方网站: http://testng.or ... -
TestNG简单的学习(七)TestNG编程方式运行
2013-12-02 09:22 2447TestNG官方网站: http://testng.or ... -
TestNG简单的学习(六)测试工厂注释的使用
2013-12-02 09:22 2776TestNG官方网站: http://testng.or ... -
TestNG简单的学习(五)参数化测试数据的定制
2013-12-02 09:22 2693TestNG官方网站: http://testng.or ...
相关推荐
### 深入解析JNA—模拟C语言结构体 #### 前言 在《JNA—JNI终结者》一文中介绍了Java Native Access (JNA) 的基本使用方法及其优势,但当时并未深入探讨如何在Java中模拟C语言中的结构体(structure)。结构体在C语言...
在"JNA 转java接口以及指针结构体解析"这个主题中,我们将深入探讨如何使用JNA来处理C语言中的结构体和指针。 首先,理解JNA的基本工作原理至关重要。JNA通过定义一个`Interface`,该接口中的方法对应于要调用的...
Java通过JNA(Java ...通过理解JNA的工作原理,创建对应的Java结构体类,以及正确地传递和解析结构体指针,可以有效地在Java和C之间进行数据交换。对于需要跨平台或利用C库的Java开发者来说,这是一个重要的技能。
本文将深入探讨JNA在处理复杂结构体传递,包括结构体数组和结构体指针方面的知识。 JNA是一种Java库,允许Java代码直接调用本机API,而无需编写JNI代码。相比于JNI,JNA的使用更为简单和直观,但可能在性能上稍逊...
可实现将C语言中的结构体转换为JAVA类型的实体类。 目前支持基础数据类型int、long、foloat、double、const char *、bool的转换,也支持将数组装换为JAVA中的ArrayList。目前有个难点是将枚举类型直接转换为JAVA中的...
这些函数应该遵循C语言的调用约定,因为JNA主要支持C风格的接口。 2. **创建SWIG接口文件**:创建一个SWIG接口文件(.i文件),在这个文件中,你需要引用C++的头文件,并且声明哪些函数和类型需要被暴露给Java。 3...
在标题中提到的“jna调用C语言函数库dll、so例子”,这里的"dll"是Windows系统中的动态链接库(Dynamic Link Library),"so"则是Linux和其他类Unix系统中的共享对象(Shared Object)。这两种都是C语言或其他编译...
Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源
详细演示了如何通过JNA实现以下形式的动态链接库接口: 1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并...
《深入解析JNA-4.4.0.jar:Java Native Access技术详解》 在Java编程领域,有时我们需要调用操作系统底层的功能,例如访问硬件设备、操作系统API或使用C/C++库。这时,Java Native Interface (JNI) 和 Java Native ...
通过JNA,开发者可以直接调用操作系统提供的API函数,比如Windows API,来实现特定的功能,例如监听或模拟键盘事件。 在描述中提到的“全局键盘或鼠标事件”,意味着这种控制不仅限于应用程序内部,而是可以在整个...
4. **结构体**:JNA提供了`Structure`类来处理C结构体。你需要定义一个类继承自`Structure`,并使用`@FieldOrder`注解指定字段顺序。例如: ```java @FieldOrder({ "x", "y" }) public class Point extends ...
JNA通过映射机制支持常见的数据类型,包括整型、浮点型、字符串等基本数据类型的转换,并提供了一套丰富的API来模拟原生语言中的指针、结构体等复杂数据结构。通过JNA,开发者可以更轻松地调用原生函数,也更容易...
### 深入浅出JNA — 快速调用原生函数 #### 为什么需要JNA? 在软件开发过程中,特别是在需要与操作系统底层交互或利用高性能计算资源时,经常需要用到原生函数(通常指C/C++语言编写的库)。Java作为一种高级语言...
这段代码展示了如何使用JNA加载`user32.dll`库并定义`SendInput()`函数,接着可以创建`INPUT`结构体实例,填充模拟的鼠标或键盘事件,调用`SendInput()`来发送这些事件。 通过这种方式,Java开发者可以利用JNA库在...
本示例将详细解析如何使用JNA在Java中调用C语言库文件。 首先,我们需要理解JNA的工作原理。JNA通过映射Java方法到本地函数来实现调用,这得益于其内建的类型映射系统。Java中的数据类型会自动转换为对应的C语言...
通过这个JNA实例,我们可以看到Java程序是如何利用JNA库来调用C语言编写的函数和处理结构体数据的。这种技术不仅简化了跨语言编程的过程,还提高了代码的可维护性和扩展性。对于需要与现有C/C++代码集成的项目来说,...
当需要与C语言中的结构体或联合体交互时,JNA提供了一种称为`Structure`的抽象基类来帮助模拟这些复杂的原生数据结构。通过继承`Structure`类,我们可以定义自己的结构体模型,使得与原生代码的交互更为直观。 ####...