如何从C(下层)调用Delphi函数(上层),它是用libXXX.so构建的?

时间:2014-10-28 09:24:30

标签: c delphi firemonkey

我在Android上运行了一个FMX项目。 我可以让FMX项目在“libTest.so”中执行C函数,如下所示:

main.pas

TMyCallbackFun = procedure(Param: Integer) of object;

procedure TForm1.MyCallbackFun(Param: Integer);
begin
  ShowMessage('MyCallbackFun called');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Handle1: THandle;
  fun1: function(MyCallbackFun: TMyCallbackFun): Integer; stdcall;
begin
  Handle1 := LoadLibrary(PChar('/system/lib/libTestC.so'));
  if Handle1 <> 0 then begin
    fun1 := GetProcAddress(Handle1, PChar('RegisterCallback'));
    if Assigned(fun1) then begin
      Memo1.Lines.Add('RegisterCallback returns ' + IntToStr(fun1(MyCallbackFun)));
    end;
  end;
end;

TestC.c

int (*RegisteredCallback)(int);
struct itimerval t;

void alarm_handler(int a) {
    LOGD("alarm_handler called");

    t.it_interval.tv_usec = 0;
    t.it_interval.tv_sec = 0;
    t.it_value.tv_usec = 0;
    t.it_value.tv_sec = 0;

    RegisteredCallback(8);
}

int RegisterCallback(void *CallbackFun)
{
    LOGD("RegisterCallback called");

    RegisteredCallback = CallbackFun;

    t.it_interval.tv_usec = 0;
    t.it_interval.tv_sec = 2;
    t.it_value.tv_usec = 0;
    t.it_value.tv_sec = 3;
    setitimer(ITIMER_REAL, &t, NULL);
    signal(SIGALRM, alarm_handler);

    return 5;
}

我希望“libTest.so”中的C函数“RegisterCallback”保持Delphi函数指针“MyCallbackFun”,然后在某个时间(如计时器到期)“libTest.so”可以调用函数指针带参数。

但是当计时器到期时它会崩溃:

10-28 19:17:55.161: D/TestC(673): RegisterCallback called
10-28 19:17:58.161: D/TestC(673): alarm_handler called
10-28 19:18:00.001: D/KeyguardUpdateMonitor(980): received broadcast android.intent.action.TIME_TICK
10-28 19:18:00.001: D/KeyguardUpdateMonitor(980): handleTimeUpdate
10-28 19:18:00.101: D/dalvikvm(31379): GC_CONCURRENT freed 382K, 75% free 2759K/10856K, paused 4ms+3ms, total 32ms
10-28 19:18:00.161: D/TestC(673): alarm_handler called
10-28 19:18:00.291: I/ActivityManager(980): Process com.embarcadero.ExternalLibrary (pid 673) has died.
10-28 19:18:00.291: I/WindowState(980): WIN DEATH: Window{41b7b560 u0 com.embarcadero.ExternalLibrary/com.embarcadero.firemonkey.FMXNativeActivity}
10-28 19:18:00.291: W/ActivityManager(980): Force removing ActivityRecord{41a03fe0 u0 com.embarcadero.ExternalLibrary/com.embarcadero.firemonkey.FMXNativeActivity}: app died, no saved state
10-28 19:18:00.311: D/Launcher(1348): Broadcasting Home Idle Screen Intent ...
10-28 19:18:00.321: D/CAT(1249): StkAppService: 7called on slot:0
10-28 19:18:00.321: D/CAT(1249): StkAppService$ServiceHandler: Need to send IDLE SCREEN Available event to SIM
10-28 19:18:00.321: D/CAT(1249): StkAppService$ServiceHandler: Event :5
10-28 19:18:00.321: D/CAT(1249): StkAppService$ServiceHandler: SetupEventList is not received. Ignoring the event: 5
10-28 19:18:00.351: D/Zygote(378): Process 673 terminated by signal (11)
10-28 19:18:00.371: W/InputMethodManagerService(980): Got RemoteException sending setActive(false) notification to pid 673 uid 10122
: E/(): Device disconnected

任何专家都可以告诉我如何修复代码以实现我的目标吗?

提前致谢!

1 个答案:

答案 0 :(得分:1)

TMyCallbackFun = procedure(Param: Integer) of object;

of object Delphi函数是一个方法指针。它是双指针类型。所以,SizeOf(TMyCallbackFun)=SizeOf(Pointer)*2。一个指针是实例,另一个是代码。调用该方法时,会有一个额外的隐藏参数,它是实现中的实例Self。你不能在这里使用方法指针。你需要它

TMyCallbackFun = procedure(Param: Integer); cdecl;

或者,如果你需要传递一个实例,那么就这样做:

TMyCallbackFun = procedure(Param: Integer; Data: Pointer); cdecl;

在C方面

void (*RegisteredCallback)(int, void*);

请注意,我将返回值从int更改为void以匹配Delphi。而且我已经使用cdecl,因为我认为这是C编译器的默认调用约定。

您还需要存储数据以及回调:

void (*RegisteredCallback)(int, void*);
void *RegisteredCallbackData;
....
int RegisterCallback(void *CallbackFun, void *CallbackData)
{
    ....
    RegisteredCallback = CallbackFun;
    RegisteredCallbackData = CallbackData;
    ....
}

要从您执行的C代码调用回调:

RegisteredCallback(..., RegisteredCallbackData);

在Delphi方面,你实现了这样的回调:

procedure MyCallbackFun(Param: Integer; Data: Pointer); cdecl;
begin
  TForm1(Data).MyCallbackFun(Param);
end;

您需要声明fun1以匹配:

fun1: function(CallbackFun: TMyCallbackFun; CallbackData: Pointer): Integer; cdecl;

我认为这就是一切!