How can a thread notify an object that does not have a window handle?

I'm new to multithreading, but not a complete newbie. I need to make a web service call in a workflow.

In the main stream, I have a form (TForm) with a private data member (private string), to which only the worker stream will write (I pass a pointer to it in the stream until it resumes). When the workflow completed its webservice call and wrote the resulting xml response to the private member of the form, the workflow uses PostMessage to send the message to the form handle (which I also passed to the stream before it resumes).

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; 

This works fine, but now I want to do the same from a datamodule (which does not have a Handle) ... so I would really appreciate some useful code that complements the working model I have.

EDIT

What I really want is a code (if possible) that will allow me to replace the string

 MyWorkerThread.SenderHandle := self.Handle; 

from

 MyWorkerThread.SenderHandle := GetHandleForThisSOAPDataModule; 
+6
source share
3 answers

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

Basically, use the second thread as the message pump on the handle received through AllocateHWND. This is admittedly annoying, and you would be better off using the library to handle all the details. I prefer OmniThreadLibrary , but there are others - see. How to choose between the various methods of performing Threading in Delphi? and Delphi are Threading frameworks .

+9
source

You can highlight your own AllocateHwnd handle and use it as the target of 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 // 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 all, not just threads. Just be careful that AllocateHWND is NOT safe, as indicated here .

+5
source

Event based alternatives:

  • Use the OnTerminate stream (already present) in combination with the 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; 

    Install TerminateFlag in the Execute routine. OnTerminate will start automatically even if FreeOnTerminate is True.

  • Add a new event property to the thread class in which you can specify a flag as a parameter to indicate the completion / thread result. Something like shown here . Be sure to sync the call. Or forget the parameter and just raise the event if the execution is done gracefully (for example, what you are doing now).

+3
source

Source: https://habr.com/ru/post/890977/


All Articles