困难与德尔福直接调用一个Android NDK功能困难、直接调用、功能、德尔福

2023-09-04 09:10:28 作者:骄傲做自己

这是可以调用一个Android德尔福通过JNI和NDK 的。要实现这是一个相当大量的工作,有人建议直接调用NDK-功能。为此我创建了一个小例子文件以及我发现,在Delphi源$ C ​​$ c中的行声明外部C函数。更具体的在<路径德尔福> \来源\ RTL \机器人

It is possible to call an Android C-function from Delphi via JNI and NDK. To implement this is quite a lot of work and it was suggested to call the NDK-functions directly. To that effect I created a small example file to declare an external C function along the lines I found in the Delphi source code. More specific in <path to delphi>\source\rtl\android.

我创建了一个非常小的testprogram测试直接从德尔福调用C函数的功能。所有的源$ C ​​$ C,你会发现下面的,这是我目前正在测试。

I created a very small testprogram to test the functionality of calling a C-function directly from Delphi. All source code you'll find below, this is what I am currently testing.

  unit DLL_external;

  interface

  const
     MIDI_Lib = '/usr/lib/libmiditest.so';
     test_fun = 'test_1';

  function test_1 (n: Integer): Integer; cdecl;
    external MIDI_Lib name test_fun;

  implementation

  initialization

  finalization

  end.

的初始化和结束是必要的,因为其他人连接错误时提到一些失踪的初始化和结束code。调用的类:

The initialization and finalization are necessary because else linking errors occur referring to some missing initialization and finalization code. The calling class:

  unit DLL_Test_Main;

  interface

  uses
    System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
    FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
    DLL_external;

  //{$I Androidapi.inc}

  type
     TForm1 = class(TForm)
      Button_Load: TButton;
      Label1: TLabel;

      procedure Button_LoadClick (Sender: TObject);
      procedure FormCreate(Sender: TObject);

     public
        procedure call_external_function (value: Integer);
     end; // Class: TForm1 //

  var
     Form1: TForm1;

  implementation

  {$R *.fmx}

  procedure TForm1.FormCreate (Sender: TObject);
  begin
     Label1.Text := 'External function not called yet';
  end; // FormCreate //

  procedure TForm1.Button_LoadClick (Sender: TObject);
  begin
     call_external_function (3);
  end; // Button_LoadClick //

  procedure TForm1.call_external_function (value: integer);
  var n: Int32;
  begin
     n := test_1 (value);
     Label1.Text := Format ('%d = test_1 (%d)', [n, value]);
  end; // call_external_function //

  end.

再加上本机库 miditest 。这是用 NDK建造建。由此产生的库 libmiditest.so 复制到 C:\用户\公用\文档\的RAD Studio \ 12.0 \ PlatformSDKs \的Andr​​oid NDK,R8E \平台\ Android为14 \弓臂的\ usr \ LIB 因为这是德尔福已经把自己的图书馆的地方。

Together with a native library miditest. This was built using ndk-build. The resulting library libmiditest.so was copied to C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\android-ndk-r8e\platforms\android-14\arch-arm\usr\lib as this is the place where Delphi has placed its own libraries.

  #include <jni.h>

  int test_1 (int n) // little test for callability
  {
     return n * n;
  }

当我做了一个 NDK建造文件 libmiditest.so 产生在子目录库\ armeabi-V7A 。我复制该文件到&LT;路径的NDK目录&GT; \平台\ Android为14 \弓臂的\ usr \ LIB 。正如我在一开始的一些链接错误(错误的名称和那种愚蠢的错误)我用 readelf -AWs libmiditest.so 来产生一个符号列表和预期的架构图书馆。该名称 TEST_1 是在符号列表的是手臂V7架构(我用的Nexus 7进行测试)。当我运行Delphi程序立即崩溃在Android上:不幸的是,DLL_Test_Propject已经停止。检查亚行输出(见下文)看来,该文件 libDLL_Test_Project.so 的预期。我更换了单位 libmiditest.so DLL_external由 libDLL_Test_Project.so 并复制的/ usr / LIB / libmiditest.so /usr/lib/libDLL_Test_Project.so 。这并没有帮助。

When I do an ndk-build a file libmiditest.so is produced in the subdirectory libs\armeabi-v7a. I copied this file to <path to your ndk directory>\platforms\android-14\arch-arm\usr\lib. As I had some linking errors in the beginning (wrong names and that kind of stupid errors) I used readelf -AWs libmiditest.so to produce a symbol list and the expected architecture of the library. The name test_1 was in the symbol list as was the arm v7 architecture (I use a Nexus 7 for testing). When I run the Delphi program it crashes immediately on Android: "Unfortunately, DLL_Test_Propject has stopped". Examining the adb output (see below) it appears that the file libDLL_Test_Project.so is expected. I replaced the libmiditest.so in unit DLL_external by libDLL_Test_Project.so and copied /usr/lib/libmiditest.so to /usr/lib/libDLL_Test_Project.so. That did not help.

没有人明白为什么德尔福生成的应用程序尝试加载其自身的图书馆吗?而更好的:任何建议,我应该怎么通过德尔福叫Android的C函数

Does anyone understand why the Delphi generated app tries to load a library of itself? And better: any suggestion how I should call an Android C-function via Delphi?

  I/InputReader(  608): Reconfiguring input devices.  changes=0x00000010
  D/dalvikvm(  799): GC_FOR_ALLOC freed 2003K, 15% free 14582K/16964K, paused 29ms, total 29ms
  I/PCKeyboard(  799): Loaded dictionary, len=841005
  I/HK/LatinKeyboardBaseView(  799): onMeasure width=1200
  I/HK/LatinKeyboardBaseView(  799): onMeasure width=1200
  D/Documents( 3358): Used cached roots for com.android.providers.downloads.documents
  D/Documents( 3358): Used cached roots for com.android.externalstorage.documents
  D/Documents( 3358): Used cached roots for com.android.providers.media.documents
  D/Documents( 3358): Used cached roots for com.google.android.apps.docs.storage
  D/Documents( 3358): Update found 7 roots in 28ms
  D/BackupManagerService(  608): Received broadcast Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.embarcadero.DLL_Test_Project flg=0x4000010 (has extras) }
  V/BackupManagerService(  608): addPackageParticipantsLocked: #1
  D/SystemBroadcastService(  987): Received broadcast action=android.intent.action.PACKAGE_ADDED and uri=
  W/ContextImpl(  987): Implicit intents with startService are not safe: Intent { act=com.google.android.gms.games.service.INTENT } android.content.ContextWrapper.startService:494 com.google.android.gms.games.service.GamesIntentService.a:101 com.google.android.gms.games.service.GamesIntentService.b:368
  I/ActivityManager(  608): Delay finish: com.android.vending/com.google.android.finsky.receivers.PackageMonitorReceiver$RegisteredReceiver
  I/ActivityManager(  608): Resuming delayed broadcast
  I/ActivityManager(  608): Delay finish: com.google.android.apps.plus/.service.PackagesMediaMonitor
  I/ActivityManager(  608): Resuming delayed broadcast
  V/GelStubAppWatcher( 3631): onReceive: android.intent.action.PACKAGE_ADDED
  I/Icing.InternalIcingCorporaProvider( 3631): Updating corpora: A: com.embarcadero.DLL_Test_Project, C: MAYBE
  I/ActivityManager(  608): START u0 {flg=0x10800000 cmp=com.estrongs.android.pop/.app.InstallMonitorActivity (has extras)} from pid 3466
  D/dalvikvm(  608): GC_EXPLICIT freed 1385K, 11% free 19671K/22028K, paused 3ms+8ms, total 194ms
  D/dalvikvm(  608): WAIT_FOR_CONCURRENT_GC blocked 24ms
  D/AndroidRuntime( 4437): Shutting down VM
  D/dalvikvm( 4437): GC_CONCURRENT freed 95K, 16% free 560K/660K, paused 0ms+0ms, total 2ms
  W/InputMethodManagerService(  608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42c98f48 attribute=null, token = android.os.BinderProxy@42b91f10
  D/dalvikvm( 3631): GC_CONCURRENT freed 560K, 6% free 10268K/10860K, paused 2ms+2ms, total 23ms
  D/AndroidRuntime( 4476):
  D/AndroidRuntime( 4476): >>>>>> AndroidRuntime START com.android.internal.os.RuntimeInit <<<<<<
  D/AndroidRuntime( 4476): CheckJNI is OFF
  D/dalvikvm( 4476): Trying to load lib libjavacore.so 0x0
  D/dalvikvm( 4476): Added shared lib libjavacore.so 0x0
  D/dalvikvm( 4476): Trying to load lib libnativehelper.so 0x0
  D/dalvikvm( 4476): Added shared lib libnativehelper.so 0x0
  D/dalvikvm( 4476): No JNI_OnLoad found in libnativehelper.so 0x0, skipping init
  D/dalvikvm( 4476): Note: class Landroid/app/ActivityManagerNative; has 179 unimplemented (abstract) methods
  D/AndroidRuntime( 4476): Calling main entry com.android.commands.am.Am
  I/ActivityManager(  608): START u0 {flg=0x10000000 cmp=com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity (has extras)} from pid 4476
  D/dalvikvm(  608): GC_FOR_ALLOC freed 807K, 12% free 19517K/22028K, paused 63ms, total 63ms
  D/AndroidRuntime( 4476): Shutting down VM
  D/dalvikvm( 4476): GC_CONCURRENT freed 96K, 15% free 586K/684K, paused 0ms+0ms, total 2ms
  D/dalvikvm( 4507): Late-enabling CheckJNI
  I/ActivityManager(  608): Start proc com.embarcadero.DLL_Test_Project for activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity: pid=4507 uid=10113 gids={50113, 3003, 1028, 1015}
  I/dalvikvm( 4507): Enabling JNI app bug workarounds for target SDK version 9...
  V/PhoneStatusBar(  667): setLightsOn(true)
  D/AndroidRuntime( 4507): Shutting down VM
  W/dalvikvm( 4507): threadid=1: thread exiting with uncaught exception (group=0x41ccbba8)
  E/AndroidRuntime( 4507): FATAL EXCEPTION: main
  E/AndroidRuntime( 4507): Process: com.embarcadero.DLL_Test_Project, PID: 4507
  E/AndroidRuntime( 4507): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativ
  eActivity}: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so
  E/AndroidRuntime( 4507):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
  E/AndroidRuntime( 4507):        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
  E/AndroidRuntime( 4507):        at android.app.ActivityThread.access$800(ActivityThread.java:135)
  E/AndroidRuntime( 4507):        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
  E/AndroidRuntime( 4507):        at android.os.Handler.dispatchMessage(Handler.java:102)
  E/AndroidRuntime( 4507):        at android.os.Looper.loop(Looper.java:136)
  E/AndroidRuntime( 4507):        at android.app.ActivityThread.main(ActivityThread.java:5017)
  E/AndroidRuntime( 4507):        at java.lang.reflect.Method.invokeNative(Native Method)
  E/AndroidRuntime( 4507):        at java.lang.reflect.Method.invoke(Method.java:515)
  E/AndroidRuntime( 4507):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
  E/AndroidRuntime( 4507):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
  E/AndroidRuntime( 4507):        at dalvik.system.NativeStart.main(Native Method)
  E/AndroidRuntime( 4507): Caused by: java.lang.IllegalArgumentException: Unable to load native library: /data/app-lib/com.embarcadero.DLL_Test_Project-1/libDLL_Test_Project.so
  E/AndroidRuntime( 4507):        at android.app.NativeActivity.onCreate(NativeActivity.java:183)
  E/AndroidRuntime( 4507):        at com.embarcadero.firemonkey.FMXNativeActivity.onCreate(FMXNativeActivity.java:67)
  E/AndroidRuntime( 4507):        at android.app.Activity.performCreate(Activity.java:5231)
  E/AndroidRuntime( 4507):        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
  E/AndroidRuntime( 4507):        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
  E/AndroidRuntime( 4507):        ... 11 more
  W/ActivityManager(  608):   Force finishing activity com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity
  I/WindowManager(  608): Screenshot max retries 4 of Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNat
  iveActivity t17 f}} appWin=Window{42a1bab8 u0 Starting com.embarcadero.DLL_Test_Project} drawState=4
  W/WindowManager(  608): Screenshot failure taking screenshot for (1200x1920) to layer 21015
  W/ActivityManager(  608): Activity pause timeout for ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17 f}
  E/WindowManager(  608): Starting window AppWindowToken{4308ff58 token=Token{42a06c48 ActivityRecord{42a42de8 u0 com.embarcadero.DLL_Test_Project/com.embarcadero.firemonkey.FMXNativeActivity t17}}} timed out
  I/Process ( 4507): Sending signal. PID: 4507 SIG: 9
  W/InputMethodManagerService(  608): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@42a1abd0 attribute=null, token = android.os.BinderProxy@42b91f10
  I/ActivityManager(  608): Process com.embarcadero.DLL_Test_Project (pid 4507) has died.
  D/Finsky  ( 1567): [1] 5.onFinished: Installation state replication succeeded.

更新1

从评论我收集了code可能是一个更大的系统的一部分。这code是一个小的独立的程序。当你看到这里的原生code库的确是小。

From the comments I gathered that the code might be part of a bigger system. This code is a small stand-alone program. The native code library is indeed as small as you see here.

更新2

由于Arioch'The指出的是,通过使用静态链接(或隐式加载在Windows而言)主程序不会加载时库不加载。这也解释了亚行的上述消息中提及。该querstion是这样的:?为什么libmiditest.so无法加载

As Arioch'The points out is that by using static linking (or implicit loading in Windows terms) the main program will not load when the library does not load. That explains the adb message mentioned above. The querstion is thus: why does libmiditest.so not load?

推荐答案

一种解决方案是通过动态绑定库在他的评论建议的Arioch'The。如何做到这一点的机制,在他的维基链接描述。在图书馆使用的dlopen Posix.Dlfcn 单元链接。

One solution is by dynamically binding the library as suggested by Arioch'The in his comments. The mechanism on how to do that is described in his wiki link. To link in the library use dlopen from the Posix.Dlfcn unit.

一个人来,虽然提供了一个路径添加到库。在这个例子中code下面我创造了 sdcard0 目录目录数据\ D 并复制 libmiditest.so 进去。这个目录可以是在其他系统不同。事实上,这或许可以解释的静态绑定不工作,(还)的原因。本库没有复制到寻找库时系统会搜索的路径。

One has to provide a path to the library though. In the example code below I created directory Data\d in the sdcard0 directory and copied libmiditest.so into it. This directory may be different on other systems. In fact, this might explain the reason that static binding does not work (yet). The library is not copied into the paths the system searches when looking for libraries.

  unit DLL_Test_Main;

  interface

  uses
     System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
     FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
     Posix.Dlfcn, FMX.Layouts, FMX.Memo, System.Math;


  const
     lib_path = '/storage/sdcard0/Data/d/libmiditest.so';
     fun_name = 'test_1';

  type
     TForm1 = class(TForm)
        Button_Load: TButton;
        Memo1: TMemo;

        procedure Button_LoadClick (Sender: TObject);
        procedure FormCreate(Sender: TObject);

      protected
         Lib_Handle: THandle;
      public
         procedure call_external_function (value: Integer);
      end; // Class: TForm1 //

  var
     Form1: TForm1;

  implementation

  {$R *.fmx}

  procedure TForm1.FormCreate (Sender: TObject);
  begin
     Lib_Handle := THandle (dlopen (lib_path, RTLD_LAZY));
     if Lib_Handle = 0 then
     begin
        Memo1.Lines.Add ('Cannot open library: ' + lib_path);
     end else
     begin
        Memo1.Lines.Add ('Opened library: ' + lib_path);
     end; // if
  end; // FormCreate //

  procedure TForm1.Button_LoadClick (Sender: TObject);
  begin
     call_external_function (RandomRange (0, 99));
  end; // Button_LoadClick //

  procedure TForm1.call_external_function (value: integer);
  var
     test_1: function (n: integer): integer; cdecl;
     n: Int32;
  begin
     if Lib_Handle <> 0 then
     begin
        test_1 := dlsym (Lib_Handle, fun_name);
        if not assigned (test_1) then
        begin
           Memo1.Lines.Add ('Cannot create function: ' + fun_name);
        end else
        begin
           n := test_1 (value);
           Memo1.Lines.Add (Format ('%d = %s (%d)', [n, fun_name, value]));
        end;
     end;
  end; // call_external_function //

  end.