Board index » delphi » Threading with TAutoObject

Threading with TAutoObject

Hi all,

I've written a COM object that gets called from a VBA macro in Word. I've
created a thread that this object uses, and when the thread finishes I want
to call a method in the COM object. The problem I'm having is that when the
thread finishes, the OnTerminate event isn't triggered, and if I try to use
'Synchronize' on a thread method to get into the context of the main thread,
the method isn't called. If I try to call the COM object method directly
from the thread, it doesn't work either.

I'm new to threading in Delphi, so I may be missing something obvious here.
I've included an edited code sample.

Any help would be much appreciated.

Many thanks,

Jamie

[snip]

type
  TOfficeIntegration = class(TAutoObject, IOfficeIntegration)
  protected
    procedure showSearch; stdcall;
    [snip]
  private
    [snip]
    procedure startRequestThread;
    procedure requestTerminated(Sender: TObject);
  end;

  TRequestThread = class(TThread)
  private
    [snip]
  protected
    procedure Execute; override;
    procedure doRequest;
  published
    [snip]
  end;

implementation

uses ComServ;

[snip]

procedure TOfficeIntegration.showSearch;
{ Shows the 'Search' dialog. }
begin
  {Defer to RPC server}
  appSession.showSearch;

  startRequestThread;
end;

procedure TOfficeIntegration.startRequestThread;
var
  requestThread: TRequestThread;
begin
  { Create thread }
  requestThread := TRequestThread.Create(True);
  requestThread.OnTerminate := requestTerminated;
  requestThread.Resume;
end;

procedure TOfficeIntegration.requestTerminated(Sender: TObject);
var
  requestThread: TRequestThread;
  resultString: String;
begin
  ShowMessage('Got thread terminated!');
end;

[snip]

{ TRequestThread }

procedure TRequestThread.doRequest;
begin
  ShowMessage('Doing request: ' + FResultString);
end;

procedure TRequestThread.Execute;
var
  result: String;
begin
  result := FAppSession.requestResult;

  if Length(result) > 0 then begin
    FResultString := result;
  end;

  Synchronize(doRequest);
end;

[snip].

 

Re:Threading with TAutoObject


Jamie,

Just a guess, could it be that the call:

result := FAppSession.requestResult;

throws an exception ?

that would explain why Synchronize isnt called...
again: just a guess i havent really looked carefully

Quote
"Jamie" <jami...@zfree.co.nz> wrote in message

news:arh8ec$n4c$1@lust.ihug.co.nz...
Quote
> Hi all,

> I've written a COM object that gets called from a VBA macro in Word. I've
> created a thread that this object uses, and when the thread finishes I
want
> to call a method in the COM object. The problem I'm having is that when
the
> thread finishes, the OnTerminate event isn't triggered, and if I try to
use
> 'Synchronize' on a thread method to get into the context of the main
thread,
> the method isn't called. If I try to call the COM object method directly
> from the thread, it doesn't work either.

> I'm new to threading in Delphi, so I may be missing something obvious
here.
> I've included an edited code sample.

> Any help would be much appreciated.

> Many thanks,

> Jamie

> [snip]

> type
>   TOfficeIntegration = class(TAutoObject, IOfficeIntegration)
>   protected
>     procedure showSearch; stdcall;
>     [snip]
>   private
>     [snip]
>     procedure startRequestThread;
>     procedure requestTerminated(Sender: TObject);
>   end;

>   TRequestThread = class(TThread)
>   private
>     [snip]
>   protected
>     procedure Execute; override;
>     procedure doRequest;
>   published
>     [snip]
>   end;

> implementation

> uses ComServ;

> [snip]

> procedure TOfficeIntegration.showSearch;
> { Shows the 'Search' dialog. }
> begin
>   {Defer to RPC server}
>   appSession.showSearch;

>   startRequestThread;
> end;

> procedure TOfficeIntegration.startRequestThread;
> var
>   requestThread: TRequestThread;
> begin
>   { Create thread }
>   requestThread := TRequestThread.Create(True);
>   requestThread.OnTerminate := requestTerminated;
>   requestThread.Resume;
> end;

> procedure TOfficeIntegration.requestTerminated(Sender: TObject);
> var
>   requestThread: TRequestThread;
>   resultString: String;
> begin
>   ShowMessage('Got thread terminated!');
> end;

> [snip]

> { TRequestThread }

> procedure TRequestThread.doRequest;
> begin
>   ShowMessage('Doing request: ' + FResultString);
> end;

> procedure TRequestThread.Execute;
> var
>   result: String;
> begin
>   result := FAppSession.requestResult;

>   if Length(result) > 0 then begin
>     FResultString := result;
>   end;

>   Synchronize(doRequest);
> end;

> [snip].

Re:Threading with TAutoObject


Have a look in the VCL source-code to see how "Synchronize" (e.g.) actually
works.  It uses custom Windows messages that VBA would not know how to
handle; nor COM, for that matter.  

Quote
>Jamie wrote:
> I've written a COM object that gets called from a VBA macro in Word. I've
> created a thread that this object uses, and when the thread finishes I
> want to call a method in the COM object. The problem I'm having is that
> when the thread finishes, the OnTerminate event isn't triggered, and if I
> try to use 'Synchronize' on a thread method to get into the context of the
> main thread, the method isn't called. If I try to call the COM object
> method directly from the thread, it doesn't work either.

> I'm new to threading in Delphi, so I may be missing something obvious
> here. I've included an edited code sample.

> Any help would be much appreciated.

> Many thanks,

> Jamie

----------------------------------
Fast automatic Paradox table repair at a click of a mouse!
http://www.sundialservices.com/products/chimneysweep

Re:Threading with TAutoObject


That call is definately successful. I just can't get control back to the
main thread - the OnTerminate doesn't fire, and if I make a Synchronize call
to a thread method, the method never gets called.

Thanks,

Jamie

"Jeroen van Hilst" <jeroen-NOSP...@six-pack.nl> wrote in message
news:arie96$oco$1@reader14.wxs.nl...

Quote
> Jamie,

> Just a guess, could it be that the call:

> result := FAppSession.requestResult;

> throws an exception ?

> that would explain why Synchronize isnt called...
> again: just a guess i havent really looked carefully

> "Jamie" <jami...@zfree.co.nz> wrote in message
> news:arh8ec$n4c$1@lust.ihug.co.nz...
> > Hi all,

> > I've written a COM object that gets called from a VBA macro in Word.
I've
> > created a thread that this object uses, and when the thread finishes I
> want
> > to call a method in the COM object. The problem I'm having is that when
> the
> > thread finishes, the OnTerminate event isn't triggered, and if I try to
> use
> > 'Synchronize' on a thread method to get into the context of the main
> thread,
> > the method isn't called. If I try to call the COM object method directly
> > from the thread, it doesn't work either.

> > I'm new to threading in Delphi, so I may be missing something obvious
> here.
> > I've included an edited code sample.

> > Any help would be much appreciated.

> > Many thanks,

> > Jamie

> > [snip]

> > type
> >   TOfficeIntegration = class(TAutoObject, IOfficeIntegration)
> >   protected
> >     procedure showSearch; stdcall;
> >     [snip]
> >   private
> >     [snip]
> >     procedure startRequestThread;
> >     procedure requestTerminated(Sender: TObject);
> >   end;

> >   TRequestThread = class(TThread)
> >   private
> >     [snip]
> >   protected
> >     procedure Execute; override;
> >     procedure doRequest;
> >   published
> >     [snip]
> >   end;

> > implementation

> > uses ComServ;

> > [snip]

> > procedure TOfficeIntegration.showSearch;
> > { Shows the 'Search' dialog. }
> > begin
> >   {Defer to RPC server}
> >   appSession.showSearch;

> >   startRequestThread;
> > end;

> > procedure TOfficeIntegration.startRequestThread;
> > var
> >   requestThread: TRequestThread;
> > begin
> >   { Create thread }
> >   requestThread := TRequestThread.Create(True);
> >   requestThread.OnTerminate := requestTerminated;
> >   requestThread.Resume;
> > end;

> > procedure TOfficeIntegration.requestTerminated(Sender: TObject);
> > var
> >   requestThread: TRequestThread;
> >   resultString: String;
> > begin
> >   ShowMessage('Got thread terminated!');
> > end;

> > [snip]

> > { TRequestThread }

> > procedure TRequestThread.doRequest;
> > begin
> >   ShowMessage('Doing request: ' + FResultString);
> > end;

> > procedure TRequestThread.Execute;
> > var
> >   result: String;
> > begin
> >   result := FAppSession.requestResult;

> >   if Length(result) > 0 then begin
> >     FResultString := result;
> >   end;

> >   Synchronize(doRequest);
> > end;

> > [snip].

Re:Threading with TAutoObject


Quote
Jamie <jami...@zfree.co.nz> wrote in message

news:arh8ec$n4c$1@lust.ihug.co.nz...

Quote
> Hi all,

> I've written a COM object that gets called from a VBA macro in Word. I've
> created a thread that this object uses, and when the thread finishes I
want
> to call a method in the COM object. The problem I'm having is that when
the
> thread finishes, the OnTerminate event isn't triggered, and if I try to
use
> 'Synchronize' on a thread method to get into the context of the main
thread,
> the method isn't called. If I try to call the COM object method directly
> from the thread, it doesn't work either.

Not sure what you are doing wrong here. I assume the server has a form
associated with it, if not, then I'll have to think again.

Assuming for the moment this is a normal vcl application, the following code
works for me on D4. There should be enough for you to guess the missing
details. Note that I am not a great fan of Synchronize or the OnTerminate
handler, but in a simple app they should and do work.

unit ThreadedAutoObject;

interface

uses
  ComObj, ActiveX, TestThreadedAuto_TLB, ThreadedAutoTest, Classes, windows;

type
  TWorker = class(TThread)
  private
    { Private declarations }
  protected
    procedure Show;
    procedure Execute; override;
  end;

  TThreadedAuto = class(TAutoObject, IThreadedAuto)
 private
  FWorkerThread: TWorker;
  protected
    procedure DoMyRequest(const Text: WideString); safecall;
 public
    procedure HandleTerminate(Sender: TObject);
  end;

implementation

uses ComServ;

// call from client
procedure TThreadedAuto.DoMyRequest(const Text: WideString);
begin
 Form1.Memo1.Lines.Add(Text);  // client says Hi
  FWorkerThread := TWorker.Create(TRUE);
 FWorkerThread.FreeOnTerminate := TRUE;
 FWorkerThread.OnTerminate := HandleTerminate;
 FWorkerThread.Resume;
end;

{ TWorker }

procedure TWorker.Execute;
begin
 Synchronize(Show);    file:// thread says Hi from within
end;

procedure TThreadedAuto.HandleTerminate(Sender: TObject);
begin
 Form1.Memo1.Lines.Add('Hi the thread has just terminated');
end;

procedure TWorker.Show;
begin
 Form1.Memo1.Lines.Add('Hi from inside the thread');
end;

initialization
  TAutoObjectFactory.Create(ComServer, TThreadedAuto, Class_ThreadedAuto,
 ciMultiInstance, tmApartment);
end.

Other Threads