[英]how to use callback function of Delphi is in c
我有一個用delphi編寫的dll,可以導出如下功能
function LaneController_Init(OnIOChangeEvent:TOnIOChangeEvent):Integer; stdcall;
OnIOChangeEvent是一個回調函數,propertype是
TOnIOChangeEvent = procedure(sender:TObject;DeviceID,iFlag:Integer) of object;
現在我的問題是在C ++中,如何定義回調函數TOnIOChangeEvent ?
非常感謝。
您的DLL使用的是Delphi的兩個獨特功能,只有C ++ Builder支持,而其他C ++編譯器則不支持:
您的回調使用的of object
修飾符,這意味着可以為該回調分配對象實例的非靜態方法。 這是在C ++ Builder中使用供應商特定的__closure
編譯器擴展來實現的。 盡管標准C ++確實具有使用函數指針指向對象方法的語法,但其實現與__closure
的實現方式有很大不同。
您的回調沒有聲明任何調用約定,因此正在使用Delphi的默認register
調用約定。 在C ++ Builder中,它對應於特定於供應商的__fastcall
調用約定(不要與Visual C ++的__fastcall
調用約定混淆,后者完全不同,並在C ++ Builder中實現為__msfastcall
)。
如果只關心支持C ++ Builder,則可以按原樣保留DLL代碼,並且相應的C ++代碼如下所示:
typedef void __fastcall (__closure *TOnIOChangeEvent)(TObject *Sender, int DeviceID, int iFlag);
int __stdcall LaneController_Init(TOnIOChangeEvent OnIOChangeEvent);
void __fastcall TSomeClass::SomeMethod(TObject *Sender, int DeviceID, int iFlag)
{
//...
}
TSomeClass *SomeObject = ...;
LaneController_Init(&(SomeObject->SomeMethod));
但是,如果需要支持其他C ++編譯器,則需要更改DLL以支持標准C / C ++,例如:
type
TOnIOChangeEvent = procedure(DeviceID, iFlag: Integer; UserData: Pointer); stdcall;
function LaneController_Init(OnIOChangeEvent: TOnIOChangeEvent; UserData: Pointer): Integer; stdcall;
然后,您可以在C ++中執行以下操作:
typedef void __stdcall (*TOnIOChangeEvent)(int DeviceID, int iFlag, void *UserData);
int __stdcall LaneController_Init(TOnIOChangeEvent OnIOChangeEvent, void *UserData);
void __fastcall TSomeClass::SomeMethod(int DeviceID, int iFlag)
{
//...
}
// note: not a member of any class. If you want to use a class
// method, it will have to be declared as 'static'...
void __stdcall LaneControllerCallback(int DeviceID, int iFlag, void *UserData)
{
((TSomeClass*)UserData)->SomeMethod(DeviceID, iFlag);
}
TSomeClass *SomeObject = ...;
LaneController_Init(&LaneControllerCallback, SomeObject);
出於某些原因,您不能在DLL和C ++中具有“對象”功能。
EXE.TObject
和DLL.TObject
。 雖然它們的實現希望是彼此的克隆,但是它們的指針是不同的,因此任何像EXE.TForm is DLL.TComponent
檢查或像DLL.TButton as EXE.TPersistent
這樣的DLL.TButton as EXE.TPersistent
類型轉換DLL.TButton as EXE.TPersistent
將失敗,從而破壞代碼的邏輯,會錯誤地期望OOP繼承基礎正常工作。 那你該怎么辦呢? 您可以建立什么樣的“共同點”?
高科技選項是使用一些具有對象概念的豐富的跨平台ABI(二進制接口)。 對於Windows,通常使用COM對象,例如Excel,Word,Internet Explorer。 因此,您使用C ++制作了一個COM Server,它具有一些帶有GUID標簽的接口,該接口中包含回調函數。 然后,您將接口指針傳遞給回調函數。 就像您使用其他COM服務器和Active-X控件(例如TExcelApplication
, TWebBrowser
等等)一樣。 還有其他方法,例如CORBA,JSON-RPC,SOAP和其他方法。 並且只允許您使用由那些跨平台互操作標准標准化的數據類型的參數。
對於低技術含量的選項,您必須將Windows API看作是“扁平化”非對象語言的面向對象接口的典型示例。
function LaneController_Init(OnIOChangeEvent:TOnIOChangeEvent; ASelf: pointer):Integer; stdcall;
TOnIOChangeEvent = procedure(const Self: pointer; const Sender: Pointer; const DeviceID, iFlag:Integer); stdcall;
在Delphi中,您將編寫類似
procedure OnIOChangeCallBack(const ASelf: pointer; const Sender: Pointer; const DeviceID, iFlag:Integer); stdcall;
begin
(TObject(ASelf) as TMyClass).OnIOChange(Sender, DeviceID, iFlag);
end;
可能在C ++中看起來像這樣
void stdcall OnIOChangeCallBack(const void * Self; const void * Sender: Pointer; const int DeviceID; const int iFlag);
{
((CMyClass*)Self)->OnIOChange(Sender, DeviceID, iFlag);
}
同樣,您將只能使用這些數據類型作為參數,這些數據類型是語言之間的“最大公分母”,例如整數,雙精度數和指針。
我非常同意@Arioch的說法,但是我認為當我們有接口時,使用裸pointers
在Windows上全面運行是很愚蠢的。
我建議您使用COM自動化接口。
首先創建一個標准程序。
在內部使用以下概述的步驟創建一個自動化對象。
請參閱此處的教程,請注意,同時提供了Delphi和C ++構建器代碼/示例。
http://docwiki.embarcadero.com/RADStudio/XE3/zh/Creating_Simple_COM_Servers_-_概述
創建自動化對象后,請向該接口添加一個接口和至少一個過程。
您不能在代碼中引用Delphi對象,只是不能移植。 但是,只要您在type library editor
聲明它們, 就可以使用自己的接口。
確保將父接口設置為IDispatch
。
現在您想使用對象的任何地方,只需使用適當的接口即可。
請注意, TObject
不實現任何接口,但TComponent
可以實現。 許多其他的Delphi對象也實現接口。
您可能需要擴展現有對象,以實現自己的接口。
這是一些示例代碼:
unit Unit22;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
ComObj, ActiveX, AxCtrls, Classes,
Project23_TLB, StdVcl;
type
TLaneController = class(TAutoObject, IConnectionPointContainer, ILaneController)
private
{ Private declarations }
FConnectionPoints: TConnectionPoints;
FConnectionPoint: TConnectionPoint;
FEvents: ILaneControllerEvents;
FCallBack: ICallBackInterface; /////////
{ note: FEvents maintains a *single* event sink. For access to more
than one event sink, use FConnectionPoint.SinkList, and iterate
through the list of sinks. }
public
procedure Initialize; override;
protected
procedure LaneController_Init(const CallBack: ICallBackInterface); safecall;
{ Protected declarations }
property ConnectionPoints: TConnectionPoints read FConnectionPoints
implements IConnectionPointContainer;
procedure EventSinkChanged(const EventSink: IUnknown); override;
procedure WorkThatCallBack; /////////
{ TODO: Change all instances of type [ITest234Events] to [ILaneControllerEvents].}
{ Delphi was not able to update this file to reflect
the change of the name of your event interface
because of the presence of instance variables.
The type library was updated but you must update
this implementation file by hand. }
end;
implementation
uses ComServ;
procedure TLaneController.EventSinkChanged(const EventSink: IUnknown);
begin
FEvents := EventSink as ILaneControllerEvents;
end;
procedure TLaneController.Initialize;
begin
inherited Initialize;
FConnectionPoints := TConnectionPoints.Create(Self);
if AutoFactory.EventTypeInfo <> nil then
FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
AutoFactory.EventIID, ckSingle, EventConnect)
else FConnectionPoint := nil;
end;
procedure TLaneController.LaneController_Init(const CallBack: ICallBackInterface);
begin
FCallBack:= CallBack;
end;
procedure TLaneController.WorkThatCallBack;
const
SampleDeviceID = 1;
SampleFlag = 1;
begin
try
if Assigned(FCallBack) then FCallBack.OnIOChangeEvent(Self, SampleDeviceID, SampleFlag);
except {do nothing}
end; {try}
end;
initialization
TAutoObjectFactory.Create(ComServer, TLaneController, Class_LaneController,
ciMultiInstance, tmApartment);
end.
請注意,大多數代碼是自動生成的。
只有標有////////
的成員沒有。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.