如何调用使用JNA返回字符串的Delphi函数? [英] How can I call a Delphi function that returns a string using JNA?

查看:259
本文介绍了如何调用使用JNA返回字符串的Delphi函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在从Delphi编译的* .so文件中调用Java程序中的函数。经过一番研究,似乎 JNA 是他要走的路。在潜入一些复杂的Delphi代码之前,我试图玩一些Hello World代码,但是遇到一个Delphi函数返回的字符串的麻烦。



Delphi代码(helloworld.pp):

 库HelloWorldLib; 

函数HelloWorld(const myString:string):string;标准
begin
WriteLn(myString);
结果:= myString;
结束

输出HelloWorld;

begin
end。

我从命令行使用 fpc -Mdelphi helloworld.pp ,其生成 libhelloworld.so



现在我的Java类:

  import com.sun.jna.Library; 
import com.sun.jna.Native;

public class HelloWorld {
public interface HelloWorldLibrary extends Library {
HelloWorldLibrary INSTANCE =(HelloWorldLibrary)Native.loadLibrary(/ full / path / to / libhelloworld.so,HelloWorldLibrary 。类);

String HelloWorld(String test);
}

public static void main(String [] args){
System.out.println(HelloWorldLibrary.INSTANCE.HelloWorld(QWERTYUIOP));
}
}

但是当我运行这个Java代码时,我得到: / p>

 #Java Runtime Environment检测到致命错误:

#SIGSEGV(0xb)在pc = 0x00007f810318add2,pid = 4088,tid = 140192489072384

#JRE版本:7.0_10-b18
#Java VM:Java HotSpot(TM)64位服务器虚拟机(23.6- b04混合模式linux-amd64压缩oops)
#问题框架:
#C [libhelloworld.so + 0xbdd2] HelloWorld + 0x6fea

请注意,如果我更改了我的Delphi方法(和相关的Java接口)以返回一个硬编码的整数,一切都很好:我传递的字符串被打印,我将int返回为预期的。



奇怪的是,如果Delphi方法返回一个字符,我必须写我的JNA代理返回一个字节,并将其转换为char(如果我声明我的界面返回一个字符打印出一个垃圾字符)。



在这里?



FYI,我在Ubuntu 12.04,64位,使用Sun JDK 1.7.0_10-b18,JNA 3.5.1和Free Pascal编译器版本2.4.4 -3.1。

解决方案

一个Delphi或FreePascal string 是一个托管类型不能用作JNA类型。 JNA文档解释说,Java String 被映射到指向8位字符的以null结尾的数组的指针。在Delphi的术语中, PAnsiChar



所以你可以将你的Pascal代码中的输入参数从 string to PAnsiChar



返回值更有问题。您将需要决定谁分配内存。无论谁分配它也必须释放它。



如果本地代码负责分配,那么您需要堆分配空终止的字符串。并返回一个指针。您还需要导出一个deallocator,以便Java代码可以要求本地代码释放分配的内存块。



通常分配更方便Java代码中的缓冲区。然后将其传递给本地代码,并让其填写缓冲区的内容。这个Stack Overflow问题说明了使用Windows API函数 GetWindowText 作为示例的技术:如何使用JNI或JNA阅读窗口标题?



使用Pascal的示例将如下所示:

  function GetText(Text:PAnsiChar; Len:Integer):Integer;标准
const
S:AnsiString ='一些文本值';
begin
Result:= Length(S)+1; // include null-terminator
如果Len> 0则
StrPLCopy(Text,S,Len-1);
结束

在Java方面,我猜这个代码看起来像这样,记住我绝对知道没有关于Java。

  public interface MyLib extends StdCallLibrary {
MyLib INSTANCE =(MyLib)Native.loadLibrary(MyLib ,MyLib.class);
int GetText(byte [] lpText,int len);
}

....

int len = User32.INSTANCE.GetText(null);
byte [] arr = new byte [len];
User32.INSTANCE.GetText(arr,len);
String Text = Native.toString(arr);


I'm working on calling functions from a Delphi compiled *.so file from a Java program. After some research it seems like JNA is he way to go. Before diving into some complex Delphi code, I'm trying to play with some "Hello World" code but am having trouble getting a string returned by a Delphi function.

The Delphi code (helloworld.pp):

library HelloWorldLib;

function HelloWorld(const myString: string): string; stdcall;
begin
  WriteLn(myString);
  Result := myString;
end;

exports HelloWorld;

begin
end.

I compile it from the command line with "fpc -Mdelphi helloworld.pp", which produces libhelloworld.so.

Now my Java class:

import com.sun.jna.Library;
import com.sun.jna.Native;

public class HelloWorld {
    public interface HelloWorldLibrary extends Library {
        HelloWorldLibrary INSTANCE = (HelloWorldLibrary) Native.loadLibrary("/full/path/to/libhelloworld.so", HelloWorldLibrary.class);

        String HelloWorld(String test);
    }

    public static void main(String[] args) {
        System.out.println(HelloWorldLibrary.INSTANCE.HelloWorld("QWERTYUIOP"));
    }
}

However when I run this Java code I get:

# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00007f810318add2, pid=4088, tid=140192489072384
#
# JRE version: 7.0_10-b18
# Java VM: Java HotSpot(TM) 64-Bit Server VM (23.6-b04 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C  [libhelloworld.so+0xbdd2]  HelloWorld+0x6fea

Note that if I change my Delphi method (and the associated Java interface) to return a hardcoded integer, everything works great: the string I pass gets printed and I get the int back as expected.

Strangely enough, if the Delphi method returns a char, I have to write my JNA proxy as returning a byte and cast it to char manually (if I declare my interface as returning a char it prints out a garbage character).

Any idea what is going wrong here?

FYI, I'm on Ubuntu 12.04, 64bits, using Sun JDK 1.7.0_10-b18, JNA 3.5.1 and Free Pascal Compiler version 2.4.4-3.1.

解决方案

A Delphi or FreePascal string is a managed type that cannot be used as a JNA type. The JNA documentation explains that Java String is mapped to a pointer to a null-terminated array of 8 bit characters. In Delphi terms that is PAnsiChar.

So you can change the input parameter in your Pascal code from string to PAnsiChar.

The return value is more problematic. You will need to decide who allocates the memory. And whoever allocates it must also free it.

If the native code is responsible for allocating it then you'd need to heap allocate the null-terminated string. And return a pointer to it. You'd also need to export a deallocator so that the Java code can ask the native code to deallocate the heap allocated block of memory.

It is usually more convenient to allocate a buffer in the Java code. Then pass that to the native code and let it fill out the content of the buffer. This Stack Overflow question illustrates the technique, using the Windows API function GetWindowText as its example: How can I read the window title with JNI or JNA?

An example of this using Pascal would be like so:

function GetText(Text: PAnsiChar; Len: Integer): Integer; stdcall;
const
  S: AnsiString = 'Some text value';
begin
  Result := Length(S)+1;//include null-terminator
  if Len>0 then
    StrPLCopy(Text, S, Len-1);
end;

On the Java side, I guess the code would look like this, bearing in mind that I know absolutely nothing about Java.

public interface MyLib extends StdCallLibrary {
    MyLib INSTANCE = (MyLib) Native.loadLibrary("MyLib", MyLib.class);
    int GetText(byte[] lpText, int len);
}

....

int len = User32.INSTANCE.GetText(null);
byte[] arr = new byte[len];
User32.INSTANCE.GetText(arr, len);
String Text = Native.toString(arr);

这篇关于如何调用使用JNA返回字符串的Delphi函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆