3

次の例を検討してください。

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 (つまり、メソッドを直接実行する)

説明:

  1. 初期の SomeFlag の値は PostponeExecution です。

  2. ttc.method1 への最初の呼び出しは、OnBefore イベント (OnBefore_fires_1) をトリガーします。SomeFlag が PostponeExecution であるため、メソッドは実行されません。したがって、SomeFlag を DirectExecute に設定し、スレッドのコンテキスト内で同じメソッドを再度呼び出すスレッドが作成されます。

  3. 次に、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 メソッドに非常に近い。

これが主なアイデアです!

更新: より明確にするために、質問全体を再編集しました。

4

1 に答える 1