我在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
任何专家都可以告诉我如何修复代码以实现我的目标吗?
提前致谢!
答案 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;
我认为这就是一切!