使用Embarcadero的Jni api单元,如何向需要它的JNI方法提供变量参数列表?例如,CallStaticObjectMethodV()方法的JNINativeInterface (清单1)有一个类型为va_list的最后一个参数,它应该封装一个变量列表的参数。在调用此方法的C++代码(清单2)中,方法签名被标记为varargs,这是令人惊讶的,因为Delphi的AndroidApi.Jni单元中没有varargs装饰。
如何在Delphi中构造Args参数以实现相同的功能?我的尝试(如清单3所示)不起作用。
清单1:从单元Androidapi.Jni中提取,稍微适应于Windows (更改后的cdecl用于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;清单2:如何从C++调用它的示例
清单2是从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;
}清单3:我将清单2翻译成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);也不起作用的
我还尝试使用varargs重新声明该方法类型,并使用这些解决方案中概述的方法实现向assember传递的varargs。他们没有工作。(访问违规)。
更多的信息
目标平台是Win32。我复制了一份AndroidApi.jni.pas for windows (WinApi.jni.pas)。我刚给stdcall换了装饰。stdcall是正确的,我可以使用该单元启动JavaVM并执行其他JNI操作。Embaracedero没有将CallStaticObjectMethodV()标记为varargs,但这可能是一个错误?
更新:最终解决方案
多亏了Jonathan Revusky的JNI包装器,我想出了一个解决方案.
有效的代码是..。
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;发布于 2015-10-09 06:37:32
va_list需要指向一个内存块,如果您已经调用了一个变量函数,那么这个内存块将被推到堆栈上。
va_start的通常实现只是产生堆栈上的位置的地址,其中的变量参数是被推送的。
#define va_start(ap, parmN) ((void)((ap) = (va_list)((char _FAR *)(&parmN)+__size(parmN)))) 因此,您试图创建一个包含参数的数组,并使用它,因为您的va_list应该工作。也许你太仓促放弃了?也许不是:
Move( Str1, Data[0], Sz);
Move( Str1, Data[Sz], Sz);你是说
Move( Str1, Data[0], Sz);
Move( Str2, Data[Sz], Sz);尽管就我个人而言,我会选择一个JNIString数组,而不是一个字节数组。
因此,您创建va_list的方法也许不错,但失败是由其他地方的错误造成的。
https://stackoverflow.com/questions/33030132
复制相似问题