1
votes

I'm developing a mobile app using C++ Builder Berlin 10.1 which use a Jar library to scan barcodes. With Java2OP, I obtained a Delphi Bridge File to use the functions of the Jar. I translated it to C++ and include it to my project with the associated Jar. Here are my files:

Delphi:

{*******************************************************}
{                                                       }
{           CodeGear Delphi Runtime Library             }
{ Copyright(c) 2014 Embarcadero Technologies, Inc.      }
{                                                       }
{*******************************************************}

unit Android.JNI.ATScanLib;

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
// ===== Forward declarations =====

  JAsyncTask = interface;//android.os.AsyncTask
  JAsyncTask_Status = interface;//android.os.AsyncTask$Status
  JATScanLib = interface;//com.atid.lib.ATScanLib
  JATScanLib_OnResponseListener = interface;//com.atid.lib.ATScanLib$OnResponseListener
  JATScanLib_TagSequenceTask = interface;//com.atid.lib.ATScanLib$TagSequenceTask
  JVoid = interface;//java.lang.Void

// ===== Interface declarations =====

  JAsyncTaskClass = interface(JObjectClass)
    ['{73C141D6-F8D7-4FE4-BFA3-3441B6367189}']
    {class} function _GetSERIAL_EXECUTOR: JExecutor;
    {class} function _GetTHREAD_POOL_EXECUTOR: JExecutor;
    {class} function init: JAsyncTask; cdecl;
    {class} procedure execute(runnable: JRunnable); cdecl; overload;
    {class} property SERIAL_EXECUTOR: JExecutor read _GetSERIAL_EXECUTOR;
    {class} property THREAD_POOL_EXECUTOR: JExecutor read _GetTHREAD_POOL_EXECUTOR;
  end;

  [JavaSignature('android/os/AsyncTask')]
  JAsyncTask = interface(JObject)
    ['{8BC49850-F199-4620-BCFF-ACDA1D69417A}']
    function cancel(mayInterruptIfRunning: Boolean): Boolean; cdecl;
    function &get: JObject; cdecl; overload;
    function &get(timeout: Int64; unit_: JTimeUnit): JObject; cdecl; overload;
    function getStatus: JAsyncTask_Status; cdecl;
    function isCancelled: Boolean; cdecl;
  end;
  TJAsyncTask = class(TJavaGenericImport<JAsyncTaskClass, JAsyncTask>) end;

  JAsyncTask_StatusClass = interface(JEnumClass)
    ['{16452E24-44D5-4E84-990E-3C1916FB372B}']
    {class} function _GetFINISHED: JAsyncTask_Status;
    {class} function _GetPENDING: JAsyncTask_Status;
    {class} function _GetRUNNING: JAsyncTask_Status;
    {class} function valueOf(name: JString): JAsyncTask_Status; cdecl;
    {class} function values: TJavaObjectArray<JAsyncTask_Status>; cdecl;
    {class} property FINISHED: JAsyncTask_Status read _GetFINISHED;
    {class} property PENDING: JAsyncTask_Status read _GetPENDING;
    {class} property RUNNING: JAsyncTask_Status read _GetRUNNING;
  end;

  [JavaSignature('android/os/AsyncTask$Status')]
  JAsyncTask_Status = interface(JEnum)
    ['{96B0BCE7-1312-49B9-9F33-43541680B0E7}']
  end;
  TJAsyncTask_Status = class(TJavaGenericImport<JAsyncTask_StatusClass, JAsyncTask_Status>) end;

  JATScanLibClass = interface(JObjectClass)
    ['{9A209D1D-07C6-4309-88AD-8A2EF359CF34}']
    {class} function _GetREAD_ENCODING_ERROR: Integer;
    {class} function _GetREAD_ERROR: Integer;
    {class} function _GetREAD_OK: Integer;
    {class} function _GetREAD_TIMEOUT: Integer;
    {class} function init: JATScanLib; cdecl;
    {class} property READ_ENCODING_ERROR: Integer read _GetREAD_ENCODING_ERROR;
    {class} property READ_ERROR: Integer read _GetREAD_ERROR;
    {class} property READ_OK: Integer read _GetREAD_OK;
    {class} property READ_TIMEOUT: Integer read _GetREAD_TIMEOUT;
  end;

  [JavaSignature('com/atid/lib/ATScanLib')]
  JATScanLib = interface(JObject)
    ['{74B37E9E-F88A-4EB0-AEDF-538A472223A0}']
    function Read(P1: TJavaArray<Byte>): Integer; cdecl;
    function getReadResults: JString; cdecl;
    procedure init; cdecl;
    procedure setEncoding(P1: JString); cdecl;
    procedure setOnResponseListener(P1: JATScanLib_OnResponseListener); cdecl;
    procedure start; cdecl;
    procedure stop; cdecl;
  end;
  TJATScanLib = class(TJavaGenericImport<JATScanLibClass, JATScanLib>) end;

  JATScanLib_OnResponseListenerClass = interface(IJavaClass)
    ['{654B87BA-B4EE-4102-85E9-9FDFF3C82BAA}']
  end;

  [JavaSignature('com/atid/lib/ATScanLib$OnResponseListener')]
  JATScanLib_OnResponseListener = interface(IJavaInstance)
    ['{35AC182B-7684-47EA-9BA8-07B504F85943}']
    procedure onResponse(P1: Integer); cdecl;
  end;
  TJATScanLib_OnResponseListener = class(TJavaGenericImport<JATScanLib_OnResponseListenerClass, JATScanLib_OnResponseListener>) end;

  JATScanLib_TagSequenceTaskClass = interface(JAsyncTaskClass)
    ['{1CF84DF6-6115-4FB5-9967-B55E74A3155C}']
  end;

  [JavaSignature('com/atid/lib/ATScanLib$TagSequenceTask')]
  JATScanLib_TagSequenceTask = interface(JAsyncTask)
    ['{B83D8A9F-C1C4-4023-B1D8-3D9ABFB1FD2C}']
  end;
  TJATScanLib_TagSequenceTask = class(TJavaGenericImport<JATScanLib_TagSequenceTaskClass, JATScanLib_TagSequenceTask>) end;

  JVoidClass = interface(JObjectClass)
    ['{E5AB6B2B-2580-469B-BBF6-C226984DFEBE}']
    {class} function _GetTYPE: Jlang_Class;
    {class} property &TYPE: Jlang_Class read _GetTYPE;
  end;

  [JavaSignature('java/lang/Void')]
  JVoid = interface(JObject)
    ['{013CC63A-938C-46BE-ACAC-BA854F2F6AC8}']
  end;
  TJVoid = class(TJavaGenericImport<JVoidClass, JVoid>) end;

implementation

procedure RegisterTypes;
begin
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JAsyncTask', TypeInfo(Android.JNI.ATScanLib.JAsyncTask));
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JAsyncTask_Status', TypeInfo(Android.JNI.ATScanLib.JAsyncTask_Status));
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JATScanLib', TypeInfo(Android.JNI.ATScanLib.JATScanLib));
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JATScanLib_OnResponseListener', TypeInfo(Android.JNI.ATScanLib.JATScanLib_OnResponseListener));
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JATScanLib_TagSequenceTask', TypeInfo(Android.JNI.ATScanLib.JATScanLib_TagSequenceTask));
  TRegTypes.RegisterType('Android.JNI.ATScanLib.JVoid', TypeInfo(Android.JNI.ATScanLib.JVoid));
end;

initialization
  RegisterTypes;
end.

C++:

#ifndef Android_JNI_ATScanLibH
#define Android_JNI_ATScanLibH

#pragma delphiheader begin
#pragma option push
#pragma option -w-      // All warnings off
#pragma option -Vx      // Zero-length empty class member
#pragma pack(push,8)

#include <Androidapi.JNIBridge.hpp>
#include <Androidapi.JNI.JavaTypes.hpp>

#include <System.hpp>
#include <SysInit.hpp>

namespace Android{
namespace Jni{
namespace Atscanlib{

__interface JAsyncTask;
typedef System::DelphiInterface<JAsyncTask> _di_JAsyncTask;

__interface JAsyncTaskClass;
typedef System::DelphiInterface<JAsyncTaskClass> _di_JAsyncTaskClass;

__interface JAsyncTask_Status;
typedef System::DelphiInterface<JAsyncTask_Status> _di_JAsyncTask_Status;

__interface JAsyncTask_StatusClass;
typedef System::DelphiInterface<JAsyncTask_StatusClass> _di_JAsyncTask_StatusClass;

__interface JATScanLib;
typedef System::DelphiInterface<JATScanLib> _di_JATScanLib;

__interface JATScanLibClass;
typedef System::DelphiInterface<JATScanLibClass> _di_JATScanLibClass;

__interface JATScanLib_OnResponseListener;
typedef System::DelphiInterface<JATScanLib_OnResponseListener> _di_JATScanLib_OnResponseListener;

__interface JATScanLib_OnResponseListenerClass;
typedef System::DelphiInterface<JATScanLib_OnResponseListenerClass> _di_JATScanLib_OnResponseListenerClass;

__interface JATScanLib_TagSequenceTask;
typedef System::DelphiInterface<JATScanLib_TagSequenceTask> _di_JATScanLib_TagSequenceTask;

__interface JATScanLib_TagSequenceTaskClass;
typedef System::DelphiInterface<JATScanLib_TagSequenceTask> _di_JATScanLib_TagSequenceTaskClass;

__interface JVoid;
typedef System::DelphiInterface<JVoid> _di_JVoid;

__interface JVoidClass;
typedef System::DelphiInterface<JVoid> _di_JVoidClass;

__interface  INTERFACE_UUID("{73C141D6-F8D7-4FE4-BFA3-3441B6367189}") JAsyncTaskClass  : public Androidapi::Jni::Javatypes::JObjectClass
{
public:
    virtual JExecutor _GetSERIAL_EXECUTOR() = 0;
    virtual JExecutor _GetTHREAD_POOL_EXECUTOR() = 0;
    HIDESBASE virtual _di_JAsyncTask __cdecl init() = 0;
    virtual void __cdecl execute(JRunnable runnable) = 0;

    __property JExecutor SERIAL_EXECUTOR = {read = _GetSERIAL_EXECUTOR};
    __property JExecutor THREAD_POOL_EXECUTOR = {read = _GetTHREAD_POOL_EXECUTOR};

};

__interface  INTERFACE_UUID("{73C141D6-F8D7-4FE4-BFA3-3441B6367189}") JAsyncTask  : public Androidapi::Jni::Javatypes::JObject
{
public:
    virtual bool __cdecl cancel(bool mayInterruptIfRunning) = 0;
    virtual JObject __cdecl get() = 0;
    virtual JObject __cdecl get(__int64 timeout, JTimeUnit unit_) = 0;
    virtual _di_JAsyncTask_Status __cdecl getStatus() = 0;
    virtual bool __cdecl isCancelled() = 0;

};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJAsyncTask : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTaskClass, _di_JAsyncTask>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTaskClass, _di_JAsyncTask> inherited;
    public:
        inline __fastcall TJAsyncTask(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTaskClass, _di_JAsyncTask> (){}
        inline __fastcall virtual ~TJAsyncTask(void){}
};

#pragma pack(pop)


__interface  INTERFACE_UUID("{16452E24-44D5-4E84-990E-3C1916FB372B}") JAsyncTask_StatusClass  : public Androidapi::Jni::Javatypes::JEnumClass
{
public:
    virtual _di_JAsyncTask_Status _GetFINISHED() = 0;
    virtual _di_JAsyncTask_Status _GetPENDING() = 0;
    virtual _di_JAsyncTask_Status _GetRUNNING() = 0;
    virtual _di_JAsyncTask_Status __cdecl valueOf(JString name) = 0;
    virtual TJavaObjectArray__1<_di_JAsyncTask_Status> __cdecl values() = 0;


    __property _di_JAsyncTask_Status FINISHED = {read = _GetFINISHED};
    __property _di_JAsyncTask_Status PENDING = {read = _GetPENDING};
    __property _di_JAsyncTask_Status RUNNING = {read = _GetRUNNING};

};

__interface  INTERFACE_UUID("{96B0BCE7-1312-49B9-9F33-43541680B0E7}") JAsyncTask_Status  : public Androidapi::Jni::Javatypes::JEnum
{
};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJAsyncTask_Status : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTask_StatusClass, _di_JAsyncTask_Status>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTask_StatusClass, _di_JAsyncTask_Status> inherited;
    public:
        inline __fastcall TJAsyncTask_Status(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JAsyncTask_StatusClass, _di_JAsyncTask_Status> (){}
        inline __fastcall virtual ~TJAsyncTask_Status(void){}
};

#pragma pack(pop)

__interface  INTERFACE_UUID("{9A209D1D-07C6-4309-88AD-8A2EF359CF34}") JATScanLibClass  : public Androidapi::Jni::Javatypes::JObjectClass
{
public:
    virtual int _GetREAD_ENCODING_ERROR() = 0;
    virtual int _GetREAD_ERROR() = 0;
    virtual int _GetREAD_OK() = 0;
    virtual int _GetREAD_TIMEOUT() = 0;
    HIDESBASE virtual _di_JATScanLib __cdecl init() = 0;

    __property int READ_ENCODING_ERROR = {read = _GetREAD_ENCODING_ERROR};
    __property int READ_ERROR = {read = _GetREAD_ERROR};
    __property int READ_OK = {read = _GetREAD_OK};
    __property int READ_TIMEOUT = {read = _GetREAD_TIMEOUT};

};

__interface  INTERFACE_UUID("{74B37E9E-F88A-4EB0-AEDF-538A472223A0}") JATScanLib  : public Androidapi::Jni::Javatypes::JObject
{
public:
    virtual int __cdecl Read(TJavaArray__1<Byte> P1) = 0;
    virtual JString __cdecl getReadResults() = 0;
    virtual void __cdecl init() = 0;
    virtual void __cdecl setEncoding(JString P1) = 0;
    virtual void __cdecl setOnResponseListener(_di_JATScanLib_OnResponseListener P1) = 0;
    virtual void __cdecl start() = 0;
    virtual void __cdecl stop() = 0;
};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJATScanLib : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLibClass, _di_JATScanLib>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLibClass, _di_JATScanLib> inherited;
    public:
        inline __fastcall TJATScanLib(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLibClass, _di_JATScanLib> (){}
        inline __fastcall virtual ~TJATScanLib(void){}
};

#pragma pack(pop)

__interface  INTERFACE_UUID("{654B87BA-B4EE-4102-85E9-9FDFF3C82BAA}") JATScanLib_OnResponseListenerClass  : public Androidapi::Jnibridge::IJavaClass
{
};

__interface  INTERFACE_UUID("{35AC182B-7684-47EA-9BA8-07B504F85943}") JATScanLib_OnResponseListener  : public Androidapi::Jnibridge::IJavaInstance
{
public:
    virtual void __cdecl onResponse(int P1) = 0;
};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJATScanLib_OnResponseListener : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_OnResponseListenerClass, _di_JATScanLib_OnResponseListener>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_OnResponseListenerClass, _di_JATScanLib_OnResponseListener> inherited;
    public:
        inline __fastcall TJATScanLib_OnResponseListener(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_OnResponseListenerClass, _di_JATScanLib_OnResponseListener> (){}
        inline __fastcall virtual ~TJATScanLib_OnResponseListener(void){}
};

#pragma pack(pop)

__interface  INTERFACE_UUID("{1CF84DF6-6115-4FB5-9967-B55E74A3155C}") JATScanLib_TagSequenceTaskClass  : public _di_JAsyncTaskClass
{
};

__interface  INTERFACE_UUID("{B83D8A9F-C1C4-4023-B1D8-3D9ABFB1FD2C}") JATScanLib_TagSequenceTask  : public _di_JAsyncTask
{
};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJATScanLib_TagSequenceTask : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_TagSequenceTaskClass, _di_JATScanLib_TagSequenceTask>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_TagSequenceTaskClass, _di_JATScanLib_TagSequenceTask> inherited;
    public:
        inline __fastcall TJATScanLib_TagSequenceTask(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JATScanLib_TagSequenceTaskClass, _di_JATScanLib_TagSequenceTask> (){}
        inline __fastcall virtual ~TJATScanLib_TagSequenceTask(void){}
};

#pragma pack(pop)

__interface  INTERFACE_UUID("{E5AB6B2B-2580-469B-BBF6-C226984DFEBE}") JVoidClass : public Androidapi::Jni::Javatypes::JObjectClass
{
public:
    virtual Jlang_Class _GetTYPE() = 0;
    __property Jlang_Class TYPE = {read = _GetTYPE};

};

__interface  INTERFACE_UUID("{013CC63A-938C-46BE-ACAC-BA854F2F6AC8}") JVoid  : public Androidapi::Jni::Javatypes::JObject
{
};
#pragma pack(push,4)

class PASCALIMPLEMENTATION TJVoid : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JVoidClass, _di_JVoid>{
    typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JVoidClass, _di_JVoid> inherited;
    public:
        inline __fastcall TJVoid(void) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JVoidClass, _di_JVoid> (){}
        inline __fastcall virtual ~TJVoid(void){}
};
#pragma pack(pop)
}
}
}
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID_JNI_ATLIB)
    using namespace Android::Jni::Atscanlib;
#endif

#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID_JNI)
    using namespace Android::Jni;
#endif

#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID)
    using namespace Android;
#endif

#pragma pack(pop)
#pragma option pop

#pragma delphiheader end.
#endif

When I tried to use the functions like this :

TJATScanLib* scan = new TJATScanLib();
_di_JATScanLibClass scanner = TJATScanLib::JavaClass->init();

C++ Builder raised this link error :

[ldandroid Erreur] C:\Users\Public\Documents\Embarcadero\Studio\18.0\CatalogRepository\AndroidNDK-9c_x86_GIB.Build.22858.6822\toolchains\arm-linux-androideabi-4.6\prebuilt\windows\bin\arm-linux-androideabi-ld.exe: .\Android\Debug\Connexion.o: in function TForm_Connexion::TForm_Connexion(System::Classes::TComponent*):C:\Users\dylan\Documents\Embarcadero\Studio\Projets\PDA_Android/Connexion.cpp:28:error:

undefined reference to 'Androidapi::Jnibridge::TJavaGenericImport__2, System::DelphiInterface >::GetJavaClass()'

and

[ldandroid Erreur] C:\Users\Public\Documents\Embarcadero\Studio\18.0\CatalogRepository\AndroidNDK-9c_x86_GIB.Build.22858.6822\toolchains\arm-linux-androideabi-4.6\prebuilt\windows\bin\arm-linux-androideabi-ld.exe: .\Android\Debug\Connexion.o: in function TForm_Connexion::TForm_Connexion(System::Classes::TComponent*):C:\Users\dylan\Documents\Embarcadero\Studio\Projets\PDA_Android/Connexion.cpp:30:error:

undefined reference to 'vtable for Android::Jni::Atscanlib::TJATScanLib'

Did I use the function in the wrong way?

1
There should be a C implementation available doing just thatuser2249683
You do not need to translate the bridge file manually at all. C++Builder includes a Delphi compiler, so you can use .pas files as-is in C++ projects. When a .pas file is compiled in a C++ project, it will generate a suitable .hpp file that you can then #include in your C++ code.Remy Lebeau
@RemyLebeau When I tried to compile the .pas file, it raised an error : Compiler for "Delphi.Personality" and the platform "Android" is missing or unavailable" So on an another computer I installed the trial version of Rad Studio 10.1 Belin with Delphi and Delphi Android. Like you said, the compilation generated a .hpp file but when I wanted to include it in C++Builder without Delphi, I had the same error of undefined reference as in my question.Dylan
Like most things in C++, having a header file is not enough. It just contains declarations, but you need to link to the compiled file that contains the implementation code for the declarations. That is what you keep missing. A .pas file compiles to a .dcu file, which you need to link into your app. When a .pas file is in a C++ project, that linkage is automatic. If you compile the .pas separately, you need to setup the linkage manually. As for the GetJavaClass() error, that suggests you are not linking to the RTL package that implements the JNI bridge framework itself.Remy Lebeau
You should not need to install the Delphi personality in order to compile Delphi files in C++ projects. C++Builder has always been able to compile Delphi code on its own. If you can't do it in the IDE anymore, the command-line compiler should still be available.Remy Lebeau

1 Answers

0
votes

I had a response from Embarcadero. I had some files which are missing from the IDE. Here are the steps to follow:

  • In C++ Builder, go to Project/Options/Delphi Compiler/Search path
  • Enter "$(BDSLIB)\$(PLATFORM)\Release;$(BDSLIB)\$(PLATFORM)\Debug" (without the quotes)
  • Save the project and THEN try the command line build (MSBuild /v:diag XXXX.cbproj)

If it still doesn't work, the Support sent me some bpl, jdgb and de files to copy in the Bin directory and two register to add. Unfortunately, I can't attach these files to a post. If you want these files, give me your mail address and I'll send them to you. If you have the files, here are the other steps:

  • Unzip the files(Link to this post) to your \Bin directory
  • Shut the IDE
  • run regedit
  • Go to : HKEY_CURRENT_USER\Software\Embarcadero\BDS\18.0\Known IDE Packages
  • add a string value with the name "$(BDS)\Bin\delphiandroid32240.bpl" and value "Delphi Android Platform" (both without the quotes)
  • Then go to: "HKEY_CURRENT_USER\Software\Embarcadero\BDS\18.0\Known IDE Packages\Delphi"
  • Add "$(BDS)\Bin\delphiandroidservices240.bpl" and value of "Delphi Android Services"
  • Start the IDE, you should now have the Delphi android support in the IDE so project should hopefully compile in IDE.

If it compiles but doesn't generate the HPP file:

  • Go to Project/Options/Delphi Compiler/ C/C++ output
  • Select "Generate C++ .objs, headers, namespaces, export"

I hope it will help others who are in my case.