线程如何通知没有窗口句柄的对象?
我是多线程的新手,但并不是一个完全的新手。我需要在工作线程中执行对 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我之前已经使用过这种技术并取得了一些成功:向非窗口应用程序发送消息< /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.
您可以使用 AllocateHwnd 分配自己的句柄并将其用作 PostMessage 目标。
您可以将其应用于任何东西,而不仅仅是线程。请注意,AllocateHWND 不是线程安全的,如此处。
You can allocate you own handle with AllocateHwnd and use that as a PostMessage target.
You can apply this to anything not just threads. Just beware that AllocateHWND is NOT threade safe as indicated here.
基于事件使用的替代方案:
使用OnTerminate线程(已经存在)与标志的组合:
在执行例程中设置 TerminateFlag。即使 FreeOnTerminate 为 True,OnTerminate 也会自动触发。
向线程类添加一个新的事件属性,您可以在其中提供标志作为参数来指示终止/线程结果。类似此处所示。请务必同步事件调用。或者忘记参数,仅在执行正常完成时调用事件(就像您现在所做的那样)。
Alternatives based on the use of an event:
Use OnTerminate of the thread (already present) in combination with a flag:
Set the TerminateFlag in the Execute routine. OnTerminate will automatically fire, even if FreeOnTerminate is True.
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).