O Delphi chama métodos JNI com lista de argumentos variáveis
Usando a unidade api Jni da Embarcadero, como um argumento variável é fornecido para os métodos JNI que o exigem? Por exemplo, o método CallStaticObjectMethodV () do JNINativeInterface (listagem 1) possui um último parâmetro do tipo va_list, que deve encapsular uma lista variável de argumentos. No código C ++ (listagem 2) que chama esse método, a assinatura do método é marcada como varargs, o que é surpreendente, porque não há decoração de varargs na unidade AndroidApi.Jni do Delphi.
Como você deve construir o parâmetro Args para obter a mesma coisa no Delphi? Minha tentativa, mostrada na listagem 3, não funciona.
Listagem 1: Extrair da unidade Androidapi.Jni, ligeiramente adaptado para a plataforma Windows (alterado cdecl para 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;
Listagem 2: Um exemplo de como é chamado em C ++A Listagem 2 foi extraída da biblioteca Saxon / C.
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;
}
Listagem 3: Minha tentativa de traduzir a listagem 2 em Delphivar
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);
O que também não funcionouTambém tentei redeclarar o tipo de método a ser decorado com varargs e implementar os varargs que passam com o mês de dezembro, usando o método descrito nessas soluções. Eles não funcionaram. (Violação de acesso).
Como uma função com 'varargs' recupera o conteúdo da pilha?Delphi "array de const" para "varargs"Um pouco mais de informaçãoA plataforma de destino é o Win32. Fiz uma cópia do AndroidApi.jni.pas para Windows (WinApi.jni.pas
) Acabei de alterar as decorações do cdecl para stdcall. O stdcall está correto e posso usar a unidade para iniciar o JavaVM e executar outras tarefas JNI. Embaracedero não marca CallStaticObjectMethodV () como varargs, mas talvez isso seja um erro?
Graças aWrapper JNI de Jonathan Revusky, Elaborei uma solução funcional ...
O código que funciona é ..
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;