Fatal error when using jni - java

I'm trying to use jni to call java methods from c++. actually more of a callback (java -> c++ -> java)
I've checked the c++ program for errors by testing it in .exe (c++ -> java)
The program works perfect in visual studio. but fails and crashes when I convert it to dll and use it in java.
I think it's related to jvm.dll because I had to include it to my visual studio project.
c++:
#include <stdio.h>
#include <jni.h>
#include <string.h>
#include "Inter.h"
JNIEnv* create_vm(JavaVM ** jvm) {
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
options.optionString = "-Djava.class.path=C:\\Users\\SolidSnake\\workspace\\Test\\bin"; //Path to the java source code
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
void callMethod() {
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
jclass m;
jmethodID test;
m = env->FindClass("Main");
test = env->GetStaticMethodID(m,"callbackFromC","()V");
env->CallStaticVoidMethod(m,test);
}
java:
public final class Main {
public native int callToC();
public static void callbackFromC() {
System.out.println("Hello from C!");
}
public static void main(String[] args) {
System.loadLibrary("Test");
new Main().callToC();
}
}
crash:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000007f9aed32ff8, pid=4016, tid=8228
#
# JRE version: Java(TM) SE Runtime Environment (8.0_11-b12) (build 1.8.0_11-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.11-b03 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C [Test.dll+0x1a08] JNIEnv_::FindClass+0x28
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
Here's how the program goes (j:callToC) -> (c:callMethod) -> (j:callbackFromC)

I had the same problem a couple of days ago and I have found a solution (maybe not the best one) of the problem and now I proud to share it (this one probably would be useful for somebody in the future).
Here is my code that worked well when I had only two projects (C++->Java), that is, the C++-project invoking Java-methods:
void JVM:: Init( const std:: string& javaClassesPath ) {
std:: string optionString = ( std:: string("-Djava.class.path=") + javaClassesPath );
JavaVMOption options_;
options_.optionString = const_cast<char*>( optionString.c_str() );
// initializing of JVM initial arguments:
JavaVMInitArgs arguments_;
memset( &arguments_, 0, sizeof( arguments_ ) );
arguments_.version = JNI_VERSION_1_6;
arguments_.nOptions = 1;
arguments_.options = &options_;
arguments_.ignoreUnrecognized = 0;
// creating JVM:
long status = JNI_CreateJavaVM( &jvm_, (void**)&env_, &arguments_ );
if ( status == JNI_ERR )
throw std:: exception("Error: unable to create Java Virtual Machine.\n");
FindClass("ChartBuilder");
}
This code was being invoked the following way:
JVM JChartBuilder( "D:\\Java Src\\Chart\\Chart.jar" ); // the path that will be used as classpath when creating VM (unless this path is specified it will be unable to find the class ChartBuilder)
Then (Java->C++Java)
When I got the third project (Java) that must invoke methods from that C++-project using JNI, I faced the problem that I cannot create VM when one has already run in the current process. But we can attach to existing VM! So, the code listed above can be changed to suit this requirements:
void JVM:: Init( const std:: string& javaClassesPath )
{
// initializing of JVM options:
std:: string optionString = ( std:: string("-Djava.class.path=") + javaClassesPath );
JavaVMOption options_;
options_.optionString = const_cast<char*>( optionString.c_str() );
// initializing of JVM initial arguments:
JavaVMInitArgs arguments_;
memset( &arguments_, 0, sizeof( arguments_ ) );
arguments_.version = JNI_VERSION_1_6;
arguments_.nOptions = 1;
arguments_.options = &options_;
arguments_.ignoreUnrecognized = 0;
// creating JVM or attaching to JVM:
long status;
/* is there any running VMs in the process? */
JavaVM* createdVMs[1] = { nullptr };
jsize numberOfVMs;
status = JNI_GetCreatedJavaVMs(createdVMs, 1, &numberOfVMs);
/* END OF is there any running VMs in the process? */
if( numberOfVMs != 0 )
{ // if VM already runs:
jvm_ = createdVMs[0]; // get the VM
jvm_->AttachCurrentThread((void**)&env_, NULL); // attach to the VM
}
else
{ // if VM does not run:
status = JNI_CreateJavaVM( &jvm_, (void**)&env_, &arguments_ );
}
if ( status == JNI_ERR )
throw std:: exception("Error: unable to create Java Virtual Machine.\n");
FindClass("ChartBuilder");
}
But that's not all yet. When running main Java-project (the first in Java->C++->Java), I got the following problem: unable to find specified class ChartBuilder. That is I attached to existing VM, but this virtual machine does not know about the path to this class. Remember, that when I created VM in C++, I specified the path explicitly. To solve this problem I have now to specify the classpath additionaly when running main Java-project.
That is all. The only thing I don't like is the need to specify the classpath to ChartBuilder when running main Java-project, but this one is not critical for me, though I'd like to find out how to avoid this need. Maybe somebody knows?

Related

Error while generating .dll files in Visual Studio 2022. Login authentication using ADSI Api in C++

I want to make a login authentication to my web application(Java). Username and password are created in windows server 2016 active directory. Using that username and password I have to login to my web app using ADSI(Active Directory Service Interface) API in C++ language to connect to the windows server. To do this I have to create the .dll file from the C++ code in Visual Studio 2022. Here I have to add Activeds.lib, adsiid.lib to make the API work. After generating the .dll file to load this in Java System.load() using JNI Concept.
This is the C++ code I used in Visual Studio 2022...
#include <iostream>
#include <jni.h>
#include<string.h>
#include "com_library_ldap_JNIHelper.h"
#include <Iads.h>
#include <adshlp.h>
#include "activeds.h"
#include <windows.h>
#include <stdlib.h>
using namespace std;
JNIEXPORT jstring JNICALL Java_com_library_ldap_JNIHelper_authenticateUser
(JNIEnv* env, jclass, jstring uname, jstring pwd) {
const char* uName = env->GetStringUTFChars(uname, NULL);
std::string username = std::string(uName);
const char* pwd1 = env->GetStringUTFChars(pwd, NULL);
std::string password = std::string(pwd1);
env->ReleaseStringUTFChars(uname, uName);
env->ReleaseStringUTFChars(pwd, pwd1);
IADs* pObject;
wchar_t wname[20];
mbstowcs(wname, username.c_str(), username.length());
LPWSTR szUsername = wname;
wchar_t wpwd[20];
mbstowcs(wpwd, password.c_str(), password.length());
LPWSTR szPassword = wpwd;
HRESULT hr;
string uAdmin = "Admin";
string uUser = "User";
string uType;
bool res = false;
if (uAdmin == "Admin") {
hr = ADsOpenObject(L"LDAP://test.local/CN=Users,DC=test,DC=local",
szUsername,
szPassword,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)&pObject);
if (SUCCEEDED(hr))
{
// Use the object.
res = true;
// Release the object.
pObject->Release();
}
if (res == true) {
uType = "Admin";
//return env->NewStringUTF(uType.c_str());
}
else
uType = "NotFound!!!";
}
else if (uUser == "User") {
hr = ADsOpenObject(L"LDAP://test.local /CN=Vimaluser,OU=Users,OU=Vimal,DC=test,DC=local",
szUsername,
szPassword,
ADS_SECURE_AUTHENTICATION,
IID_IADs,
(void**)&pObject);
if (SUCCEEDED(hr))
{
// Use the object.
res = true;
// Release the object.
pObject->Release();
}
if (res == true) {
uType = "User";
}
else {
uType = "NotFound!!!";
}
}
return env->NewStringUTF(uType.c_str());
}
below is the error I'm getting...
Build started...
1>------ Build started: Project: Adsi-JNI, Configuration: Release x64 ------
1>Adsi-JNI.cpp
1> Creating library C:\Users\Vimal\Desktop\VS Code\Adsi-JNI\x64\Release\Adsi-JNI.lib and object C:\Users\Vimal\Desktop\VS Code\Adsi-JNI\x64\Release\Adsi-JNI.exp
1>Adsi-JNI.obj : error LNK2019: unresolved external symbol ADsOpenObject referenced in function Java_com_library_ldap_JNIHelper_authenticateUser
1>Adsi-JNI.obj : error LNK2001: unresolved external symbol IID_IADs
1>E:\Java Program\Library System\src\main\java\com\library\ldap\lib\ActiveDS.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
1>E:\Java Program\Library System\src\main\java\com\library\ldap\lib\adsiid.lib : warning LNK4272: library machine type 'x86' conflicts with target machine type 'x64'
1>C:\Users\Vimal\Desktop\VS Code\Adsi-JNI\x64\Release\Adsi-JNI.dll : fatal error LNK1120: 2 unresolved externals
1>Done building project "Adsi-JNI.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
And I wrote a code for server connection, but i don't know whether the code is correct for server connection or not. Please help me to solve this problem and also share what are all the things related to this. Thanks in advance.

JNI JVM Invocation Classpath

I am writing a small C program using Cygwin that launches a Java Virtual Machine (libraries I am using require POSIX environment). So far, I have been able to get it to work as long as I place all of my classes in the same folder as the executable. However, I want to specify an actual JAR file that contains the application I want to run. This does not seem to work though, FindClass simply returns a null. I've narrowed it down to a problem with the classpath setting, like I said, because I can extract my jar file in the same directory as the executable and it will work. Here is a subset of my code:
I've loosely been following this guide: http://www.inonit.com/cygwin/jni/invocationApi/
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
Tried using -classpath and -cp
int main( int argc, char *argv[] )
{
void* jvmDllHandle;
JNIEnv* jenv;
JavaVM* jvm;
JavaVMInitArgs args;
JavaVMOption options[1];
jclass cls;
jmethodID mainMethod;
jobjectArray appArgs;
jstring arg0;
assert( cygwin_internal( CW_SYNC_WINENV ) != 1UL );
jvmDllHandle = LoadLibrary( "c:\\Path\\To\\Application\\jre\\bin\\server\\jvm.dll" );
createJavaVM = dlsym( jvmDllHandle, "JNI_CreateJavaVM" );
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-classpath c:\\Path\\To\\Application\\TheJarFile.jar";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
createJavaVM( &jvm, (void **) &jenv, &args );
cls = (*jenv)->FindClass( jenv, "some/package/MainClass" );
assert( cls != NULL ); // This fails.
/// Omitted...
return 0;
}
How am I specifying the classpath incorrectly?
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.
I worked around this by providing a local jni.h header:
#include "stdint.h"
#define __int64 int64_t
#define long int32_t
#include "jni_md.h"
#undef long
#include_next "jni.h"
I'm including only jni_md.h within the scope of the long redefinition because it doesn't include any other headers, whereas jni.h includes a couple of standard headers which we would not want to be affected as well.
To ensure this is always included ahead of the Oracle header, use the -I compiler option to add its directory to the #include path.

Calling unmanaged C++ via Java

I needed a Java application to call unmanaged C++. I copied MSVCR90.dll manually from Visual Studio 2008 redist path to the vmware's Windows Server Datacenter.
This is the error I get:
A fatal error has been detected by the Java Runtime Environment:
EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x73b4ae7a, pid=1108, tid=2272
JRE version: 6.0_38-b05
Java VM: Java HotSpot(TM) Client VM (20.13-b02 mixed mode, sharing windows-x86 )
Problematic frame:
C [MSVCR90.dll+0x3ae7a]
An error report file with more information is saved as:
...\hs_err_pid1108.log
If you would like to submit a bug report, please visit:
http://java.sun.com/webapps/bugreport/crash.jsp
The crash happened outside the Java Virtual Machine in native code.
See problematic frame for where to report the bug.
This is the C++ code:
#include "stdafx.h"
#include <stdio.h>
#include "CCCheckString.h"
#include <vector>
#include <String>
using namespace std;
#include "jobHandler.h"
JNIEXPORT jbolean JNICALL Java_CCCheckString_Login
(JNIEnv *env, jobject object, jstring host, jstring UserName, jstring Domain, jstring Password)
{
bool result;
jobHandler *handler = new jobHandler();
const char *hostStr = (env)->GetStringUTFChars(host, NULL);
string hostS(hostStr);
const char *UserNameStr = (env)->GetStringUTFChars(UserName, NULL);
string UserNameS(UserNameStr);
const char *DomainStr = (env)->GetStringUTFChars(Domain, NULL);
string DomainS(DomainStr);
const char *PasswordStr = (env)->GetStringUTFChars(Password, NULL);
string PasswordS(PasswordStr);
//if comment this line everthing is okey
**result = handler->Login(hostS,UserNameS,DomainS,PasswordS);**
(env)->ReleaseStringUTFChars(host, NULL);
(env)->ReleaseStringUTFChars(UserName, NULL);
(env)->ReleaseStringUTFChars(Domain, NULL);
(env)->ReleaseStringUTFChars(Password, NULL);
delete handler;
return result;
}
Below is the processing code in Java:
CCCheckString ccCheckString = new CCCheckString();
result=ccCheckString.Login("xxx", "xxx", "xx", "xxx");
How can i fix the error?
I solved.
Problem is that Login() accepts std::string but we cannot send it
We reorganize our Login() and it must take const char []
Ex:
bool ClassName::Login(std::string host,std::string userName,std::string userDomain,std::string userPassword)
{ //Orginal Code Here ....}
bool ClassName::Login(const char host[],const char userName[],const char userDomain[],const char userPassword[])
{
std::string strHost(host);
std::string strUserName(userName);
std::string strUserDomain(userDomain);
std::string strUserPassword(userPassword);
return Login(strHost,strUserName,strUserDomain,strUserPassword);
}
Everything is fine.

Programmatically getting Version number of Java and Flash using C, C++, or Java

I want to find the version number of the current version of Java installed on the computer and the current version of Flash installed on any given web browser that has flash. Is there a way to do this using Java, C, or C++? If so, what class/library should I look into?
If you are in Java
System.out.println(System.getProperty("java.version"));
System.out.println(System.getProperty("java.vendor"));
System.out.println(System.getProperty("java.vm.name"));
output
1.7.0_03
Oracle Corporation
Java HotSpot(TM) Client VM
there are more props about Java
for(Map.Entry e : System.getProperties().entrySet()) {
if (((String)e.getKey()).startsWith("java")) {
System.out.println(e);
}
}
.
java.runtime.name=Java(TM) SE Runtime Environment
java.vm.version=22.1-b02
java.vm.vendor=Oracle Corporation
java.vendor.url=http://java.oracle.com/
java.vm.name=Java HotSpot(TM) Client VM
java.vm.specification.name=Java Virtual Machine Specification
java.runtime.version=1.7.0_03-b05
....
As for other languages I think you can do it by running %JAVA_HOME%/java -version from your app, reading the output
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b05)
Java HotSpot(TM) Client VM (build 22.1-b02, mixed mode, sharing)
Or you can right a JavaProps app like the above, run it as %JAVA_HOME%/java JavaProps and read the output
Detecting java version from C/C++ could be tricky, the thing is that there is not standard API you can use to get the installed JRE properties. A way to do it is creating a subprocess that invokes java -version and get the output from this process (also you can make the subprocess write to a specific file and then retrieve the data from it) , then parse it and get the version.
The following is a modified version of a Windows .exe launcher routine I did sometime ago that uses what I just explained (create the subprocess for java -version, make the subprocess write to a temp file, open the file and read its content, parse the version):
void printJVMVersion() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
size_t sizeOfStartupInfo = sizeof(STARTUPINFO);
ZeroMemory(&si, sizeOfStartupInfo);
si.cb = sizeOfStartupInfo;
SECURITY_ATTRIBUTES sa = { sizeof(sa) }; // Open files in inheritable mode
sa.bInheritHandle = TRUE; // Allow inheritance
sa.lpSecurityDescriptor = NULL; // Handles to the child process
si.dwFlags = STARTF_USESTDHANDLES;
const std::string DEFAULT_TEMP_FILE_NAME = "temp_file.txt";
const size_t MAX_PATH = 256;
char buffer[MAX_PATH];
char tempPathBuffer[MAX_PATH];
GetTempPath(MAX_PATH, tempPathBuffer);
std::string file_name;
if ( GetTempFileName(tempPathBuffer, "some_random_prefix", 0, buffer) != 0 ) {
file_name = std::string(buffer);
} else {
file_name = DEFAULT_TEMP_FILE_NAME;
}
si.hStdError = CreateFileA(file_name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, 0, NULL);
si.hStdOutput = CreateFileA(file_name.c_str(), GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, 0, NULL);
if (si.hStdOutput == NULL) {
MessageBox(NULL, "Error checking for the installed Java Virtual Machine, operation aborted", "Launching", MB_ICONERROR | MB_OK);
return false;
}
if (!CreateProcessA(NULL, "java -version", NULL, NULL, true, 0, NULL, NULL, &si, &pi)) {
MessageBox(NULL, "It appears that neither you have the Java Virtual Machine installed on your system nor its properly configured", "Launching", MB_ICONERROR | MB_OK);
ErrorExit("CreateProcessA");
return false;
}
WaitForSingleObject( pi.hProcess, INFINITE );
if( si.hStdError ) {
CloseHandle( si.hStdError );
}
if( si.hStdOutput ) {
CloseHandle( si.hStdOutput );
}
// "Parse" the txt file
std::ifstream ifs(file_name.c_str());
std::string line;
int versionIndex = 0;
int value[2];
value[0] = value[1] = 0;
while (std::getline(ifs, line)) {
const std::string JAVA_VERSION_STRING = "java version ";
size_t index = line.find(JAVA_VERSION_STRING.c_str());
if (index != std::string::npos) {
// get either the 1.X.X or 2.X.X
std::string version = line.substr(JAVA_VERSION_STRING.size());
std::string::iterator ite = version.begin();
std::string::iterator end = version.end();
std::string tmp = "";
for (; ite != end; ++ite) {
char c = *ite;
if (isdigit(c)) {
tmp += c;
} else if (c == '.') {
value[versionIndex] = atoi(tmp.c_str());
versionIndex++;
tmp = "";
// If we have, version, major and minor, then another set of digits is unnecessary
if (versionIndex == 2)
break;
}
}
std::cout << "detected java version: " << (value[0]) << ", " << (value[1]) << std::endl;
}
}
// Delete the temp file
DeleteFile(file_name.c_str());
}
Hope this can be of any help

Calling Java Methods from Visual C/C++ using C++/CLI

I'm getting a "error LNK1104: cannot open file {path}\jvm.lib" when trying tocompile a VS C++/CLI (managed) project. It's very simple and my goal is to call some Java methods in pre-existing java libs - here is the code I'm using:
// This is the main DLL file.
#include "stdafx.h"
#include <jni_md.h>
#include <jni.h>
#include "JBridge.h"
#pragma once
using namespace System;
namespace JBridge
{
public ref class JniBridge
{
// TODO: Add your methods for this class here.
public:
void HelloWorldTest()
{
System::Console::WriteLine("Hello Worldl from managed C++!");
}
JNIEnv* create_vm(JavaVM ** jvm)
{
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
//Path to the java source code
options.optionString = "-Djava.class.path=D:\\Java Src\\TestStruct";
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM\n");
return env;
}
};
}
I've verified the file does exist in the path location and I've added it to the project properties for the include dir and the linker property pages.
Update
Got the jvm.lib to be linked with a bit more fiddling.
Compilation causes following errors during build:
Error 1 error LNK2028: unresolved token (0A00000A) "extern "C" long __stdcall JNI_CreateJavaVM(struct JavaVM_ * *,void * *,void *)" (?JNI_CreateJavaVM##$$J212YGJPAPAUJavaVM_##PAPAXPAX#Z) referenced in function "struct JNIEnv_ * __cdecl create_vm(struct JavaVM_ * *)" (?create_vm##$$FYAPAUJNIEnv_##PAPAUJavaVM_###Z) c:\Temp\CLRTest\JBridge\JBridge\JBridge.obj JBridge
Error 2 error LNK2019: unresolved external symbol "extern "C" long __stdcall JNI_CreateJavaVM(struct JavaVM_ * *,void * *,void *)" (?JNI_CreateJavaVM##$$J212YGJPAPAUJavaVM_##PAPAXPAX#Z) referenced in function "struct JNIEnv_ * __cdecl create_vm(struct JavaVM_ * *)" (?create_vm##$$FYAPAUJNIEnv_##PAPAUJavaVM_###Z) c:\Temp\CLRTest\JBridge\JBridge\JBridge.obj JBridge
Error 3 error LNK1120: 2 unresolved externals c:\temp\CLRTest\JBridge\Debug\JBridge.dll JBridge
Work around was to dynamically load the JVM by using LoadLibrary("path/to/jvm"); and then invoking the native functions.

Categories