【问题标题】:Casting a Delphi interface to TObject* in C++ Builder and proper ClassName() function call在 C++ Builder 中将 Delphi 接口转换为 TObject* 和正确的 ClassName() 函数调用
【发布时间】:2015-11-16 10:52:25
【问题描述】:

我正在尝试使用 C++ Builder(Android 开发)从 *.apk 文件动态访问 Java 类。为此,我在 Delphi 中找到了完整的示例指南(此处:http://www.pclviewer.com/android/androidJNI.html)。在 Delphi 中一切正常,但是当我尝试使用 C++ Builder XE7 将 Pascal 源代码(在指南末尾)重写为 C++ 时,我发现了一个奇怪的问题。如何在 C++ 中将 Delphi 接口转换为 TObject*,正确调用 ClassName() 函数并获取 JavaObjectID?花了很多时间,还是无法重写这3行代码:

var
JavaObject:JObject;
oTemp:TObject;
JavaObjectID:JNIObject;
:
oTemp:=JavaObject as TObject;                   //1. ?
JavaObjectID:=tjavaimport(otemp).GetObjectID;   //2. ?
memo1.Lines.Add(oTemp.ClassName);               //3. ? 
:

我尝试过不同的类型转换,也使用 TObject 类的 GetInterface() 方法,但没有任何效果。作为一名 C++ 程序员,我不明白如何将 delphi 接口直接转换为 TObject*。

Androidapi::Jni::Javatypes::_di_JObject JavaObject;
System::TObject* oTemp; 
Androidapi::Jni::_JNIObject *JavaObjectID;  
:
oTemp = (TObject*)(&JavaObject);   //Compiling ok, but segmentation fault 
?
Memo1->Lines->Add(oTemp->ClassName()); // after ClassName() invoke.

有人知道吗。请问如何将这 3 行 Pascal 代码重写为 C++?非常感谢您的回复。

【问题讨论】:

  • 我的问题是你为什么要将接口投射到TObject。那不是你不应该做的事情吗?
  • 这种方式在 Delphi 示例 (pclviewer.com/android/androidJNI.html) 中使用并且功能齐全。我试图将代码逐行重写到 C++ Builder 项目中,但发现 C++ 没有接口。 Delphi 接口被 C++ Builder 中具有纯虚函数的抽象类所模仿。可能可以通过不同的方式重写提到的代码,或者直接在 C++ Builder 中使用 Delphi 生成的目标代码以及生成的 *.hpp 头文件,但是感觉,我不懂基础,无法从 Pascal 重写 3 行我不开心。
  • 坚持接口会更有意义。完全用接口解决问题,停止尝试强制转换为实现对象。完全违背界面精神的东西。
  • 也许你是对的。我会尝试不同的方式。即使仍然不明白如何在 Pascal 中编写“oTemp:=JavaObject as TObject”,然后正确获取返回“TJavaImport”的 ClassName。这两个实体在 Pascal 中也是完全不同的。它们唯一的共同点是,它们都支持 IUnknown 接口。常见的“ClassName”从何而来对我来说是个谜。
  • ClassName 是TObject 的一个方法。 Delphi 接口可以由类的实例实现,as 强制转换是检索该实现实例的一种方式。当然,有些接口是外部的,不是由 Delphi 对象实现的。对他们来说,as 演员将失败。

标签: java delphi c++builder


【解决方案1】:

将接口转换为对象引用是 Delphi 独有的功能,在 C++ 中没有直接钻研 vtable 的等价物。但是,您可以做的是在 Delphi 中编写一个函数,该函数将接口作为输入,对其进行转换,然后将对象作为输出返回,然后在 C++ 代码中调用该函数。你可以在C++Builder项目中添加一个Delphi.pas文件,编译后会生成一个C++.hpp文件。

unit MyJavaHelper;

interface

uses
  Androidapi.JNI.JavaTypes;

function JObjectToTObject(JavaObject: JObject): TObject;

implementation

function JObjectToTObject(JavaObject: JObject): TObject;
begin
  Result := JavaObject as TObject;
end;

end.
#include "MyJavaHelper.hpp"

Androidapi::Jni::Javatypes::_di_JObject JavaObject;
System::TObject* oTemp; 
Androidapi::Jni::_JNIObject *JavaObjectID;  

JavaObject = ...;
oTemp = JObjectToTObject(JavaObject);
JavaObjectID = static_cast<TJavaImport*>(otemp)->GetObjectID();   
Memo1->Lines->Add(oTemp->ClassName());

【讨论】:

  • 很好的回答,非常感谢。它完全阐明了我的问题。
  • 你几乎肯定不应该这样做。
  • @DavidHeffernan:为什么?虽然ClassName() 不是特别有用,但在一般的C++ 项目中使用Delphi 代码是完全有效的。在使用 Embarcadero 的 Android JNI Bridge 框架访问 Android API 时,您需要访问 Java ObjectID 才能访问 JNI 的一些功能。如果您实际查看有问题的示例代码,它使用 ObjectID 调用 TJNIResolver.GetJavaMethodID() 以获取对 Java getIntValue() 对象方法的引用,然后使用 JavaEnv^.CallIntMethod() 使用 ObjectID 调用该方法。跨度>
  • @DavidHeffernan:总的来说,是的。但这不是一般情况。这是一个非常特殊的情况,即直接调用外部包装 API (JNI),并将 ID 作为输入而不是接口指针。并非 Java 中的所有内容都通过漂亮干净的接口公开,有时您会更深入地了解。想想 JNI,或者更具体地说是 JNI 的反射模型(示例代码正在使用),类似于 Delphi 的 RTTI 模型。可以通过名称发现事物,获取它们的 ID,然后通过它们的 ID 和相关参数调用它们。
  • 这种方法非常有用,如果您需要将 Java 库与现有的 Pascal 或 C++ 代码混合使用。就我而言,我只需要从 *.apk 包中调用几个 Java 函数。它包含第 3 方通信功能。然而,整个 Android 应用程序将在 C++ Builder 中创建,这对我来说很舒服。我可以像 Windows 操作系统中的 DLL 函数一样调用 *.apk 包中的 Java 函数。
猜你喜欢
  • 1970-01-01
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2015-07-28
  • 2013-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多