Delphi使用变量参数列表调用JNI方法

时间:2015-10-09 04:51:23

标签: delphi java-native-interface

使用Embarcadero的Jni api单元,如何为需要它的JNI方法提供变量参数列表?例如,JNINativeInterface(清单1)的CallStaticObjectMethodV()方法具有类型为va_list的最后一个参数,该参数应该封装变量参数列表。在调用此方法的C ++代码(清单2)中,方法签名被标记为varargs,这是令人惊讶的,因为Delphi的AndroidApi.Jni单元中没有varargs装饰。

如何在Delphi中构建Args参数以实现相同的功能?我在列表3中显示的尝试不起作用。

清单1:从单元Androidapi.Jni中提取,稍微适用于Windows平台(更改为stdcall的cdecl)

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。我为Windows(WinApi.jni.pas)制作了AndroidApi.jni.pas的副本。我刚刚更改了stdcall的cdecl装饰。 stdcall是正确的,我可以使用该单元启动JavaVM并执行其他JNI操作。 Embaracedero没有将CallStaticObjectMethodV()标记为varargs,但可能这是一个错误?

更新:最终解决方案

感谢Jonathan Revusky's JNI Wrapper,我制定了一个有效的解决方案......

有效的代码是..

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;

1 个答案:

答案 0 :(得分:0)

x = {}; x.cyclic = x;需要指向一个内存块,如果您调用了一个可变参数函数,它将与推入堆栈的内存相匹配。

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的方法很好,但失败是由其他地方的错误引起的。