C 语言调用 JNI_CreateJavaVM
Posted 车斗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C 语言调用 JNI_CreateJavaVM相关的知识,希望对你有一定的参考价值。
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
以上是关于C 语言调用 JNI_CreateJavaVM的主要内容,如果未能解决你的问题,请参考以下文章
Android NDK编译之undefined reference to 'JNI_CreateJavaVM'
java - 来自JNI_CreateJavaVM(jvm.dll)的异常0xC0000005