Jni C++ findclass function return null - c++

I found a problem with jni about C calling Java code.
Environment WIN10 JDK1.8
Currently I need C++ code to call Java code. At first I wrote a demothat was successful. Code show as below:
public class Sample2 {
public String name;
public static String sayHello(String name) {
return "Hello, " + name + "!";
}
public String sayHello() {
return "Hello, " + name + "!";
}
}
Some of the C++ code is as follows:
int main(){
printf("hello world");
JavaVMOption options[3];
JNIEnv* env;
JavaVM* jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jfieldID fid;
jobject obj;
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = 0;
// 启动虚拟机
status = JNI_CreateJavaVM(&jvm, (void**)& env, &vm_args);
if (status != JNI_ERR){
// 先获得class对象
cls = env->FindClass("Sample2");
}
}
I used Eclipse to compile the Java code into a .class file, copy the .class file into my C++ project, the above DEMO C++ call Java function is successful, and the findclass function returns to normal.
Because I have to introduce a third-party JAR package org.eclipse.paho.client.mqttv3-1.2.0.jar in my own Java, based on the above example, I modified the Java code in DEMO, but when I want to reference the JAR package function, And then run successfully in Eclipse, when I copy the .class file to the C++ project. JNI_CreateJavaVM in the C++ code is returned successfully, but FINDCLASS always returns null, I don't know why. I have not changed the other parts code.
Some Java code:
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Sample2 {
public String name;
static MqttAsyncClient mqttClient = null;
static String username = "xxx";
static String password = "xxx";
static String broker = "xxx";
public static void main(String[] args) throws InterruptedException {
System.out.print("hello");
}
public static void start() {
String clientId = "mqttserver" + String.valueOf(System.currentTimeMillis());
try {
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence());
} catch (Exception me) {
me.printStackTrace();
}
}
When in start function is added
mqttClient = new MqttAsyncClient(broker, clientId, new MemoryPersistence()); After the code, there will be problems

Take a look here
char opt1[] = "-Djava.compiler=NONE";
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
options[0].optionString = opt1; options[0].extraInfo = NULL;
options[1].optionString = opt2; options[1].extraInfo = NULL;
options[2].optionString = opt3; options[2].extraInfo = NULL;
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
You are passing three options (array of options with three options defined) but then, you say something like this
vm_args.nOptions = 1;
which means you are passing just one option. It means your options
char opt2[] = "-Djava.class.path=.";
char opt3[] = "-verbose:NONE";
are not even read. You have to change your code to
vm_args.nOptions = 3;
Also, make sure to put on java.class.path all the JARs, folders, where classes required by your code are.

Related

HMAC SHA256 sign on Java, Verify on C++ private-public keys

I try to sign some data by Java with private key and then verify it by C++ with public key. I user Java as client and C++ as server.
Java run on Windows, C++ on Ubuntu
in Java I use
key = "MIIEowIBAAKCAQ......s8mFoA2"; //private key
byte[] b1 = Base64.decodeBase64(key);
this.Sign = hmacSha256Base64("test", b1);
/**************/
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, NoSuchProviderException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256", "BC");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.encodeBase64String(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
and on C++, to verify I real try different code, for example:
int verify_it(const unsigned char *msg, size_t mlen, const unsigned char *val, size_t vlen, EVP_PKEY *pkey)
{
/* Returned to caller */
int result = 0;
EVP_MD_CTX* ctx = NULL;
unsigned char buff[EVP_MAX_MD_SIZE];
size_t size;
int rc;
if (!msg || !mlen || !val || !vlen || !pkey)
return 0;
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
if (rc != 1) {
printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignUpdate(ctx, msg, mlen);
if (rc != 1) {
printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
goto err;
}
size = sizeof(buff);
rc = EVP_DigestSignFinal(ctx, buff, &size);
if (rc != 1) {
printf("EVP_DigestSignFinal failed, error 0x%lx\n", ERR_get_error());
goto err;
}
result = (vlen == size) && (CRYPTO_memcmp(val, buff, size) == 0);
err:
EVP_MD_CTX_free(ctx);
return result;
}
RSA* createPublicRSA(std::string TermId, bool is_local) {
RSA *rsa = NULL;
BIO *keybio;
FILE * fp = fopen((SettingsConfig["UserKeys"] + "user_public/" + TermId).c_str(), "rb");
if (fp != 0)
{
rsa = PEM_read_RSA_PUBKEY(fp, &rsa, NULL, NULL);
fclose(fp);
}
return rsa;
}
size_t calcDecodeLength(const char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
padding = 2;
else if (b64input[len - 1] == '=') //last char is =
padding = 1;
return (len * 3) / 4 - padding;
}
void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) {
BIO *bio, *b64;
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
*length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
}
std::string test = "XChhsTE....NkE="; //Sign from Java
std::string msg = "test";
RSA* publicRSA = createPublicRSA("#1.pem", false); //public key
EVP_PKEY* pubKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pubKey, publicRSA);
unsigned char* encMessage;
size_t encMessageLength;
Base64Decode(test.c_str(), &encMessage, &encMessageLength);
int result_sign = verify_it((unsigned char*)msg.c_str(), msg.length(), encMessage, encMessageLength, pubKey);
std::cout << std::to_string(result_sign) << std::endl; //return 0
And any others examples return false. I don't know what is problem, please help! Thanks!
p.s. private key:
MIIEowIBAAKCAQEAra2jau89VIfcunyOth5O08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYiUjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplLepYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQvgrwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiDUOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7DwIDAQABAoIBAHbNlkGp0Uwne6fdWEnfxZA4QPLTGpL/FmdiUXux+pAsYXqzHVG1Ww/CN7/82cYAOEYSn6OzZAGBPw1DW+uPRV7wp2xU+Ljz8H69g7ISEs1zXGTfW67v0GUSYor2ZoZKPAajcmpPh4ltqacxP3q9pdH/NlpWIpm5gAGOo8STsoHl0PItHpxYbWXRylzWIgysalYPRERicT/ibQlJ4w8jhdk1lqYZAyEg2trJXDXxiNGx19OxEfRoqDVumK+W7Pn38ye9zgjuR8TYRAMPJ1WcQ9HZPLZKbVBvjztLSvUk/Q+Z8PoomIN9s+Ggev1y6+ccOiRWpPQLp45483k5fHHXTpECgYEA4KJsRwGTw3yomIAN+k0eFSL/+bJJBimQXjRcc0qw+NbeLoytfVrnSCBD85QYamcB8tMg+CvcCdJve46ByOsmYN6jXLdUmai4Nt/kJfUU6bWpPwBdtUOGKb9mYH4xLGnnJqyUhCJ+vhY6WrOUBXu1KfkQZUEc/r/EWyEo09UNsCsCgYEAxe3IQ2tXI1zJ91xu0R309eToH/ZhfVSKR6UBoCKptEweBBPlCj8tQcKS6Ao9Oyk28rSoAYM8thy1V9+XItku97L+fP1JSZMkGq3g/r4DHuklshDoR3xAYOSZ6/89BxsV0O9a92bb0CV472wM7HHH0KAMtODwRqw8IpC5qlMHiq0CgYEAxYTMJJt0bF3+eSmQINkSbI97+PkVUL/XW5469H1mo0d70f6Mxj7aQwdr+I/t8BFnGzceNFmMf25z7HbgE+UAuAjMKEhjsUEzybyQhfe8TcwYZ3dQ7oPTQn4z7QDJCD6Oq+jwJkeWnlo5MWvZ6gBeyetgyUe50R6Z72920NzzzkUCgYBeY/V7YXde3+NZWfVnONgXZCDnDUKU2HpRjHln+t/foeU2oJ478sEMeVRB4JAu5IrV2B2/Cu0rFCnPTEvxTI2/htcimFAZDFjNeFqyYb9vQFS/xJxhavnwu1REXaam+t2+lEdXcPAnJZe05lyLbf+SmKE2qYcszPqoqUhB1/LiyQKBgGDXVyw05oSvR9GGKfMKIghRimeF97+EZhS718zcuDqXJ8Qmn+S+qrrwvn1X7TZbZ3bnM6JSnC5FcgLLVTWulLShjIo2ctsqaZUPnUJTBPoCJMkmGCR8H6XaVuFlfElT/jXglqwS+UkMMM2WPkDubnLzuTuslH1DJnrBBs8mFoA2
public key:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra2jau89VIfcunyOth5O
08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYi
UjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplL
epYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQv
grwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiD
UOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7
DwIDAQAB
-----END PUBLIC KEY-----
message: 12105333071
signaturee from Java: XChhsTE+Yr4wkiibvTFiLTMhJ8tLqYo7WQs///VtNkE=
Just using HMACSHA256 is not the same as Private/Public Key signature. The full name of HMACSHA256 is "Hash-based Message Authentication Code" and you "sign" and "verify" this with the same "key" that is just a byte array and has nothing to do with Private or Public Key.
Of course you can take the encoded bytes of the Private/Public key as input, but when doing so (I do NOT recommend this)
you need to pass the same key to the verification part.
I setup two small programs to show how it works. For Java I'm using your code except of using Bouncy Castle as "native" Java
should have this build in. As well I left out the apache-Base64-conversion as it's build in as well. The C#-part is the same program but has a "verification" output.
Both code samples do not have any exceptional handling and are for educational purposes only.
Result of Java-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
hmacSha256 (Base64): /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Result of C#-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
HMACSHA256 in C#: /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
HMACSHA256 Java : /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Hashes are equal: True
Java-code:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Org {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
System.out.println("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
String message = "12105333071";
String key = "12345678901234567";
String result = hmacSha256Base64(message, key.getBytes(StandardCharsets.UTF_8));
System.out.println("hmacSha256 (Base64): " + result);
}
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
}
C#-code:
using System;
using System.Text;
using System.Security.Cryptography;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
string message = "12105333071";
string key = "12345678901234567";
string expectedHashBase64 = "/1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0="; // from Java
// generate HMACSHA256
string hmacSha256DigestBase64 = HmacSha256DigestBase64(key, message);
Console.WriteLine("HMACSHA256 in C#: " + hmacSha256DigestBase64);
Console.WriteLine("HMACSHA256 Java : " + expectedHashBase64);
Console.WriteLine("Hashes are equal: " + hmacSha256DigestBase64.Equals(expectedHashBase64, StringComparison.OrdinalIgnoreCase));
//Console.ReadLine();
}
private static string HmacSha256DigestBase64(string secret, string message)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return Convert.ToBase64String(bytes);
}
}
Golang code to complete the collection (tested to produce the exactly same result as the java code form Michael Fehr:
package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
b64 "encoding/base64"
)
func main() {
secret := "12345678901234567"
data := "12105333071"
fmt.Printf("Secret: %s Data: %s\n", secret, data)
// Create a new HMAC by defining the hash type and the key (as byte array)
h := hmac.New(sha256.New, []byte(secret))
// Write Data to it
h.Write([]byte(data))
// Get result and base64 encode the string
sha := b64.StdEncoding.EncodeToString(h.Sum(nil))
fmt.Println("Result: " + sha)
}

Passing a list/arraylist of reference type objects contained in a class to JNI/C++

I wrote a piece of code that manipulates reference type objects contained inside a class, now am having trouble while converting this instance to a list of objects.
Here is the code snippet;
classes.java
=================
public class Leg {
public int id;
public String name;
// a few other primitive data types
....
}
public class Line {
public int id;
....
//public Leg mLeg; this worked well
public List<Leg> mLegList;
}
Driver.java
============
public native void setLine(Line jobj);
Line line = new Line();
line.id =200;
line.mLegList = new ArrayList<Leg>(1); // using only one for brevity
Leg obj = new Leg(200, "test", ...);
line.mLegList.add(obj);
Native CPP function
==================
JNIEXPORT void JNICALL Java_Driver_setLine (JNIEnv * env, jobject jobj1, jobject jobj)
{
jclass cls = env->GetObjectClass(jobj);
if (cls == NULL)
return;
jmethodID ctorID = env->GetMethodID(cls, "<init>","()V");
if (!ctorID)
return ;
// I did for one instance like this
/*************************************************************
* jfieldID fid_legID = env->GetFieldID(cls, "mLeg", "LLeg;");
* if (!fid_legID)
* return;
* jobject legObject = env->GetObjectField(jobj, fid_legID);
* jclass clsObject = env->GetObjectClass(legObject);
* if (clsObject) {
* // Get all fields of Leg inside Line ....
*************************************************************/
// Now as i changed it to list/arraylist of Legs, it isn't Working
jfieldID fid_legID = env->GetFieldID(cls, "mLegList", "[LLeg;");
if (!fid_legID)
env->ExceptionDescribe();
// Exception in thread "main" java.lang.NoSuchFieldError: mLegList
}
My questions are:
how to make it working with a list/ArrayList here. I need to
retrieve a set of fields contained in this list.
Why isn't the
native function signature included jobjectarray (or something similar
for jobj1?) Is that causing issues? Thanks for reading the post.
Update
I was able to construct the object successfully as using Arraylist/List require to use
fieldID fid_legID = env->GetFieldID(cls, "mLegList", "Ljava/util/List;");
instead. but now I'm wondering how to iterate through this list and read through all the fields.
Let's say your source code tree looks like this
|-- c
| `-- Driver.c
|-- lib
|-- mypackage
| |-- Driver.java
| |-- Leg.java
| `-- Line.java
`-- target
and then, you have following files:
mypackage/Driver.java
package mypackage;
import java.util.ArrayList;
public class Driver {
static {
System.loadLibrary("Driver");
}
public void list(ArrayList<Leg> list) {
}
public native void setLine(Line line);
public static void main(String [] arg) {
Driver d = new Driver();
Line line = new Line();
line.id = 200;
line.mLegList = new ArrayList<Leg>(1);
Leg obj_1 = new Leg(200, "test_1");
Leg obj_2 = new Leg(300, "test_2");
Leg obj_3 = new Leg(400, "test_2");
line.mLegList.add(obj_1);
line.mLegList.add(obj_2);
line.mLegList.add(obj_3);
d.setLine( line );
}
}
mypackage/Leg.java
package mypackage;
class Leg {
public int id;
public String name;
public Leg(int id, String name) {
this.id = id;
this.name = name;
}
}
mypackage/Line.java
package mypackage;
import java.util.List;
public class Line {
public int id;
public List<Leg> mLegList;
}
You can access elements of the List following way
c/Driver.c
#include "mypackage_Driver.h"
JNIEXPORT void JNICALL Java_mypackage_Driver_setLine(JNIEnv * env, jobject jobj, jobject line)
{
jclass cls_Line = (*env)->GetObjectClass (env, line);
jfieldID fid_mLegList = (*env)->GetFieldID (env, cls_Line, "mLegList", "Ljava/util/List;");
jobject list = (*env)->GetObjectField (env, line, fid_mLegList);
jclass cls_list = (*env)->GetObjectClass (env, list);
int listSize = (*env)->GetArrayLength (env, list);
for (int i = 0; i < listSize; i++) {
jmethodID midGet = (*env)->GetMethodID (env, cls_list, "get",
"(I)Ljava/lang/Object;");
jstring obj = (*env)->CallObjectMethod (env, list, midGet, i);
jclass cls_Leg = (*env)->GetObjectClass(env, obj);
jfieldID fid_name = (*env)->GetFieldID( env, cls_Leg, "name",
"Ljava/lang/String;");
jobject name = (*env)->GetObjectField (env, obj, fid_name);
const char *c_string = (*env)->GetStringUTFChars (env, name, 0);
printf ("[value] = %s\n", c_string);
(*env)->ReleaseStringUTFChars (env, obj, c_string);
}
}
If you want to compile all the stuff, you can use following script
#!/bin/bash
mkdir -p target
mkdir -p lib
ARCH=`uname -s | tr '[:upper:]' '[:lower:]'`
EXT=
if [[ "${ARCH}" == "darwin" ]]; then
EXT=dylib
else
EXT=so
fi
echo $ARCH
javac -h c -d target mypackage/*.java
cc -g -shared -fpic -I${JAVA_HOME}/include -I${JAVA_HOME}/include/${ARCH} c/Driver.c -o lib/libDriver.${EXT}
${JAVA_HOME}/bin/java -Djava.library.path=${LD_LIBRARY_PATH}:./lib -cp target mypackage.Driver
Once you run it, you will get what you are looking for :)
> ./compile.sh
darwin
[value] = test_1
[value] = test_2
[value] = test_2
Have fun with JNI :)
Update
accessing array elements using get method: recipeNo067
accessing array elements using Iterator: recipeNo068
passing ArrayList as Object []: recipeNo069
Usage
> git clone https://github.com/mkowsiak/jnicookbook.git
> export JAVA_HOME=your_JDK_installation
> cd jnicookbook/recipes/recipeNo067
> make
> make test
Iterating across a List in JNI is the same as in Java, it is just more typing.
jobject list = env->GetObjectField(line, fid_legID);
// Iterator iterator = list.iterator();
jclass cls_List = env->FindClass("java/util/List");
jmethodID mid_List_iterator = env->GetMethodID(cls_List, "iterator", "()Ljava/util/Iterator;");
jobject iterator = env->CallObjectMethod(list, mid_List_iterator);
jclass cls_Iterator = env->FindClass("java/util/Iterator");
jmethodID mid_Iterator_hasNext = env->GetMethodID(cls_Iterator, "hasNext", "()Z");
jmethodID mid_Iterator_next = env->GetMethodID(cls_Iterator, "next", "()Ljava/lang/Object");
while (true) {
// if !iterator.hasNext() break
jboolean hasNext = env->CallBooleanMethod(iterator, mid_Iterator_hasNext);
if (!hasNext)
break;
jobject v = env->CallObjectMethod(iterator, mid_Iterator_next);
// Do something with `v`
// Avoid overflowing the local reference table if legList gets large
env->DeleteLocalRef(v);
}
Note that this example still needs error checking.

Passing String argument from c to java using jni

I am trying to call java method from C, but getting below error
Status after JNI_CreateJavaVM=<0>
restCall method found:: Avinash Kumar
Execution error : file ''
error code: 114, pc=0, call=1, seg=0
114 Attempt to access item beyond bounds of memory (Signal 11)
Target is to send String as an argument from C to Java function.
Below is the code :
#include <jni.h>
#define ENV (*env)
enum eConst
{
MAX_Options = 21 ,
MAX_LREF = 120 ,
PARSING_ERROR = -10 ,
ERROR = -999 ,
EXCEPTION_ERROR = -100 ,
OK = 1 ,
YES = 'Y' ,
NO = 'N'
};
int main()
{
jint jRet;
jstring jszRes;
static JavaVM *jvm = NULL;
static JNIEnv *env = NULL ;
JavaVMInitArgs vm_args;
JavaVMOption options[MAX_Options-1];
int optionCount=0;
options[optionCount++].optionString = "-verbose:jni,class,gc";
char *path="user/work/avikumar/";
char *class_path="user/work/avikumar/";
char path_option[2000]={'\n'};
sprintf(path_option,"-Djava.class.path=%s/MyClass.class:%s/jersey-core-1.19.jar:%s/jersey-client-1.19.jar:%s/javax.ws.rs-api-2.0-m02.jar:.",class_path,path,path,path);
options[optionCount++].optionString = path_option;
options[optionCount++].optionString = "-Xms128m";
options[optionCount++].optionString = "-Xmx512m";
options[optionCount++].optionString = "-Xss8m";
vm_args.options = options;
vm_args.nOptions = optionCount;
vm_args.version = JNI_VERSION_1_6;
vm_args.ignoreUnrecognized = JNI_FALSE;
jRet = JNI_CreateJavaVM(&jvm,(void**)&env,(void*)&vm_args);
printf("Status after JNI_CreateJavaVM=<%d>\n",jRet);
if (jRet < 0)
{
return(-4);
}
if( ENV->EnsureLocalCapacity(env, MAX_LREF) < 0)
{
printf( "\n out of memory. Program terminated\n");
(*jvm)->DestroyJavaVM(jvm);
return(-5);
}
jclass jlocClass;
jlocClass= ENV->FindClass(env, "MyClass");
if (jlocClass == NULL || (*env)->ExceptionOccurred(env))
{
printf("Can not load the main class\n");
return (-6);
}
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
if( restCall_method == NULL)
{
printf("restCall method not found\n");
return (-7);
}
char *inp = "Avinash Kumar";
printf("restCall method found:: %s\n",inp);
//jstring jstr = (*env)->NewStringUTF(env,inp);
jstring jstr1=(*env)->NewStringUTF(env, "Avinash Kumar");
ENV->CallVoidMethod(env, jlocClass, restCall_method,jstr1);
(*jvm)->DestroyJavaVM(jvm);
return 0;
}
MyClass.java
class MyClass
{
public static void main(String args[])
{
System.out.println("Hello Avinash\n");
}
public void restCall(String inp)
{
System.out.println(inp+ " :: Hello Avinash from restCall\n");
}
}
You can't mix static/non-static context inside JNI. You have two choices.
Static method
restCall_method = ENV->GetStaticMethodID(
env, jlocClass, "restCall", "(Ljava/lang/String;)V");
...
...
...
ENV->CallStaticVoidMethod(env, jlocClass, restCall_method,jstr1);
and, inside Java
public static void restCall(String inp)
or non-static method
jmethodID init = ENV->GetMethodID(env,jlocClass, "<init>", "()V");
jobject obj_MyClass = ENV->NewObject(env,jlocClass, init);
jmethodID restCall_method;
restCall_method = ENV->GetMethodID(env, jlocClass,"restCall","(Ljava/lang/String;)V");
...
...
...
ENV->CallVoidMethod(env, obj_MyClass, restCall_method,jstr1);
and, inside Java
public void restCall(String inp)
In first case, you have to use Static family methods to get id and call target method, in second case, you have to create instance of class and use methods for object based context.

Using VC++ 6.0 COM DLL from Threads C#

BackGround:
We have DLL created using VC++6.0. This DLL in interface have functions that send message to our server. So external applications call function of our DLL by passing input parameter and our DLL send this message to a server using TCP/IP and return back a output for caller.
DLL is compiled with following settings
Main of DLL looks like this
I have created a samll application in C# (framework 2.0) that creates thread to call function of DLL. What happens is that second thread gets blocked until first thread has not finished the job.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using PAGAMENTOLib;
using log4net;
using log4net.Config;
using System.Configuration;
namespace ThreadTestAPI
{
class Program
{
public static ILog log = LogManager.GetLogger(typeof(Program));
argpayClass apiFunctions = new argpayClass();
static void Main(string[] args)
{
XmlConfigurator.Configure();
List<string> Ip_List = new List<string>();
List<string> Ip_port = new List<string>();
int MAX_THREAD = Convert.ToInt32(ConfigurationManager.AppSettings["MAX_THREAD"].ToString());
string ReplacePattern = string.Empty;
string IP = ConfigurationManager.AppSettings["IP"].ToString();
string PORT = ConfigurationManager.AppSettings["PORT"].ToString();
Program call = new Program();
//int LastPart = Convert.ToInt32(IP.Split('*')[1]);
//string PatterntoReplase = "*" + LastPart;
log.Info("-------------------------------------------------------------------------");
log.Info(" Started Program ");
log.Info("-------------------------------------------------------------------------");
List<Thread> ThreadList = new List<Thread>(); //Added by Asif Iqbal
WaitAllThreads waitThreads = new WaitAllThreads();
List<Thread> myPOSthread = new List<Thread>();
ParamIn param = new ParamIn();
for (int i = 0; i < MAX_THREAD; i++)
{
Thread thread = new Thread(new ParameterizedThreadStart(call.CallApiFunction));
thread.Name = "Thread " + i;
param.Ped_ip = IP;
log.Info("Thread Name is : " + thread.Name);
log.Info("IP is " + param.Ped_ip);
param.PedPort = PORT.Replace("*",i.ToString());
log.Info("Port is " + param.PedPort);
param.Amount = i;
param.port_listener = "0";
param.DatiAgg = "Thread " + i;
ThreadList.Add(thread);
thread.IsBackground = true;
thread.TrySetApartmentState(ApartmentState.MTA);
thread.Start(param);
myPOSthread.Add(thread);
Console.WriteLine("***********************************************************");
Console.WriteLine("Thread Name: " + thread.Name);
System.Threading.Thread.Sleep(250);
}
}
[MTAThread]
public void CallApiFunction(object param)
{
log.Info("Calling Api function");
ParamIn members = (ParamIn)param;
//argpayClass apiFunctions = new argpayClass();
string Response = string.Empty;
apiFunctions.PagamentoTCP(0,500,ref members.Ped_ip,Convert.ToInt32(members.PedPort),ref members.DatiAgg,ref Response);
log.Info("IP is " + members.PedPort + ".Response Received from Api is: " + Response);
}
}
public class ParamIn
{
public int Amount;
public string DatiAgg;
public string PedPort;
public string Ped_ip;
public string ip_listener;
public string port_listener;
public int tmo;
public int PrintTicket;
public int Advance;
public ParamIn()
{
Amount = 0;
DatiAgg = "";
Ped_ip = "";
PedPort ="";
port_listener = "";
ip_listener = "";
tmo = 0;
PrintTicket = 0;
}
}
}
I tried to use
CoInitializeEx(NULL, COINIT_MULTITHREADED);
in DLL main but no success. I also changed this but still same behavior
public CComObjectRootEx<CComSingleThreadModel>
to public CComObjectRootEx<CComMultiThreadModel>
Does anyone know why Function of dll doesn't get called until first thread is in progress?
Whereas in c# threads were created correctly. Each thread finish job but not in pralall of others. It has to wait.
If you know your object’s implementation is thread safe and you want to tell .NET about that, implement IMarshal interface in your COM object.
Use CoCreateFreeThreadedMarshaler to create the marshaler, return the marshaler when .NET asks for IMarshal interface through QueryInterface call.
If you’re using ATL to implement your object, look at my answer for more info.

FindClass returns null

I'm having problems with my FindClass call which returns null:
JData::JIgZorroBridgeClass = env->FindClass(JData::IgZorroBridgePath);
Most answers I find about this points to missing package name, or class loader issues.
IgZorroBridgePath here is my class with fully qualified package name, ie. com/igz/Zorro.
Also as you can see I'm initializing the JVM right before this FindClass call, so I think I should be on the correct thread already.
Is there anything else I can check?
#include <assert.h>
#include "JNIHandler.hpp"
#include "JReferences.hpp"
const char* JVMClassPathOption = "-Djava.class.path=Plugin/ig/igplugin-0.1.jar";
const char* IgZorroBridgePath = "com/danlind/igz/ZorroBridge";
void JNIHandler::init()
{
initializeJVM();
initializeJavaReferences();
}
void JNIHandler::initializeJVM()
{
if(isJVMLoaded) return;
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JData::JNI_VERSION;
args.nOptions = 1;
args.options = options;
options[0].optionString = (char*)JData::JVMClassPathOption;
args.ignoreUnrecognized = JNI_FALSE;
jint res = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
assert(res == JNI_OK);
isJVMLoaded = true;
}
JNIEnv* JNIHandler::getJNIEnvironment(){
return env;
}
JNIEnv* JNIHandler::getEnvForCurrentThread()
{
int envStat = jvm->GetEnv((void **)&env, JData::JNI_VERSION);
if (envStat == JNI_EDETACHED) {
jint res = jvm->AttachCurrentThread((void**)&env, NULL);
assert (res == JNI_OK);
}
return env;
}
void JNIHandler::initializeJavaReferences()
{
if(areJavaReferencesInitialized) return;
initExceptionHandling();
initBridgeObject();
registerNatives();
areJavaReferencesInitialized = true;
}
void JNIHandler::initBridgeObject()
{
JData::JIgZorroBridgeClass = env->FindClass(JData::IgZorroBridgePath);
checkJNIExcpetion(env);
registerClassMethods();
JData::JIgZorroBridgeObject = env->NewObject(JData::JIgZorroBridgeClass, JData::constructor.methodID);
checkJNIExcpetion(env);
}
void JNIHandler::initExceptionHandling()
{
JData::ExceptionClass = env->FindClass(JData::ExcPath);
JData::excGetName.methodID = env->GetMethodID(JData::ExceptionClass, JData::excGetName.name, JData::excGetName.signature);
}
void JNIHandler::registerNatives()
{
JData::JZorroClass = env->FindClass(JData::ZorroPath);
env->RegisterNatives(JData::JZorroClass, JData::nativesTable, JData::nativesTableSize);
checkJNIExcpetion(env);
}
void JNIHandler::registerClassMethods()
{
for(auto *desc : JData::igZorroBridgeMethods)
{
desc->methodID = env->GetMethodID(JData::JIgZorroBridgeClass, desc->name, desc->signature);
checkJNIExcpetion(env);
}
}
void JNIHandler::checkJNIExcpetion(JNIEnv* env)
{
jthrowable exc = env->ExceptionOccurred();
if (!exc) return;
jclass exccls(env->GetObjectClass(exc));
jstring name = static_cast<jstring>(env->CallObjectMethod(exccls, JData::excGetName.methodID));
char const* utfName(env->GetStringUTFChars(name, 0));
JData::excGetMessage.methodID = env->GetMethodID(exccls, JData::excGetMessage.name, JData::excGetMessage.signature);
jstring message = static_cast<jstring>(env->CallObjectMethod(exc, JData::excGetMessage.methodID));
char const* utfMessage(env->GetStringUTFChars(message, 0));
BrokerError(utfName);
BrokerError(utfMessage);
env->ReleaseStringUTFChars(message, utfMessage);
env->ReleaseStringUTFChars(name, utfName);
env->ExceptionClear();
}
I was using the spring boot maven plugin, which reshuffles the package structure. I switched to using the shade plugin instead, which doesn't move packages around. This enabled FindClass to find my java class.