이 질문은 one에서 발생합니다.
문제는 시스템에서 많은 콜백 명령을 저장할 수있는 비 시각적 구성 요소를 작성하는 것입니다. 사용자는 IDE에서 무제한으로 콜백을 정의 할 수 있습니다. 콜백은 TCollection에서 TCollectionItem으로 정의됩니다.
이것은 꽤 잘 작동하는 패턴이지만 몇 가지 단점이 있습니다. 더 나은 할 수 있다면, 따라서 궁금 후술 ;-)
이, 사용자가 CommandsTable 컬렉션을 콜백 함수의 IDE 무제한의 주요 구성 요소를 정의 할 수있다
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은 시스템에서 호출되는 콜백입니다. 여기
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;
을 (STDCALL이 협약 호출) 그리고 것은 구현입니다. 전체 프로세스는 "시작"절차
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() 함수를 사용하는 것입니다.
function TEventCollectionItem.InternalEventFunction(ASomeNotUsefullPointer:pointer; ASomeInteger: integer): Word; stdcall; begin // some important preprocessing stuff // ... if Assigned(FOnEventCommand) then begin FOnEventCommand(Param1, Param2); end; end;
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;
는 내가 전에 한 바와 같이, 괜찮 작동하지만 기능 MethodToProcedure는()
썽크 기술을 사용합니다. 데이터 실행 방지 (DEP)가 및 64 비트 아키텍처에서 활성화 된 시스템에서 프로그램이 작동하지 않기 때문에 새로운 MethodToProcedure() 함수가 필요합니다.
더 좋은 패턴을 알고 있습니까?
그냥 완료, 여기 MethodToProcedure()입니다. (나는 누가 원저자인지 모릅니다). 대신 포인터의 배열의 기록의 배열을 허용하도록 DLL을 변경할 수있는 경우
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;
답변 해 주셔서 감사합니다. Unfrotuantely, 나는 DLL을 변경할 수 없습니다. (그것은 전혀 잘못 설계되었지만 함께 살아야합니다.) 그래서 썽크가 유일한 해결책 일 것입니다. – Peter