【发布时间】:2012-05-23 20:02:00
【问题描述】:
这个问题来自这个one。
问题是:创建可以容纳来自系统的许多回调命令的非可视组件。
用户可以在 IDE 中定义无限数量的回调。回调将在 TCollection 中定义为 TCollectionItem。
这是一种效果很好的模式,但也有一些缺点。 (稍后描述)
因此,我想知道是否可以做得更好;-)
这是一个主要组件,用户可以通过CommandsTable集合在IDE中定义无限个回调函数
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() 使用 Thunk 技术。
我喜欢避免这种情况,因为程序无法在启用了数据执行保护 (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;
【问题讨论】:
标签: delphi pointers methods callback stdcall