首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

在JNI中调用当地带结构体参数的函数

2012-12-24 
在JNI中调用本地带结构体参数的函数?说起JNI,《The Java Native Interface -- Programmers Guide and Spec

在JNI中调用本地带结构体参数的函数

?

说起JNI,《The Java Native Interface -- Programmer's Guide and Specification》我认为是挺好的入门教程。浅显易懂,而且也附有参考。对很多问题和陷阱也进行了讲解和提示。可以在 Sun 的官网上免费下载到这本书,下载地址:http://java.sun.com/docs/books/jni/download/jni.pdf。?

但是我认为这本书在第 9 章 Leveraging Existing Native Libaries 中对开头所讲的一段程序的解释有点潦草。内容大意是有一个 Win32 API, CreateFile。它带了很多参数。有 const char*、DWORD、HANDLE,最重要的是它带了一个 SECURITY_ATTRIBUTES*,一个结构体指针。?

C代码?

HANDLE CreateFile( ?

?? ?const char *fileName, // file name ?

?? ?DWORD desiredAccess, // access (read-write) mode ?

?? ?DWORD shareMode, // share mode ?

?? ?SECURITY_ATTRIBUTES *attrs, // security attributes ?

?? ?DWORD creationDistribution, // how to create ?

?? ?DWORD flagsAndAttributes, // file attributes ?

?? ?HANDLE templateFile // file with attr. to copy ?

); ?

这个 SECURITY_ATTRIBUTES 是自定义的。具体的定义就不给出了。书中也给出了对应的 Java 函数。?

Java代码?

public class Win32 { ?

?? ?public static native int CreateFile( ?

?? ? ? ?String fileName, // file name ?

?? ? ? ?int desiredAccess, // access (read-write) mode ?

?? ? ? ?int shareMode, // share mode ?

?? ? ? ?int[] secAttrs, // security attributes ?

?? ? ? ?int creationDistribution, // how to create ?

?? ? ? ?int flagsAndAttributes, // file attributes ?

?? ? ? ?int templateFile); // file with attr. to copy ?

?? ?... ?

} ?

但怪的是,作者使用了一个 int[] 来对应 SECURITY_ATTRIBUTES*。对此,作者只有一段短小的解释。?

引用

Because of potential differences in how fields are laid out in memory, we do?

not map C structures to classes in the Java programming language. Instead, we use?

an array to store the contents of the C structure SECURITY_ATTRIBUTES. The caller?

may also pass null as secAttrs to specify the default Win32 security attributes.?

We will not discuss the contents of the SECURITY_ATTRIBUTES structure or how to?

encode that in an int array.?

我认为这个解释是为了让读者耐心读下去而写的。这里用 int[] 是说不通的。可以理解作者。书中这时的重点还是讲解如何调用一个普通函数。在读完这章的时候,我才明白,这里应该用后面所介绍的 Peer Class 来做。但我后面给出的解决方法并不是针对这个例子的。但是,看完后您可以自已写一个解决方法。因为后面的示例给出一个解决 Java 调用 C/C++ 带结构体参数函数的思路。?

Peer Class,用我的话来说就是一个 Java 类,它包含了一个 C/C++ 对象的指针。一个通常的 Peer Class 长成这个样子:?

Java代码?

public class PeerClass { ?

?? ?private long peer; ?

?? ?... ?

} ?

引用《The Java Native Interface -- Programmer's Guide and Specification》,java.io.FileDescriptor 也包含一个 int fd。用来保存对应本地结构体的一个指针。所以说,Peer Class 的使命就是提供一个对 C/C++ 结构体或类的一个包装,使得 Java 可以使用。故,解决上面 CreateFile 函数参数问题的办法就是针对 SECURITY_ATTRIBUTES 结构体定义一个包装类。具体的实现我用另外一个例子。因为 CreateFile 的签名太长,有点吓人 (#o#)?

假定,Java 一段程序需要调用 C++ 一个函数 CppFunc(STRUCT* stru)。这个 STRUCT 包含一个 long 成员变量和一个 char 成员变量。CppFunc 会打印出这个结构体实例中变量的值。为了构造一个这样的 C++ 结构体,我们给它做一个包装类。也就是一个 Peer Class。不妨叫 StructWrapper。通过构造 StructWrapper,Java 程序给其对应的 C++ 结构体赋值。再将这个 StructWrapper 实例传给 Java 里对 CppFunc 的包装函数,从而达到目的。?

示例中的 C++ 工程是一个 Win32 DLL。下面的代码展示可能为了逻辑的连贯性而把一个文件的内容分开来。读者实践的时候可以自行合并。?

首先,定义 C++ 的 CppFunc 和 STRUCT 结构体。?

C++代码?

// Entry.cpp ?

using namespace std; ?

void CppFunc(STRUCT* stru) ?

{ ?

?? ?cout << "long value:\t" << stru->l << endl; ?

?? ?cout << "char value:\t" << stru->c << endl; ?

} ?

// Struct.h ?

#ifndef _Included_STRUCT ?

#define _Included_STRUCT ?

typedef struct _STRUCT ?

{ ?

?? ?long l; ?

?? ?char c; ?

} STRUCT; ?

#endif ?

再次,定义 STRUCT 的包装类 StructWrapper。?

Java代码?

// StructWrapper.java ?

public class StructWrapper { ?

?? ?private long peer; ?

?? ?public long getPeer() { ?

?? ? ? ?return peer; ?

?? ?} ?

?? ?private native long initialize(long l, char c); ?

?? ?private native void destroy(long peer); ?

?? ?public StructWrapper(long l, char c) { ?

?? ? ? ?peer = initialize(l, c); ?

?? ?} ?

?? ?public synchronized void destroy() { ?

?? ? ? ?if (peer != 0) { ?

?? ? ? ? ? ?destroy(peer); ?

?? ? ? ? ? ?peer = 0; ?

?? ? ? ?} ?

?? ?} ?

?? ?protected void finalize() { ?

?? ? ? ?destroy(); ?

?? ?} ?

?? ?static { ?

?? ? ? ?System.loadLibrary("TestJNI"); ?

?? ?} ?

} ?

initialize 函数的作用就是调用 C++ 代码来初始化一个 C++ 下的 STRUCT 对象,并返回这个对象的指针。StructWrapper 在构造时把这个指针保存在 peer 中。这里 peer 其实可以用 int。因为 C++ 下指针是四字节,Java 下 int 也是四字节。destroy 设计为线程安全的原因是因为在 finalize 方法中会自动调用 destroy。而用户也可能会手动销毁对象。有可能出现并发的情况。?

还缺一个针对 CppFunc 的 Java 包装函数。?

Java代码?

// Entry.java ?

public class Entry { ?

?? ?private native void callCppFunc(long structWrapperPeer); ?

?? ?public static void main(String[] args) { ?

?? ? ? ?... ?

?? ?} ?

} ?

callCppFunc(long) 这个 Java 函数。它提供了一个 CppFunc 的包装。它所需要的参数实际上是 StructWrapper 实例中所保存的 C++ STRUCT 结构体实例的地址。这样就实现了把 Java 对象传给 C++。?

大家看到,这里 StructWrapper 用到了几个本地函数。我们一样得在 C++ 中实现它们。?

C++代码?

// StructWrapper.h ?

/* DO NOT EDIT THIS FILE - it is machine generated */ ?

#include <jni.h> ?

/* Header for class StructWrapper */ ?

#ifndef _Included_StructWrapper ?

#define _Included_StructWrapper ?

#ifdef __cplusplus ?

extern "C" { ?

#endif ?

/*?

?* Class: ? ? StructWrapper?

?* Method: ? ?initialize?

?* Signature: (JC)J?

?*/ ?

JNIEXPORT jlong JNICALL Java_StructWrapper_initialize ?

??(JNIEnv *, jobject, jlong, jchar); ?

/*?

?* Class: ? ? StructWrapper?

?* Method: ? ?destroy?

?* Signature: (J)V?

?*/ ?

JNIEXPORT void JNICALL Java_StructWrapper_destroy ?

??(JNIEnv *, jobject, jlong); ?

#ifdef __cplusplus ?

} ?

#endif ?

#endif ?

// StructWrapper.cpp ?

#include "StructWrapper.h" ?

#include "Struct.h" ?

JNIEXPORT jlong JNICALL ?

Java_StructWrapper_initialize(JNIEnv* env, jobject self, jlong l, jchar c) ?

{ ?

?? ?STRUCT* peer = new STRUCT(); ?

?? ?peer->l = (long)l; ?

?? ?peer->c = (char)c; ?

?? ?return (jlong)peer; ?

} ?

JNIEXPORT void JNICALL ?

Java_StructWrapper_destroy(JNIEnv* env, jobject self, jlong peer) ?

{ ?

?? ?delete (STRUCT*)peer; ?

} ?

Okay,现在可以定义主函数了。?

Java代码?

// Entry.java ?

public class Entry { ?

?? ?private native void callCppFunc(long strutWrapperPeer); ?

?? ?public static void main(String[] args) { ?

?? ? ? ?StructWrapper struct = new StructWrapper(1, 'a'); ?

?? ? ? ?new Entry().callCppFunc(struct.getPeer()); ?

?? ? ? ?struct.destroy(); ?

?? ?} ?

} ?

其对应的 C++ 实现,?

C++代码?

// Entry.h ?

/* DO NOT EDIT THIS FILE - it is machine generated */ ?

#include <jni.h> ?

/* Header for class Entry */ ?

#ifndef _Included_Entry ?

#define _Included_Entry ?

#ifdef __cplusplus ?

extern "C" { ?

#endif ?

/*?

?* Class: ? ? Entry?

?* Method: ? ?callCppFunc?

?* Signature: (J)V?

?*/ ?

JNIEXPORT void JNICALL Java_Entry_callCppFunc ?

??(JNIEnv *, jobject, jlong); ?

#ifdef __cplusplus ?

} ?

#endif ?

#endif ?

// Entry.cpp ?

JNIEXPORT void JNICALL ?

Java_Entry_callCppFunc(JNIEnv* env, jobject self, jlong structWrapperPeer) ?

{ ?

?? ?STRUCT* structPointer = (STRUCT*)structWrapperPeer; ?

?? ?CppFunc(structPointer); ?

} ?

如果顺利的话,您现在跑这个 Java 程序,应该输出?

引用

long value:1?

char value:a?


?

?

热点排行