线程如何通知没有窗口句柄的对象?

时间:2011-06-21 04:56:13

标签: multithreading delphi windows-messages

我是多线程的新手,但不是一个完整的新手。我需要在工作线程中执行对Web服务的调用。

在主线程中,我有一个表单(TForm),它有一个私有数据成员(私有字符串),只有工作线程才会写入(我会在它恢复之前将指针传递给线程)。当工作线程完成其webservice调用并将结果响应xml写入表单上的私有成员时,工作线程使用PostMessage向表单的句柄发送消息(我在恢复之前也将其传递给线程)。 / p>

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;

3 个答案:

答案 0 :(得分:9)

我之前使用过这种技术取得了一些成功:Sending messages to non-windowed applications

基本上,在通过AllocateHWND获得的句柄上使用第二个线程作为消息泵。这无疑是令人恼火的,你最好使用库来处理所有细节。我更喜欢OmniThreadLibrary,但还有其他人 - 请参阅How Do I Choose Between the Various Ways to do Threading in Delphi?Delphi - Threading frameworks

答案 1 :(得分: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;

您可以将此应用于任何不仅仅是线程。请注意,如here所示,AllocateHWND不是线程安全的。

答案 2 :(得分: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. 向线程类添加一个新的事件属性,您可以在该线程类中将标志作为参数来指示终止/线程结果。像shown here这样的东西。务必同步事件调用。或者忘记参数,只是在执行正常完成时调用事件(就像你现在正在做的那样)。