原文 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
;
分享到:
相关推荐
### 深入解析JNA—模拟C语言结构体 #### 前言 在《JNA—JNI终结者》一文中介绍了Java Native Access (JNA) 的基本使用方法及其优势,但当时并未深入探讨如何在Java中模拟C语言中的结构体(structure)。结构体在C语言...
Java通过JNA(Java ...通过理解JNA的工作原理,创建对应的Java结构体类,以及正确地传递和解析结构体指针,可以有效地在Java和C之间进行数据交换。对于需要跨平台或利用C库的Java开发者来说,这是一个重要的技能。
在"JNA 转java接口以及指针结构体解析"这个主题中,我们将深入探讨如何使用JNA来处理C语言中的结构体和指针。 首先,理解JNA的基本工作原理至关重要。JNA通过定义一个`Interface`,该接口中的方法对应于要调用的...
本文将深入探讨JNA在处理复杂结构体传递,包括结构体数组和结构体指针方面的知识。 JNA是一种Java库,允许Java代码直接调用本机API,而无需编写JNI代码。相比于JNI,JNA的使用更为简单和直观,但可能在性能上稍逊...
可实现将C语言中的结构体转换为JAVA类型的实体类。 目前支持基础数据类型int、long、foloat、double、const char *、bool的转换,也支持将数组装换为JAVA中的ArrayList。目前有个难点是将枚举类型直接转换为JAVA中的...
在标题中提到的“jna调用C语言函数库dll、so例子”,这里的"dll"是Windows系统中的动态链接库(Dynamic Link Library),"so"则是Linux和其他类Unix系统中的共享对象(Shared Object)。这两种都是C语言或其他编译...
Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源
1:基本数据类型 2:基本数据类型的指针和引用 3:结构体 4:结构体的指针和引用 5:函数指针和回调函数 6:字符串指针 7:输入一个数组 8:输出一个数组并释放空间 本资源包括三个工程: C++动态链接库; VC调用...
1. **准备C++头文件**:确保你的C++头文件(.h文件)包含了所有需要调用的函数和结构体声明。这些函数应该遵循C语言的调用约定,因为JNA主要支持C风格的接口。 2. **创建SWIG接口文件**:创建一个SWIG接口文件(.i...
《深入解析JNA-4.4.0.jar:Java Native Access技术详解》 在Java编程领域,有时我们需要调用操作系统底层的功能,例如访问硬件设备、操作系统API或使用C/C++库。这时,Java Native Interface (JNI) 和 Java Native ...
1. **引入JNA库**:首先,你需要在项目中添加JNA库的依赖,这可以通过Maven或Gradle等构建工具来完成。 2. **定义接口**:JNA允许我们定义一个Java接口,该接口对应于要调用的本机库函数。例如,我们可以定义一个...
### 深入浅出JNA — 快速调用原生函数 #### 为什么需要JNA? 在软件开发过程中,特别是在需要与操作系统底层交互或利用高性能计算资源时,经常需要用到原生函数(通常指C/C++语言编写的库)。Java作为一种高级语言...
JNA通过映射机制支持常见的数据类型,包括整型、浮点型、字符串等基本数据类型的转换,并提供了一套丰富的API来模拟原生语言中的指针、结构体等复杂数据结构。通过JNA,开发者可以更轻松地调用原生函数,也更容易...
通过这个JNA实例,我们可以看到Java程序是如何利用JNA库来调用C语言编写的函数和处理结构体数据的。这种技术不仅简化了跨语言编程的过程,还提高了代码的可维护性和扩展性。对于需要与现有C/C++代码集成的项目来说,...
这段代码展示了如何使用JNA加载`user32.dll`库并定义`SendInput()`函数,接着可以创建`INPUT`结构体实例,填充模拟的鼠标或键盘事件,调用`SendInput()`来发送这些事件。 通过这种方式,Java开发者可以利用JNA库在...
本示例将详细解析如何使用JNA在Java中调用C语言库文件。 首先,我们需要理解JNA的工作原理。JNA通过映射Java方法到本地函数来实现调用,这得益于其内建的类型映射系统。Java中的数据类型会自动转换为对应的C语言...
在JNA3.09 API中,我们可以看到几个关键的概念和技术,用于模拟C语言中的各种数据类型和参数传递方式。 1. **模拟普通传值参数**: 在C语言中,函数接收基本类型参数,如`int`, `float`, `long`等。JNA允许你直接...