How to create a string array using JNI in Qt - java

I have to write some Android platform specific code in Qt and need to use JNI. I have a problem with how to create an array of some object. In this case I want to construct an array of strings from C++.
In the two code snippets below the first one creates a java string and it works as expected. In the second code snippet I want to create a java string array, but I get the debug message: "Java string array not valid" so I assume the signature and/or parameters passed to the "QAndroidJniObject javaStringArray()" function is not correct.
I have been looking at the documentation, but was not able to find or properly understand how to do this.
I assume I have to send in the size of the java string array object I want to construct as well.
Any help is appreciated!
QAndroidJniObject javaString("java/lang/String");
if (!javaString.isValid()) {
qDebug() << "Java string not valid";
return false;
}
QAndroidJniObject javaStringArray("[Ljava/lang/String;");
if (!javaStringArray.isValid()) {
qDebug() << "Java string array not valid";
return false;
}

The QAndroidJniObject constructor you are using takes a class name, so I'm afraid passing a string array signature won't work. You'll probably have to get your hands dirty and call JNI NewObjectArray(). Try something like:
QAndroidJniEnvironment env;
jobjectArray stringArray = env->NewObjectArray(5, env->FindClass("java/lang/String"), NULL);
QAndroidJniObject jniArray = QAndroidJniObject::fromLocalRef(stringArray);
// ...
This would create an array of 5 null strings, and transfer the ownership to QAndroidJniObject if you prefer, or else you'll have to take care of calling DeleteLocalRef().

Related

Read c++ console output from method call using Java

Currently, I am trying to read the console output of my program when there is a method call. How it works is, my Java program calls the JNI wrapper to invoke the C++ method call. My C++ is using std::cout. The payload.invoke will invoke my c++ library API. Inside the c++ API there are a few cout statements. That is the statement I want to read it as a variable in Java.
The current PrintStream only supports out, print, println and printf.Each time when there is a method call, there are bunch of logs being printed out in command prompt. Now, I would like my java program to read those output which my C++ prints out.
This is my current Java code:
public void execute() {
try {
Matcher m = Pattern.compile("([^()]*)[(]([^()]*)[)]").matcher(getPayloadString());
m.find();
boolean passed;
if (m.group(2).isEmpty()) {
// this is where it invoke the method
passed = (boolean) payload.invoke(testcase);
System.out.println("Boolean is: " + passed + "\n");
}else {
passed = (boolean) payload.invoke(testcase, ObjectCreator.convertParameters(m.group(2)));
System.out.println("Boolean2 is: " + passed + "\n");
}
String output = passed ? "PASS" : "FAIL";
// I want to use the console output string variable here -> xxxx
TestCase.printResult(payload.getName(), output, m.group(2), xxxx, "");
} catch (Exception ex) {
ex.printStackTrace();
}
}
Are there any ways to do it? Previously I tried using one of the tutorial from here. but that seems not working. Can someone help me out with this please. Thanks in advance.
If you have full control of the .cpp codebase it is possible for you to change the std::cout to become a ostream& instead.
By referencing a base class you can easily switch the implementation.
Do a JNI-Stream whichh extends the ostream& and just overwrite the operators you need to send JNI wrapped callbacks to java.
Then depending on if you run native or Java style use your JNI-Stream instead of the std::cout.
I would do something as seen in the link below to package and send the data to Java:
JNI - How to callback from C++ or C to Java?
that is my 2 cents.
Read carefully the JNI tutorial.
https://www.baeldung.com/jni
Follow the tutorial.
Basically you need to create a java class with a static block to load the C++ library:
static {
System.loadLibrary("native");
}
Then you can create your own static java method and invoke a method on the library.
If what you need is read the output from a system call, it's a different matter.
This can help you:
Java Runtime.getRuntime(): getting output from executing a command line program

Using JNI to call a Java method from C++ that has non-primitive output

I am trying to call the following Java method (from an external jar file) from C++
Game game = GameLoader.loadGameFromName("My game name");
Where the function signature for loadGameFromName is
public static Game loadGameFromName(String name)
and GameLoader is a Java public final class. Also Game is a public final class.
The code I have used to attempt to call this method is
jclass gameLoader = env->FindClass("player/GameLoader");
jmethodID mid = env->GetStaticMethodID(gameLoader,"loadGameFromName","(Ljava/lang/String;)Lgame/Game");
Here the gameLoader class was successfully found in the first line however the loadGameFromName method was not found in the second line.
Also, it might be relevant that I was able to successfully call a different method from GameLoader which was
String[] games = GameLoader.listGames()
with the following code
jclass gameLoader = env->FindClass("player/GameLoader");
jmethodID mid = env->GetStaticMethodID(gameLoader,"listGames","()[Ljava/lang/String;");
jobjectArray stringArray = (jobjectArray) env->CallStaticObjectMethod(gameLoader,mid);
So that code works fine. So it looks like the main difference here is that loadGameFromName returns a Game object rather than a Java string object. Also, it might be relevant to note that I am able to successfully find the Game class with jclass game = env->FindClass("game/Game");.
Is there something wrong with the way I am calling GetStaticMethodID for the loadGameFromName method? I have tried to search for examples of calling Java methods with JNI that return arbitrary Java objects like Game but I couldn't find any.

How to retrieve string data from parcel in native space?

I have a Native Command which interacts with a System Service in Android Framework through binder using parcel to fetch some data and display it to the user. The data consists of integer and string variables. The data sent from SS is sent using
parcel.writeInt()
and
parcel.writeString()
respectively for integer and string data.
Now, the problem is how do I retrieve this data in native space ( not JNI though ) where my native command handling code is present in the form of a .cpp file? There is no equivalent parcel.readInt() or parcel.readString() methods in native space available to unmarshall or process the received parcel data. If I use the available methods there like parcel.readInt32() and parcel.readString16() methods, the data is incorrect ( probably ) and is not displayed on the screen.
Any insight and possible solution to this problem would be much appreciated.
Thanks & Regards,
You need can create an function in Java that gets a string from the parcel:
private String getDataFromParcel(Parcel parcel) {
return parcel.readString();
}
And call it from JNI (you said you have the parcel in the JNI context):
std::string getString(jobject object, jobject parcel) const {
bool result = false;
JNIEnv *env = JniGetEnv();
jmethod jGetDataFromParcelMethod = getJMethod(object);
jstring javaValue = (jstring) env->CallObjectMethod(object, jGetDataFromParcelMethod, parcel);
...
}

Passing an Enum To a COM Library method using Jacob

I instantiate a COM Object, then invoke a method.
ActiveXComponent comp = new ActiveXComponent("MyDll.MyClass");
String argument1 = "test1";
String argument2 = "test2";
Variant[] arguments = { new Variant(argument1), new Variant(argument2) };
comp.invoke("myMethod", arguments)
Assuming MyDll has a method called
myMethod(String s1, String s2)
it works fine.
Now, what if I have a Method
myMethod(String s1, ReturnDeletedModeEnum enum)
with an enum defined in MyDll?
I need to pass the enum to the method somehow, but I don't know how to access it.
I tried getting the Enum as ActiveXComponent,
new ActiveXComponent("MyDll.ReturnDeletedModeEnum");
which (not surprisingly) didn't work:
com.jacob.com.ComFailException: Can't get object clsid from progid
I tried finding some more documentation about Jacob, because there seem to be Enum-specific classes, but I haven't found any explanation on how to use them.
I ran into the same uncertainty when I needed to call a method with an Enummeration parameter. I couldn't find much documentation - JACOB or otherwise.
I did stumble across a helpful post on the subject which says the values ... correspond to internally stored numbers and An enumeration in VBA is always of data type Long.
Armed with that and the MS Documentation for my particular Enumeration, I gave this a try ...
Dispatch.call(oDocuments, "Open", fileIn, ... , new Variant(1L));
And it worked!
I'm sure there's a way to get actual "Enumeration" data structures, but this was good enough for me.

Windows Mobile Native Code - jstring to LPCTSTR

I have a Java app which needs to interact with the camera on a Windows Mobile device. I have written the Java Code and the Native code and it all works fine. The problem I am having now is that I want to start passing variables from Java to the Native code, e.g. the directory and file name to use for the photo.
The native code uses a SHCAMERACAPTURE object to interact with the camera and it expects the directory and filename to be specified using LPCTSTRs. The string passed in is a jstring, which I can get to a const char * by calling:
const char *strDir=(jEnv)->GetStringUTFChars(dirName, 0);
But I am not sure how I can pass this to the the SHCAMERACAPTURE object because it cannot convert const char * to LPCTSTR. I tried a cast (LPCTSTR)strDir and it compiled, but I get an error when it runs (that it can't create the file).
I am a Java developer and pretty new to C++ etc. so I am really not too sure what I need to do to get my string into the native call. Any ideas?
I think you should try GetStringChars() instead of GetStringUTFChars()
According to this page it returns the Unicode String.
WindowsCE and Windows mobile use UNICODE exclusively so LPCTSTR
is actually LPCWSTR (Long Pointer to Const WideChar String)
SHCAMERACAPTURE shcc;
ZeroMemory(&shcc, sizeof(shcc));
shcc.cbSize = sizeof(shcc);
shcc.pszInitialDir = (TCHAR*)(jEnv)->GetStringChars(dirName, 0 );
shcc.pszDefaultFileName = (TCHAR*)(jEnv)->GetStringChars(defFileName, 0 );
I assume you want to provide a path and a filename. This is adapted from this MS page

Categories