簡體   English   中英

如何在Delphi中使用回調函數

[英]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 ++編譯器則不支持:

  1. 您的回調使用的of object修飾符,這意味着可以為該回調分配對象實例的非靜態方法。 這是在C ++ Builder中使用供應商特定的__closure編譯器擴展來實現的。 盡管標准C ++確實具有使用函數指針指向對象方法的語法,但其實現與__closure的實現方式有很大不同。

  2. 您的回調沒有聲明任何調用約定,因此正在使用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 ++中具有“對象”功能。

  1. C ++類基礎結構可能與Delphi有所不同,直到您證明它們一點一點相同。 “ TObject”應被證明是相同的共同點。 http://docwiki.embarcadero.com/RADStudio/XE5/en/Libraries_and_Packages_Index
  2. DLL沒有類型安全性。 它們僅具有“名稱=指針”列表。 因此,即使不同版本的Delphi也將具有不同的,不兼容的類實現。
  3. 即使您使用相同的Delphi版本制作應用程序和DLL,您仍然會有兩個不同的TObject類: EXE.TObjectDLL.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控件(例如TExcelApplicationTWebBrowser等等)一樣。 還有其他方法,例如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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM