Using Embarcadero's Jni api unit, how does one supply variable argument lists to the JNI methods that require it? For example, the CallStaticObjectMethodV() method of the JNINativeInterface (listing 1) has a last parameter of type va_list, which is supposed to encapsulate a variable list of arguments. In C++ code (listing 2) which calls this method, the method signature is marked as varargs, which is surprising because there is no varargs decoration in Delphi's AndroidApi.Jni unit.
How are you supposed to construct the Args parameter to achieve the same thing in Delphi? My attempt, shown in listing 3 does not work.
Listing 1: Extract from unit Androidapi.Jni, slightly adapted for Windows platform (changed cdecl for stdcall)
JNINativeInterface = packed record
...
CallStaticObjectMethod : function(Env: PJNIEnv; AClass: JNIClass; MethodID: JNIMethodID): JNIObject; stdcall;
CallStaticObjectMethodV: function(Env: PJNIEnv; AClass: JNIClass; MethodID: JNIMethodID; Args: va_list ): JNIObject; stdcall;
CallStaticObjectMethodA: function(Env: PJNIEnv; AClass: JNIClass; MethodID: JNIMethodID; Args: PJNIValue): JNIObject; stdcall;
Listing 2: An example of how it is called from C++
Listing 2 was extracted from the Saxon/C library.
XdmValue * SaxonProcessor::parseFile(const char* source){
jmethodID mID = (jmethodID)env->GetStaticMethodID(saxonCAPIClass, "xmlParseFile", "(Lnet/sf/saxon/s9api/Processor;Ljava/lang/String;Ljava/lang/String;)Lnet/sf/saxon/s9api/XdmNode;");
if (!mID) {
cerr<<"\nError: MyClassInDll "<<"xmlParseFile()"<<" not found"<<endl;
return NULL;
}
jobject xdmNodei = env->CallStaticObjectMethod(saxonCAPIClass, mID, proc, env->NewStringUTF(cwd.c_str()), env->NewStringUTF(source));
if(exceptionOccurred()) {
exception= checkForException(env, saxonCAPIClass, NULL);
} else {
XdmValue * value = new XdmValue(xdmNodei);
value->setProcessor(this);
return value;
}
return NULL;
}
Listing 3: My attempt at translating listing 2 into Delphi
var
mID: JNIMethodID;
xdmNodei: JNIObject;
Str1, Str2: JNIString;
Hold1, Hold2: TBytes;
ArgsAsList: va_list;
Data: TBytes;
Sz: integer;
begin
mID := FJNIEnv.GetStaticMethodID( Fpenv, FsaxonCAPIClass, 'xmlParseFile',
'(Lnet/sf/saxon/s9api/Processor;Ljava/lang/String;Ljava/lang/String;)Lnet/sf/saxon/s9api/XdmNode;');
Str1 := FJNIEnv.NewStringUTF( Fpenv, String_to_MarshaledAString( Fcwd , Hold1));
Str2 := FJNIEnv.NewStringUTF( Fpenv, String_to_MarshaledAString( Source, Hold2));
Sz := SizeOf( JNIString);
SetLength( Data, 3 * Sz);
FillChar( Data[0], Length( Data), 0);
Move( Str1, Data[0], Sz);
Move( Str1, Data[Sz], Sz);
ArgsAsList := va_list( @Data[0]);
xdmNodei := FJNIEnv.CallStaticObjectMethodV( Fpenv, FsaxonCAPIClass, mID, ArgsAsList);
What also hasn't worked
I have also tried redeclaring the method type to be decorated with varargs, and implementing the varargs passing with assember, using the method outlined in these solutions. They did not work. (Access violation).
- How can a function with 'varargs' retrieve the contents of the stack?
- Delphi "array of const" to "varargs"
A bit more information
The target platform is Win32. I made a copy of AndroidApi.jni.pas for windows (WinApi.jni.pas
). I just changed the cdecl decorations for stdcall. stdcall is correct, and I can use the unit to start the JavaVM and do other JNI stuff. Embaracedero does not mark the CallStaticObjectMethodV() as varargs, but maybe this is an error?
Update: The final solution
Thanks to Jonathan Revusky's JNI Wrapper, I worked out a working solution ...
The code that works is ..
function TSaxonProcessor.parseFile( const Source: string): TXdmValue;
var
mID: JNIMethodID;
xdmNodei: JNIObject;
Str1, Str2: JNIString;
Hold1, Hold2: TBytes;
Data: TArray<JNIString>;
begin
mID := FJNIEnv.GetStaticMethodID( Fpenv, FsaxonCAPIClass, 'xmlParseFile',
'(Lnet/sf/saxon/s9api/Processor;Ljava/lang/String;Ljava/lang/String;)Lnet/sf/saxon/s9api/XdmNode;');
Str1 := FJNIEnv.NewStringUTF( Fpenv, String_to_MarshaledAString( Fcwd , Hold1));
Str2 := FJNIEnv.NewStringUTF( Fpenv, String_to_MarshaledAString( Source, Hold2));
SetLength( Data, 3);
Data[0] := FProc;
Data[1] := Str1;
Data[2] := Str2;
xdmNodei := FJNIEnv.CallStaticObjectMethodV( Fpenv, FsaxonCAPIClass, mID, @Data[0]);
end;