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

发布于 2024-11-16 18:10:19 字数 2064 浏览 5 评论 0原文

我是多线程的新手,但并不是一个完全的新手。我需要在工作线程中执行对 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;

I'm new to multithreading, but not a complete novice. I need to perform a call to a webservice in a worker thread.

In the main thread I have a form (TForm) with a private data member (private string) that only the worker thread will write to (I pass the a pointer to it into the thread before it resumes). When the worker thread has finished its webservice call and written the resultant response xml to the private member on the form, the worker thread uses PostMessage to send a message to the form's handle (which I also passed into the thread before it resumed).

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;

It works great, but now I want to do the same thing from a datamodule (which doesn't have a Handle)... so I would really appreciate some useful code to supplement the working model I have.

EDIT

What I really want is the code (if possible) that would allow me to replace the line

MyWorkerThread.SenderHandle := self.Handle;

with

MyWorkerThread.SenderHandle := GetHandleForThisSOAPDataModule;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

何处潇湘 2024-11-23 18:10:19

我之前已经使用过这种技术并取得了一些成功:向非窗口应用程序发送消息< /a>

基本上,使用第二个线程作为通过 AllocateHWND 获取的句柄上的消息泵。这无疑令人恼火,您最好使用库来处理所有细节。我更喜欢 OmniThreadLibrary 但还有其他 - 请参阅我如何在执行线程的各种方法之间进行选择Delphi?Delphi - 线程框架

I have used this technique before with some success: Sending messages to non-windowed applications

Basically, use a second thread as a message pump on a handle obtained via AllocateHWND. This is admittedly irritating, and you would be better off using a library to handle all the details. I prefer OmniThreadLibrary but there are others - see How Do I Choose Between the Various Ways to do Threading in Delphi? and Delphi - Threading frameworks.

过期情话 2024-11-23 18:10:19

您可以使用 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 不是线程安全的,如此处。

You can allocate you own handle with AllocateHwnd and use that as a PostMessage target.

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;

You can apply this to anything not just threads. Just beware that AllocateHWND is NOT threade safe as indicated here.

会发光的星星闪亮亮i 2024-11-23 18:10:19

基于事件使用的替代方案:

  1. 使用OnTerminate线程(已经存在)与标志的组合:

    TMyDataModule = 类(TDataModule)
    私人的    
      过程 OnWebServiceCallComplete(Sender: TObject);
    ...  
    
    TMyThread = 类(TThread)
    民众
      属性 TerminateFlag: 整数 ...
    ...
    
    过程 TMyDataModule.StartWorkerThread;
      ...
      MyWorkerThread.OnTerminate := OnWebServiceCallComplete;
      ...
    
    过程 TMyDataModule.OnWebServiceCallComplete(Sender: TObject);
    开始
      如果 MyWorkerThread.TerminateFlag = WEBCALL_COMPLETE 那么
        ...
    结尾;
    

    在执行例程中设置 TerminateFlag。即使 FreeOnTerminate 为 True,OnTerminate 也会自动触发。

  2. 向线程类添加一个新的事件属性,您可以在其中提供标志作为参数来指示终止/线程结果。类似此处所示。请务必同步事件调用。或者忘记参数,仅在执行正常完成时调用事件(就像您现在所做的那样)。

Alternatives based on the use of an event:

  1. Use OnTerminate of the thread (already present) in combination with a flag:

    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;
    

    Set the TerminateFlag in the Execute routine. OnTerminate will automatically fire, even if FreeOnTerminate is True.

  2. Add a new event property to the thread class in which you may provide the flag as a parameter to indicate termination/thread result. Something like shown here. Be sure to synchronize the event call. Or forget the parameter and just only call the event if execution completed gracefully (like you're doing now).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文