C 语言调用 JNI_CreateJavaVM

环境:win10 + vs2015 + jdk-8u171-windows-x64.zip

假设我们已经存在了 C/C++ 语言的动态库clib.dll,如果想从 java 语言调用此动态库clib.dll,需要写个供 java 调用的C/C++的JNI动态库: clib_jniwrapper.dll,clib_jniwrapper.dll 桥接了从Java(JNIWrapper)对象到C/C++对象(clib.dll)的转换。 然后再写个 java 类如: JNIWrapper.java,在这个java JNIWrapper类里调用JNI方法提供者库clib_jniwrapper.dll。这就是 从 Java 调用 C 的整个过程:

[Java(JNIWrapper.java) ]===JNI Call===> [C/C++ (clib_jniwrapper.dll)] ===> [clib.dll]

clib_jniwrapper.dll 既然是一个 JNI 动态库,就要实现 Java(JNIWrapper)声明的 native 方法。首先是在 JNIWrapper.java 中加入要实现的 native 方法,然后用 javah命令自动创建出 jniwrapper 头文件:com_github_jni_JNIWrapper.h。然后需要手工编写 clib_jniwrapper.dll,实现这个头文件里声明的方法。

显然,在实际应用中,clib_jniwrapper.dll 被 Java 运行时所加载,然后在 Java 里调用 JNI 方法,间接通过 clib_jniwrapper.dll 最后调用到 clib.dll。

然而开发这样一个 clib_jniwrapper.dll 需要大量的调试和测试,因此需要写一个C语言的测试程序:test_clib_jniwrapper.exe。测试程序 test_clib_jniwrapper.exe 加载 jvm 运行时,然后创建 jni 对象,通过 jni 方法和对象调用 clib_jniwrapper.dll,这样就可以很方便地本地调试 C/C++ 的JNI 动态库 clib_jniwrapper.dll 了。

下面是 test_clib_jniwrapper.c 核心代码:

#include <com_github_jni_JNIWrapper.h>

static const char THIS_FILE[] = "test_clib_jniwrapper.c";

HINSTANCE jvmdll = NULL;
JavaVM * jvm = NULL;
void * jenv = NULL;

static void appexit_cleanup(void)
{
if (jvm) {
(*jvm)->DestroyJavaVM(jvm);
}

if (jvmdll) {
FreeLibrary(jvmdll);
}
}


static HANDLE logon_user(const char *username, const char *domain, const char *password)
{
HANDLE hLogon = NULL;
BOOL result = LogonUserA(username, domain, password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hLogon);
if (!result) {
exit(-1);
}
result = ImpersonateLoggedOnUser(hLogon);
if (!result) {
exit(-1);
}
return hLogon;
}


static void get_env_var(const char *var, const char *prefix, char *value, size_t valuesz)
{
errno_t err;
size_t bsize;

int prelen = 0;
if (prefix) {
prelen = snprintf_chkd_V1(value, valuesz, prefix);
valuesz -= prelen;
}

err = getenv_s(&bsize, NULL, 0, var);
if (err || !bsize) {
fprintf(stderr, "failed to get env: %s\n", var);
exit(-1);
}

if ((int)bsize >= (int)(valuesz)) {
fprintf(stderr, "insufficent value buffer for: JAVA_HOME\n");
exit(-1);
}

err = getenv_s(&bsize, &value[prelen], valuesz, var);
if (err || !bsize || (int)bsize >= (int)valuesz) {
fprintf(stderr, "error to get env: %s\n", var);
exit(-1);
}

printf("${env:%s}=%.*s\n", var, (int)bsize, &value[prelen]);
}


void createJVM()
{
typedef jint(JNICALL *ProcCreateJavaVM)(JavaVM **, void**, void *);

jint res;
ProcCreateJavaVM jvm_CreateJavaVM;

char javahome[200];
char jvmdllpath[260];
char classpath[1200];

JavaVMInitArgs vm_args;
JavaVMOption options[30];

memset(&vm_args, 0, sizeof(vm_args));
memset(&options, 0, sizeof(options));

get_env_var("JAVA_HOME", NULL, javahome, sizeof(javahome));
get_env_var("CLASSPATH", "-Djava.class.path=", classpath, sizeof(classpath));

snprintf_chkd_V1(jvmdllpath, sizeof(jvmdllpath), "%s\\jre\\bin\\server\\jvm.dll", javahome);

// 注意: 务必使用动态载入 jvm.dll 方式调用 JNI_CreateJavaVM
jvmdll = LoadLibraryA(jvmdllpath);
if (!jvmdll) {
printf("failed to load: %s\n", jvmdllpath);
exit(-1);
}
printf("success load: %s\n", jvmdllpath);

jvm_CreateJavaVM = (ProcCreateJavaVM) GetProcAddress(jvmdll, "JNI_CreateJavaVM");
if (!jvm_CreateJavaVM) {
printf("failed to GetProcAddress: JNI_CreateJavaVM\n");
exit(-1);
}

options[0].optionString = classpath;

vm_args.version = JNI_VERSION_1_6;
vm_args.ignoreUnrecognized = JNI_TRUE;
vm_args.options = options;
vm_args.nOptions = 1;

// 务必关闭异常: (VS2015: Ctrl+Alt+E -> Win32 Exceptioins -> 0xc0000005 Access violation)
// https://stackoverflow.com/questions/36250235/exception-0xc0000005-from-jni-createjavavm-jvm-dll
res = jvm_CreateJavaVM(&jvm, &jenv, &vm_args);
if (res == 0) {
printf("successfully created JVM.\n");
} else {
printf("failed to create JVM.\n");
exit(-1);
}
}

int main(int argc, char *argv[])
{
WINDOWS_CRTDBG_ON

createJVM();
jobject obj = NULL;

atexit(appexit_cleanup);
Java_com_github_jni_JNIWrapper_JNI_1clib_1lib_1version(jenv, obj);

return 0;
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_github_jni_JNIWrapper */

#ifndef _Included_com_github_jni_JNIWrapper
#define _Included_com_github_jni_JNIWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_github_jni_JNIWrapper
* Method: JNI_clib_lib_version
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_github_jni_JNIWrapper_JNI_1clib_1lib_1version
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

以上需要注意的是,务必关闭异常: (VS2015: Ctrl+Alt+E -> Win32 Exceptioins -> 0xc0000005 Access violation)。

以上测试完全可用。完整的DEMO:

​https://github.com/pepstack/clib-jni​