此問題源於此one。
問題是:創建非可視組件,它可以容納來自系統的許多回調命令。 用戶可以在IDE中定義無限數量的回調。回調將在TCollection中定義爲TCollectionItem。
這是一種很不錯的模式,但有一些缺點。 (後述) 所以我想,如果這是可以做到更好;-)
這是一個主要組成部分,用戶可以在IDE無限數量的回調函數,通過CommandsTable集合定義
具有stdcall調用約定的回調系統組件的模式
TMainComp = class(TComponent)
private
CallbacksArray: array [0..x] of pointer;
procedure BuildCallbacksArray;
public
procedure Start;
published
property CommandsTable: TCommandCollection read FCommandsTable write SetCommandsTable;
end;
每個集合項目都是這樣的,InternalCommandFunction是從系統調用的回調函數。 (stdcall調用)
TCommandCollectionItem = class(TCollectionItem)
public
function InternalCommandFunction(ASomeNotUsefullPointer:pointer; ASomeInteger: integer): Word; stdcall;
published
property OnEventCommand: TComandFunc read FOnEventCommand write FOnEventCommand;
end;
TComandFunc = function(AParam1: integer; AParam2: integer): Word of Object;
這裏是一個實現。整個過程可以用「開始」程序
procedure TMainComp.Start;
begin
// fill CallBackPointers array with pointers to CallbackFunction
BuildCallbacksArray;
// function AddThread is from EXTERNAL dll. This function creates a new thread,
// and parameter is a pointer to an array of pointers (callback functions).
// New created thread in system should call our defined callbacks (commands)
AddThread(@CallbacksArray);
end;
開始這是有問題的代碼。我認爲如何獲得指向「InternalEventFunction」函數 的唯一方法是使用MethodToProcedure()函數。
procedure TMainComp.BuildCallbacksArray;
begin
for i := 0 to FCommandsTable.Count - 1 do begin
// it will not compile
//CallbacksArray[i] := @FCommandsTable.Items[i].InternalEventFunctionWork;
// compiles, but not work
//CallbacksArray[i] := @TCommandCollectionItem.InternalCommandFunction;
// works pretty good
CallbacksArray[i] := MethodToProcedure(FCommandsTable.Items[i], @TCommandCollectionItem.InternalCommandFunction);
end;
end;
function TEventCollectionItem.InternalEventFunction(ASomeNotUsefullPointer:pointer; ASomeInteger: integer): Word; stdcall;
begin
// some important preprocessing stuff
// ...
if Assigned(FOnEventCommand) then begin
FOnEventCommand(Param1, Param2);
end;
end;
正如我以前所描述的,它的工作原理確定,但功能MethodToProcedure()使用咚技術。 我喜歡避免這種情況,因爲程序無法在啓用數據執行保護(DEP) 以及64位體系結構的系統上運行,這可能是全新的MethodToProcedure()函數所必需的。
你知道一些更好的模式嗎?
剛剛竣工,這裏是一個MethodToProcedure()。 (我不知道誰是原作者)。
TMethodToProc = packed record
popEax: Byte;
pushSelf: record
opcode: Byte;
Self: Pointer;
end;
pushEax: Byte;
jump: record
opcode: Byte;
modRm: Byte;
pTarget: ^Pointer;
target: Pointer;
end;
end;
function MethodToProcedure(self: TObject; methodAddr: Pointer): Pointer;
var
mtp: ^TMethodToProc absolute Result;
begin
New(mtp);
with mtp^ do
begin
popEax := $58;
pushSelf.opcode := $68;
pushSelf.Self := Self;
pushEax := $50;
jump.opcode := $FF;
jump.modRm := $25;
jump.pTarget := @jump.target;
jump.target := methodAddr;
end;
end;
感謝您的回答。毫無疑問,我無法改變dll。 (這是錯誤的設計,但我必須忍受它)。所以thunk可能是唯一的解決方案。 – Peter