【问题标题】:How to convert String to PWideString in Delphi for JNA consumption如何在 Delphi 中将 String 转换为 PWideString 以供 JNA 使用
【发布时间】:2017-03-22 23:16:27
【问题描述】:

我在Delphi中创建一个DLL,方法是这样的:

function teste3 : PWideString; stdcall;
  var
    _string : string;
  begin
    _string := 'teste';
    Result := PWideString(_string);
  end;

然后我尝试使用 JAVA 中的 JNA lib 调用此方法。 JAVA中调用该方法的代码是这样的:

System.out.println(TDLL.getInstance().teste3());

但是当调用返回的只是字符 t 时,其余的文本不会出现。

如何在不丢失所有字符的情况下将String 转换为PWideString

Obs:我也试过返回 PWideChar 但结果是一样的。

【问题讨论】:

  • 函数一结束,字符串就会超出范围。如果您的目标是传递一个 const 字符串,请声明 const _string : widestring = 'teste'
  • 这只是一个测试,但想象一下我会处理一些东西而不是在方法中返回它。

标签: java string delphi dll jna


【解决方案1】:

Delphi 的PWideString 类型是一个指向WideString 的指针。如果您返回这样一个指向 Java 的指针,Java 将不知道如何处理它。您需要返回一个原始的 PAnsiCharPWideChar(更喜欢后者,因为 Java 字符串是 Unicode),但是您需要处理一个问题 - 内存管理。

如果你返回一个指向静态分配字符串的指针,或者至少是一个比函数寿命更长的动态分配字符串,你可以直接返回一个指向字符数据的指针:

德尔福:

const
  _string : WideString = 'teste'; // or UnicodeString in D2009+

function teste3 : PWideChar; stdcall;
begin
  Result := PWideChar(_string);
end;

或者:

var
  _string : WideString; // or UnicodeString

function teste3 : PWideChar; stdcall;
begin
  _string = 'teste'
  Result := PWideChar(_string);
end;

Java:

public class TDLL extends Library {
  TDLL INSTANCE = (TDLL) Native.loadLibrary("myjnalib.dll", TDLL.class);
  public static TDLL getInstance() {
    return INSTANCE;
  }
  WString teste3();
}

System.out.println(TDLL.getInstance().teste3().toString());

但是,如果您需要动态分配字符串,则必须在同一个 DLL 中释放该内存,否则它将被泄漏。 Java 无法为您释放内存,因为它不知道内存是如何分配的。

要解决这个问题,您应该重新设计 DLL 函数以接受内存缓冲区作为输入,然后您可以在 Java 端使用 JNA 的 Memory 类。让DLL填充内存缓冲区,然后Java可以调用Memory.getString()方法将输出数据读入原生Javastring

德尔福:

function teste3(buffer: PByte; bufsize: Integer) : Integer; stdcall;
var
  _string : WideString; // or UnicodeString
  MaxChars: Integer;
begin
  _string := 'teste';
  MaxChars := (bufsize div sizeof(WideChar)) - 1;
  StrLCopy(PWideChar(buffer), PWideChar(_string), MaxChars);
  Result := Min(Length(_string), MaxChars);
end;

Java:

public class TDLL extends Library {
  TDLL INSTANCE = (TDLL) Native.loadLibrary("myjnalib.dll", TDLL.class);
  public static TDLL getInstance() {
    return INSTANCE;
  }
  int teste3(Memory buf, int bufsize);
}

Memory buf = new Memory(10);
TDLL.getInstance().teste3(buf, buf.size());
System.out.println(buf.getString(0, true));

话虽如此,您可能会考虑改用 JNI(请参阅 Programming JNI with Delphi),因为这样 DLL 可以使用 Java 自己的内存管理器来动态分配并返回实际的 Java string 对象,然后 Java 可以释放它通常根据需要:

德尔福:

function Java_TDLL_test3(env: PJNIEnv; obj: JObject): JString; stdcall;
var
  _string : WideString; // or UnicodeString
begin
  _string := 'teste';
  Result := env^.NewString(env, PJChar(PWideChar(_string)), Length(_string));
end;

Java:

public class TDLL {
  static {
    System.loadLibrary("myjnalib");
  }

  public native string test3();
}

System.out.println(new TDLL().teste3());

【讨论】:

  • 最后一个例子对我来说不是很清楚。在 Delphi 中声明 Java_TDLL_test3 方法后,我将如何在 JAVA 中使用它?在您的示例中,您没有发送 PJNIEnv 和 JObject 参数。
  • @LeonardoGaldioli 这些参数由 Java 本身自动传递。它是native 说明符背后的“魔术”连接的一部分。有大量关于 JNI 如何工作的教程,这超出了这个问题的范围。
  • 所以不使用 JNA,我应该尝试使用 JNI 并使用该库来使用 delphi 中的 java 类型,对吗?如果我有更多参数,我不会出现“无效内存访问”错误?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
相关资源
最近更新 更多