次の例を検討してください。
type
TTestClass = class
public
procedure method1; virtual;
end;
TForm2 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
public
vmi: TVirtualMethodInterceptor;
ttc: TTestClass;
end;
{ Initially SomeFlag is PostponeExecution }
procedure TForm2.FormCreate(Sender: TObject);
begin
vmi := TVirtualMethodInterceptor.Create(TTestClass);
ttc := TTestClass.Create;
vmi.OnBefore :=
procedure(Instance: TObject; Method: TRttiMethod;
const Args: TArray<TValue>; out DoInvoke: Boolean;
out Result: TValue)
begin
if { SomeFlag = DirectExecution } then
DoInvoke := true
else
begin
{ SomeFlag := DirectExecution }
TThread.CreateAnonymousThread(
procedure
begin
// Invoke() will trigger vmi.OnBefore
// because Instance is the proxified object
// I want to keep "Self" to be the proxified object
Method.Invoke(Instance, Args);
end
).Start;
end
end;
vmi.Proxify(ttc);
ttc.method1;
end;
{ TTestClass }
procedure TTestClass.method1;
begin
// Do something async
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
vmi.Unproxify(ttc);
vmi.Free;
ttc.Free;
end;
フックされた仮想メソッドがスレッドで実行されるようにします。つまり、その実行を遅延/延期します。
この目的のために、TVirtualMethodInterceptor を使用して、特定のクラスの仮想メソッドをインターセプトします。仮想メソッドが呼び出されると、vmi.OnBefore が起動されます。これは私の考えを簡略化したものです:
Call_VirtualMethod(method1) -> OnBefore_fires_1 -> CreateThread_and_InvokeAgain -> OnBefore_fires_2 -> DoInvoke := true (つまり、メソッドを直接実行する)
説明:
初期の SomeFlag の値は PostponeExecution です。
ttc.method1 への最初の呼び出しは、OnBefore イベント (OnBefore_fires_1) をトリガーします。SomeFlag が PostponeExecution であるため、メソッドは実行されません。したがって、SomeFlag を DirectExecute に設定し、スレッドのコンテキスト内で同じメソッドを再度呼び出すスレッドが作成されます。
次に、OnBefore が再び起動します (Instance がプロキシ化されたオブジェクト、つまりメソッドがフックされたメソッドであるため)。今回は SomeFlag が DirectExecute で、メソッドが呼び出されます。
メソッドを呼び出すときにプロキシ化されたオブジェクト (Instance var) を使用します。このように、method1 が同じクラスの他の仮想メソッドを呼び出すと、後者もスレッドで自動的に実行されます。
これを行うには、フラグをどこかに保存する必要があります。つまり、OnBefore の 2 回目の呼び出しで何をすべきかを示します。私の質問は、OnBefore の 2 回の呼び出し中にアクセスできるように、「SomeFlag」を格納する方法/場所です。ソリューションはクロスプラットフォームでなければなりません。提案/その他の解決策も大歓迎です。
VMT パッチ ( link1 、 link2 、 link3 ) で実行できると思いますが、VirtualProtectは Windows のみの機能であるため、クロスプラットフォームの要件に違反します。
どんなアイデアでも大歓迎です。
これはどういうことですか:
Delphi でこの種のクラスを作成できると想像してください。
TBusinessLogic = class
public
// Invokes asynchronously
[InvokeType(Async)]
procedure QueryDataBase;
// Invokes asynchronously and automatically return asocciated ITask (via OnBefore event)
[InvokeType(Await)]
function DownloadFile(AUrl: string): ITask;
// This method touches GUI i.e. synchonized
[InvokeType(VclSend)]
procedure UpdateProgressBar(AValue: integer);
// Update GUI via TThread.Queue
[InvokeType(VclPost)]
procedure AddTreeviewItem(AText: string);
end;
...
procedure TBusinessLogic.QueryDataBase;
begin
// QueryDataBase is executed ASYNC (QueryDataBase is tagged as Async)
// Do heavy DB Query here
// Updating GUI is easy, because AddTreeviewItem is tagged as VclPost
for SQLRec in SQLRecords do
AddTreeviewItem(SQLRec.FieldByName["CustomerName"].asString);
end;
このアプローチにより、スレッド化と同期が大幅に簡素化されます。TThread.Synchronize()、TThread.Queue() などのダックタイピングはもう必要ありません。ビジネス ロジックに集中し、適切なメソッドを呼び出すだけです。OnBefore イベントが「汚い」仕事をしてくれます。C# の Await メソッドに非常に近い。
これが主なアイデアです!
更新: より明確にするために、質問全体を再編集しました。