从 C++(使用 Cygwin)调用 JavaVM 时,如何加载 JAR?
When invoking JavaVM from C++ (using Cygwin) how can I load a JAR?
我正在用 C++ 编写应用程序。我想启动 JVM 并从此应用程序调用 Java 方法。我在 运行 Windows 10 上使用 Cygwin。
根据 Java Invocation API 页面,我相信我正确地调用了 JVM;但是,当我打印系统 属性 "java.class.path" 时,它显示为空。
如果我将 class 文件复制到 $PWD/main/MyClass.class
一切正常,但如果我将此 class 文件打包到 $PWD/main.jar
并尝试以相同的方式引用它,然后我得到:
java.lang.NoClassDefFoundError: main/MyClass
Caused by: java.lang.ClassNotFoundException: main.MyClass
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
以下程序的输出是:
Value of 'java/lang/System.getProperty(java.class.path)' is ''.
Value of 'java/lang/System.getProperty(user.dir)' is 'C:\Users\admin\java_from_cpp_stack\bin'.
Failed to find class 'main/MyClass'.
这是 C++ 代码:
#include <jni.h>
#include <iostream>
bool
printSysemProperty(JNIEnv* env, std::string property)
{
std::string className = "java/lang/System";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cout << "Failed to find class '" << className.c_str() << "'.\n";
return false;
}
std::string methodName = "getProperty";
jmethodID mid = env->GetStaticMethodID(cls, methodName.c_str(), "(Ljava/lang/String;)Ljava/lang/String;");
if (mid == 0) {
std::cout << "Failed to find method '" << className.c_str() << "." << methodName.c_str() << "'.\n";
return false;
}
jstring cp = env->NewStringUTF(property.c_str());
jstring val = (jstring)env->CallStaticObjectMethod(cls, mid, (jstring)cp);
const char* valCStr = env->GetStringUTFChars(val, JNI_FALSE);
std::cout << "Value of '" << className.c_str() << "." << methodName.c_str() << "(" << property.c_str() << ")' is '" << (char*)valCStr << "'.\n";
return true;
}
int
main(int argc, char **argv)
{
// Create JVM
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
//options[0].optionString = (char*)"-Djava.class.path=.";
//options[0].optionString = (char*)"-Djava.class.path=C:\Users\admin\java_from_cpp_stack\bin\main.jar";
//options[0].optionString = (char*)"-Djava.class.path=/cygdrive/c/Users/admin/java_from_cpp_stack/bin/main.jar";
options[0].optionString = (char*)"-Djava.class.path=main.jar";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
delete options;
if (ret != 0)
std::cerr << "Failed to create JVM.\n";
// Print System Properties
bool rc = true;
rc &= printSysemProperty( env, "java.class.path");
rc &= printSysemProperty( env, "user.dir");
if (!rc) {
std::cout << "Printing system properties failed.\n";
return rc;
}
// Call Static Method from JAR
std::string className = "main/MyClass";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cout << "Failed to find class '" << className.c_str() << "'.\n";
return -3;
}
jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V");
if (mid == 0) {
std::cout << "Failed to find method '" << className.c_str() << ".hello'.\n";
return -4;
}
std::cout << "Calling '" << className.c_str() << ".main'.\n";
env->CallStaticVoidMethod(cls, mid);
// Clean up
vm->DestroyJavaVM();
std::cout << "Complete.\n";
return 0;
}
这里是 Java 代码:
package main;
public class MyClass {
public MyClass() {
}
public static void hello() {
System.out.println("From Java: Hello World!");
}
}
这是我用来编译的BASH脚本:
#!/bin/bash
set -e
set -x
rm -rf ./build
mkdir ./build
rm -rf ./bin/
mkdir ./bin/
g++ \
-o ./bin/myprog \
./src/cpp/myprog.c \
-D__int64=int64_t \
-L/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/ \
-ljvm \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include/win32
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/javac \
-Xlint:all \
-Werror \
-g \
-verbose \
-cp ./build \
-sourcepath ./src/java \
-d ./build \
./src/java/main/MyClass.java
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar \
cvf ./bin/main.jar \
-C ./build \
main/MyClass.class
#mkdir ./bin/main
#cp ./build/main/MyClass.class ./bin/main
cd ./bin
PATH=/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/:/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/:$PATH ./myprog
原来这里贴出了解决方案:
JNI JVM Invocation Classpath
On x86-64, the Oracle Windows JDK headers define jint as long. This is
32 bits with Microsoft compilers (which the Oracle JDK is written for)
but 64 bits with Cygwin gcc. Since JavaVMInitArgs
contains some fields
of this type, its binary layout is changed by this discrepancy.
另见:
Error when compiling in cygwin
我的修复是:
从 g++
命令行中删除 -D__int64=int64_t
添加头文件jni_local.h
然后JavaVMInitArgs
被认出来了
src/cpp/jni_local.h
文件内容为:
#include "stdint.h"
#define __int64 int64_t
#define long int32_t
#include "jni_md.h"
#undef long
#include_next "jni.h"
src/cpp/myprog.c
缩减为:
#include "jni_local.h"
#include <iostream>
int
main(int argc, char **argv)
{
// Create JVM
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = (char*)"-Djava.class.path=myPackage.jar";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
delete options;
if (ret != 0) {
std::cerr << "Failed to create JVM.\n";
return ret;
}
// Call Static Method from JAR
std::string className = "myPackage/MyClass";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cerr << "Failed to find class '" << className.c_str() << "'.\n";
return -3;
}
jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V");
if (mid == 0) {
std::cerr << "Failed to find method '" << className.c_str() << ".hello'.\n";
return -4;
}
std::cerr << "Calling 'myPackage." << className.c_str() << ".hello()'\n";
env->CallStaticVoidMethod(cls, mid);
// Clean up
vm->DestroyJavaVM();
std::cerr << "Complete.\n";
return 0;
}
src/java/myPackage/MyClass.java
没有改变。
run.sh
文件已更新为:
#!/bin/bash
set -e
set -x
rm -rf ./build
mkdir ./build
rm -rf ./bin/
mkdir ./bin/
g++ \
-o ./bin/myprog \
./src/cpp/myprog.c \
-L/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/ \
-ljvm \
-Isrc/cpp \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include/win32
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/javac \
-Xlint:all \
-Werror \
-g \
-verbose \
-cp ./build \
-sourcepath ./src/java \
-d ./build \
./src/java/myPackage/MyClass.java
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar \
cvf ./bin/myPackage.jar \
-C ./build \
myPackage/MyClass.class
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar tf ./bin/myPackage.jar
cd ./bin
PATH=/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/:/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/:$PATH ./myprog
我正在用 C++ 编写应用程序。我想启动 JVM 并从此应用程序调用 Java 方法。我在 运行 Windows 10 上使用 Cygwin。
根据 Java Invocation API 页面,我相信我正确地调用了 JVM;但是,当我打印系统 属性 "java.class.path" 时,它显示为空。
如果我将 class 文件复制到 $PWD/main/MyClass.class
一切正常,但如果我将此 class 文件打包到 $PWD/main.jar
并尝试以相同的方式引用它,然后我得到:
java.lang.NoClassDefFoundError: main/MyClass
Caused by: java.lang.ClassNotFoundException: main.MyClass
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
以下程序的输出是:
Value of 'java/lang/System.getProperty(java.class.path)' is ''.
Value of 'java/lang/System.getProperty(user.dir)' is 'C:\Users\admin\java_from_cpp_stack\bin'.
Failed to find class 'main/MyClass'.
这是 C++ 代码:
#include <jni.h>
#include <iostream>
bool
printSysemProperty(JNIEnv* env, std::string property)
{
std::string className = "java/lang/System";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cout << "Failed to find class '" << className.c_str() << "'.\n";
return false;
}
std::string methodName = "getProperty";
jmethodID mid = env->GetStaticMethodID(cls, methodName.c_str(), "(Ljava/lang/String;)Ljava/lang/String;");
if (mid == 0) {
std::cout << "Failed to find method '" << className.c_str() << "." << methodName.c_str() << "'.\n";
return false;
}
jstring cp = env->NewStringUTF(property.c_str());
jstring val = (jstring)env->CallStaticObjectMethod(cls, mid, (jstring)cp);
const char* valCStr = env->GetStringUTFChars(val, JNI_FALSE);
std::cout << "Value of '" << className.c_str() << "." << methodName.c_str() << "(" << property.c_str() << ")' is '" << (char*)valCStr << "'.\n";
return true;
}
int
main(int argc, char **argv)
{
// Create JVM
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
//options[0].optionString = (char*)"-Djava.class.path=.";
//options[0].optionString = (char*)"-Djava.class.path=C:\Users\admin\java_from_cpp_stack\bin\main.jar";
//options[0].optionString = (char*)"-Djava.class.path=/cygdrive/c/Users/admin/java_from_cpp_stack/bin/main.jar";
options[0].optionString = (char*)"-Djava.class.path=main.jar";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
delete options;
if (ret != 0)
std::cerr << "Failed to create JVM.\n";
// Print System Properties
bool rc = true;
rc &= printSysemProperty( env, "java.class.path");
rc &= printSysemProperty( env, "user.dir");
if (!rc) {
std::cout << "Printing system properties failed.\n";
return rc;
}
// Call Static Method from JAR
std::string className = "main/MyClass";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cout << "Failed to find class '" << className.c_str() << "'.\n";
return -3;
}
jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V");
if (mid == 0) {
std::cout << "Failed to find method '" << className.c_str() << ".hello'.\n";
return -4;
}
std::cout << "Calling '" << className.c_str() << ".main'.\n";
env->CallStaticVoidMethod(cls, mid);
// Clean up
vm->DestroyJavaVM();
std::cout << "Complete.\n";
return 0;
}
这里是 Java 代码:
package main;
public class MyClass {
public MyClass() {
}
public static void hello() {
System.out.println("From Java: Hello World!");
}
}
这是我用来编译的BASH脚本:
#!/bin/bash
set -e
set -x
rm -rf ./build
mkdir ./build
rm -rf ./bin/
mkdir ./bin/
g++ \
-o ./bin/myprog \
./src/cpp/myprog.c \
-D__int64=int64_t \
-L/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/ \
-ljvm \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include/win32
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/javac \
-Xlint:all \
-Werror \
-g \
-verbose \
-cp ./build \
-sourcepath ./src/java \
-d ./build \
./src/java/main/MyClass.java
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar \
cvf ./bin/main.jar \
-C ./build \
main/MyClass.class
#mkdir ./bin/main
#cp ./build/main/MyClass.class ./bin/main
cd ./bin
PATH=/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/:/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/:$PATH ./myprog
原来这里贴出了解决方案: JNI JVM Invocation Classpath
On x86-64, the Oracle Windows JDK headers define jint as long. This is 32 bits with Microsoft compilers (which the Oracle JDK is written for) but 64 bits with Cygwin gcc. Since
JavaVMInitArgs
contains some fields of this type, its binary layout is changed by this discrepancy.
另见: Error when compiling in cygwin
我的修复是:
从
g++
命令行中删除-D__int64=int64_t
添加头文件
jni_local.h
然后
JavaVMInitArgs
被认出来了
src/cpp/jni_local.h
文件内容为:
#include "stdint.h"
#define __int64 int64_t
#define long int32_t
#include "jni_md.h"
#undef long
#include_next "jni.h"
src/cpp/myprog.c
缩减为:
#include "jni_local.h"
#include <iostream>
int
main(int argc, char **argv)
{
// Create JVM
JavaVM* vm;
JNIEnv* env;
JavaVMInitArgs vm_args;
JavaVMOption* options = new JavaVMOption[1];
options[0].optionString = (char*)"-Djava.class.path=myPackage.jar";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
int ret = JNI_CreateJavaVM(&vm, (void**)&env, &vm_args);
delete options;
if (ret != 0) {
std::cerr << "Failed to create JVM.\n";
return ret;
}
// Call Static Method from JAR
std::string className = "myPackage/MyClass";
jclass cls = env->FindClass(className.c_str());
if (cls == 0) {
std::cerr << "Failed to find class '" << className.c_str() << "'.\n";
return -3;
}
jmethodID mid = env->GetStaticMethodID(cls, "hello", "()V");
if (mid == 0) {
std::cerr << "Failed to find method '" << className.c_str() << ".hello'.\n";
return -4;
}
std::cerr << "Calling 'myPackage." << className.c_str() << ".hello()'\n";
env->CallStaticVoidMethod(cls, mid);
// Clean up
vm->DestroyJavaVM();
std::cerr << "Complete.\n";
return 0;
}
src/java/myPackage/MyClass.java
没有改变。
run.sh
文件已更新为:
#!/bin/bash
set -e
set -x
rm -rf ./build
mkdir ./build
rm -rf ./bin/
mkdir ./bin/
g++ \
-o ./bin/myprog \
./src/cpp/myprog.c \
-L/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/ \
-ljvm \
-Isrc/cpp \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include \
-I/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/include/win32
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/javac \
-Xlint:all \
-Werror \
-g \
-verbose \
-cp ./build \
-sourcepath ./src/java \
-d ./build \
./src/java/myPackage/MyClass.java
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar \
cvf ./bin/myPackage.jar \
-C ./build \
myPackage/MyClass.class
/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/jar tf ./bin/myPackage.jar
cd ./bin
PATH=/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/jre/bin/server/:/cygdrive/c/Program\ Files/Java/jdk1.8.0_131/bin/:$PATH ./myprog