6

私はマルチスレッドは初めてですが、完全な初心者ではありません。ワーカー スレッドで Web サービスの呼び出しを実行する必要があります。

メイン スレッドには、ワーカー スレッドのみが書き込むプライベート データ メンバ (プライベート文字列) を持つフォーム (TForm) があります (スレッドが再開する前に、そのポインタをスレッドに渡します)。ワーカー スレッドが Web サービス呼び出しを終了し、結果の応答 xml をフォームのプライベート メンバーに書き込むと、ワーカー スレッドは PostMessage を使用してメッセージをフォームのハンドルに送信します (これは、再開前にスレッドにも渡しました)。

interface

const WM_WEBSERVCALL_COMPLETE = WM_USER + 1;

type 
  TWebServiceResponseXML = string;
  PWebServiceResponseXML = ^TWebServiceResponseXML;

  TMyForm = class(TForm)
    ...
  private
    ...
    fWorkerThreadID: Cardinal;
    fWebServiceResponseXML: TWebServiceResponseXML;
  public
    ...
    procedure StartWorkerThread;
    procedure OnWebServiceCallComplete(var Message: TMessage); Message WM_WEBSERVCALL_COMPLETE;
  end;

  TMyThread = class(TThread)
  private      
  protected
    procedure Execute; override;
  public
    SenderHandle: HWnd;
    RequestXML: string;
    ResponseXML: string;
    IMyService: IService;
    PResponseXML: PWebServiceResponseXML;
  end;

implementation

procedure TMyForm.StartWorkerThread;
var
  MyWorkerThread: TMyThread;
begin
  MyWorkerThread := TMyThread.Create(True);
  MyWorkerThread.FreeOnTerminate := True;
  MyWorkerThread.SenderHandle := self.Handle;
  MyWorkerThread.RequestXML := ComposeRequestXML;
  MyWorkerThread.PResponseXML := ^fWebServiceResponseXML;
  MyWorkerThread.Resume;
end;

procedure TMyForm.OnWebServiceCallComplete(var Message: TMessage);
begin
  // Do what you want with the response xml string in fWebServiceResponseXML
end;

procedure TMyThread.Execute;
begin
  inherited;
  CoInitialize(nil);
  try
    IMyService := IService.GetMyService(URI);
    ResponseXML := IMyService.Search(RequestXML);
    PResponseXML := ResponseXML;
    PostMessage(SenderHandle, WM_WEBSERVCALL_COMPLETE, 0, 0);
  finally
    CoUninitialize;
  end;
end;

それはうまく機能しますが、今はデータモジュール(ハンドルを持たない)から同じことをしたいので、私が持っている作業モデルを補足する便利なコードを本当に感謝しています。

編集

私が本当に欲しいのは、(可能であれば)行を置き換えることができるコードです

MyWorkerThread.SenderHandle := self.Handle;

MyWorkerThread.SenderHandle := GetHandleForThisSOAPDataModule;
4

3 に答える 3

9

以前にこの手法を使用して、いくつかの成功を収めました:非ウィンドウ アプリケーションへのメッセージの送信

基本的に、AllocateHWND を介して取得したハンドルのメッセージ ポンプとして 2 番目のスレッドを使用します。これは確かに苛立たしく、ライブラリを使用してすべての詳細を処理する方がよいでしょう。私はOmniThreadLibraryを好みますが、他にもあります。「Delphi でスレッド化を行うさまざまな方法をどのように選択すればよいですか?」を参照してください。Delphi-Threading フレームワーク。

于 2011-06-21T05:05:00.810 に答える
5

AllocateHwndを使用して独自のハンドルを割り当て、それをPostMessageターゲットとして使用できます。

TTestThread = class(TThread)
private
  FSignalShutdown: boolean;
  // hidden window handle 
  FWinHandle: HWND;                       
protected
  procedure Execute; override;
  // our window procedure 
  procedure WndProc(var msg: TMessage);   
public
  constructor Create;
  destructor Destroy; override;
  procedure PrintMsg;
end;

constructor TTestThread.Create;
begin
  FSignalShutdown := False;

  // create the hidden window, store it's 
  // handle and change the default window 
  // procedure provided by Windows with our 
  // window procedure 

  FWinHandle := AllocateHWND(WndProc);
  inherited Create(False);
end;

destructor TTestThread.Destroy;
begin
  // destroy the hidden window and free up memory 
  DeallocateHWnd(FWinHandle);
  inherited;
end;

procedure TTestThread.WndProc(var msg: TMessage);
begin
  if Msg.Msg = WM_SHUTDOWN_THREADS then
    // if the message id is WM_SHUTDOWN_THREADS
    // do our own processing        
    FSignalShutdown := True 
  else        
    // for all other messages call 
    // the default window procedure 
    Msg.Result := DefWindowProc(FWinHandle, Msg.Msg, 
                                Msg.wParam, Msg.lParam);
end;

これは、スレッドだけでなく、あらゆるものに適用できます。ここに示されているように、AllocateHWNDはスレッドセーフではないことに注意してください。

于 2011-06-21T05:56:07.833 に答える
3

イベントの使用に基づく代替手段:

  1. スレッド (既に存在する) のOnTerminateをフラグと組み合わせて使用​​します。

    TMyDataModule = class(TDataModule)
    private    
      procedure OnWebServiceCallComplete(Sender: TObject);
    ...  
    
    TMyThread = class(TThread)
    public
      property TerminateFlag: Integer ...
    ...
    
    procedure TMyDataModule.StartWorkerThread;
      ...
      MyWorkerThread.OnTerminate := <Self.>OnWebServiceCallComplete;
      ...
    
    procedure TMyDataModule.OnWebServiceCallComplete(Sender: TObject);
    begin
      if MyWorkerThread.TerminateFlag = WEBCALL_COMPLETE then
        ...
    end;
    

    Execute ルーチンで TerminateFlag を設定します。FreeOnTerminate が True であっても、OnTerminate は自動的に起動します。

  2. 終了/スレッドの結果を示すパラメータとしてフラグを指定できる新しいイベント プロパティをスレッド クラスに追加します。ここに示すようなもの。イベント呼び出しを必ず同期してください。または、パラメーターを忘れて、実行が正常に完了した場合にのみイベントを呼び出します (現在行っているように)。

于 2011-06-21T06:19:29.880 に答える