Link to home
Start Free TrialLog in
Avatar of seinc
seinc

asked on

How to send message from thread to main thread?

I have created a windows service that spawns a new thread. I need to send messages to the service(main thread) as this child thread is executing. Everything I have found so far seems to rely on a GUI application. I have no user interface, I just need to update a string variable in the main thread based on what the spawned thread is doing.
Avatar of Russell Libby
Russell Libby
Flag of United States of America image


Below is an example of a worker thread that updates the sample service at random intervals (displays a service dialog, which requires interactive  = true). This should give you some ideas of what you can do. If have questions, then please ask, but it would helpe to see specific code examples for what you are currently doing.

Regards,
Russell

---


unit SampleService;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs;

type
  TWorkerThread     =  class(TThread)
  private
     // Private declarations
     procedure      UpdateService;
  protected
     // Protected declarations
     procedure      Execute; override;
  public
     // Public declarations
     constructor    Create;
  end;

type
  TsvcSample        =  class(TService)
     procedure      ServiceStart(Sender: TService; var Started: Boolean);
     procedure      ServicePause(Sender: TService; var Paused: Boolean);
     procedure      ServiceContinue(Sender: TService; var Continued: Boolean);
     procedure      ServiceStop(Sender: TService; var Stopped: Boolean);
  private
     // Private declarations
     FWorker:       TWorkerThread;
  protected
     // Protected declarations
     procedure      UpdateServiceState(Value: String);
  public
     // Public declarations
     function       GetServiceController: TServiceController; override;
  end;

var
  svcSample:        TsvcSample;

implementation
{$R *.DFM}

//// TWorkerThread /////////////////////////////////////////////////////////////
procedure TWorkerThread.UpdateService;
begin

  // Running in context of main thread
  svcSample.UpdateServiceState(IntToStr(GetTickCount));

end;

procedure TWorkerThread.Execute;
begin

  // While not terminated
  while not(Terminated) do
  begin
     // Sleep a random amount of time
     Sleep(Random(5000));
     // Update the service
     Synchronize(UpdateService);
  end;

end;

constructor TWorkerThread.Create;
begin

  // Set defaults
  FreeOnTerminate:=False;
  Priority:=tpLower;

  // Perform inherited
  inherited Create(False);

end;

//// TsvcSample ////////////////////////////////////////////////////////////////
procedure ServiceController(CtrlCode: DWord); stdcall;
begin

  // Handle the control code
  svcSample.Controller(CtrlCode);

end;

function TsvcSample.GetServiceController: TServiceController;
begin

  // Return the service controller
  Result:=ServiceController;

end;

procedure TsvcSample.ServiceStart(Sender: TService; var Started: Boolean);
begin

  // Create the worker thread
  FWorker:=TWorkerThread.Create;

end;

procedure TsvcSample.ServicePause(Sender: TService; var Paused: Boolean);
begin

  // Pause the worker thread
  FWorker.Suspend;

end;

procedure TsvcSample.ServiceContinue(Sender: TService; var Continued: Boolean);
begin

  // Resource protection
  try
     // Resume the worker thread
     FWorker.Resume;
  finally
     // Continue the service
     Continued:=True;
  end;

end;

procedure TsvcSample.ServiceStop(Sender: TService; var Stopped: Boolean);
begin

  // Resource protection
  try
     // Terminate the thread
     FWorker.Terminate;
     // Wait for thread execute to finish
     FWorker.WaitFor;
  finally
     // Free worker thread
     FWorker.Free;
  end;

end;

procedure TsvcSample.UpdateServiceState(Value: String);
begin

  // Update from worker thread
  MessageBox(0, PChar(Value), 'Worker Update', MB_OK or MB_SERVICE_NOTIFICATION);

end;

end.
Unfortunately, the very nature of the servicethread does not easily do as you request.
The main service thread does very little after it starts things.
You can use ControlService() and "DoCustomControl" but it's not ideal.
I'd recommend you use some other form of inter process communication
sockets, shared memory, named pipes etc.
ASKER CERTIFIED SOLUTION
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I agree with TheRealLoki. If you only need to update a string, use a critical section.

Or you use a Windows event with the CreateEvent/PulseEvent/SetEvent/ResetEvent API functions. But that would mean your main thread is continuously waiting for some event.

Or you implement your own messageloop and thus you would be able to send/post messages to your main thread.

But basically there are many different solutions. Just keep in mind that whenever you have one thread sending messages to the other thread, the other thread must be able to receive and respond to this message. This often means that the main thread has to regularly check for new messages. In a GUI application the windows themselves will implement this mechanism. In a background task you will have to write your own code for this, though.
Avatar of seinc
seinc

ASKER

Thanks for all the advice. I am a newbie with Delphi, so I'm not sure how to implement many of these suggestions. It appears that Critical section is pretty straightforward to use, but how do I make a global variable that my spawned thread and main thread can access?
I can't really paste code because it would go on forever. I am tasked with adding this functionality to existing code. Basically what I have is this:

A windows service that runs the Indy HttpServer.
This windows service spawns a thread that listens on an Enterprise Service Bus for Joram messages.
When a message on a relevant topic is found, an xml Business Object Document ("BOD" based on Oagis) processor specific to that BOD type is instantiated.
This BOD processor inherits from a class that inherits from a class.
This BOD processor is supposed to communicate information  back to the windows service about if it is currently processing a BOD, and if it is, what is the BOD id, type, etc, etc.
The Indy HttpServer is supposed to respond with info sent by the BOD processor.

Maybe thread messages is not the way to go here. Maybe I create a webservice that the BOD processor updates, and the windows service reads?