Discussion:
Non blocking HTTP.Post
(too old to reply)
Fahd
2008-08-01 17:28:04 UTC
Permalink
Anybody knows how to accomplish an HTTP.Post because my app wil freeze
for up to 30s waiting for the response while the webservice is doing
other stuff. I've used idAntiFreeze but nothing happened with my Delphi
7 app. If I can't use idAntifreeze then how can I execute the post into
a seperate thread using either indy safethread class or borland Thread
class.
Thanks
Remy Lebeau (TeamB)
2008-08-01 19:02:13 UTC
Permalink
Post by Fahd
Anybody knows how to accomplish an HTTP.Post because my app
wil freeze for up to 30s waiting for the response while the webservice
is doing other stuff.
Perform the Post() in a separate worker thread.
Post by Fahd
I've used idAntiFreeze but nothing happened with my Delphi 7 app.
That should be working fine. Did you validate that TIdAntiFreeze was
actually enabled?
Post by Fahd
If I can't use idAntifreeze then how can I execute the post into a
seperate thread using either indy safethread class or borland Thread
class.
Simply call Post() inside the thread's Execute() method like any other call.


Gambit
Fahd
2008-08-01 19:47:08 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Fahd
Anybody knows how to accomplish an HTTP.Post because my app
wil freeze for up to 30s waiting for the response while the webservice
is doing other stuff.
Perform the Post() in a separate worker thread.
Post by Fahd
I've used idAntiFreeze but nothing happened with my Delphi 7 app.
That should be working fine. Did you validate that TIdAntiFreeze was
actually enabled?
Post by Fahd
If I can't use idAntifreeze then how can I execute the post into a
seperate thread using either indy safethread class or borland Thread
class.
Simply call Post() inside the thread's Execute() method like any other call.
Gambit
I created my class and implemented in my form using HTTPRIO object
because Indy component antifreeze didn't work. Anyway, when I execute it
I get and error message saying : "CoInitialize has not been called" when
it hit FThread.Resume. Any suggestion? am I doing it the right way? Thanks

(Myform Class)
private
FThread:TThread;
procedure OnTerminate(Sender:TObject)

I made a class like this:

TMyThread=class(TThread)
protected
procedure Execute;override;
public
constructor Create();
end;

.
.
.
procedure TClientForm.LoginButtonClick(Sender: TObject);
begin
FThread:=TMyThread.Create();
FThread.OnTerminate:=OnTerminate;
FThread.Resume;
end;

procedure TMyThread.Execute;
var
svc: IRealtimeSoap;
begin
svc := ClientForm.GetService;
ShowMessage(svc.myMethod(ClientForm.Edit1.Text));
end;

procedure TClientForm.OnTerminate(Sender: TObject);
begin
FThread:=nil;
end;
Marc Rohloff [TeamB]
2008-08-01 20:45:54 UTC
Permalink
Post by Fahd
I get and error message saying : "CoInitialize has not been called" when
it hit FThread.Resume. Any suggestion? am I doing it the right way? Thanks
Change your execute method to something like:

procedure TMyThread.Execute;
var
svc: IRealtimeSoap;
begin
CoInitialize;
try
svc := ClientForm.GetService;
//ShowMessage(svc.myMethod(ClientForm.Edit1.Text));
except
CoUninitialize;
end
end;

You should not access the VCL controls from a thread. Neither
ShowMessage nor Edit1.Text are safe to call from a non-ui thread.
--
Marc Rohloff [TeamB]
marc -at- marc rohloff -dot- com
Remy Lebeau (TeamB)
2008-08-01 21:24:30 UTC
Permalink
Post by Fahd
I created my class and implemented in my form using HTTPRIO
object because Indy component antifreeze didn't work
TIdAntiFreeze only works for Indy components. THTTPRio is not an Indy
component.
Post by Fahd
"CoInitialize has not been called" when it hit FThread.Resume.
THTTPRio uses ActiveX/COM internally. You have to call CoInitialize/Ex()
(and CoUninitialize()) in any thread that wants to use ActiveX/COM objects
before they can be instantiated.

Worse, ActiveX/COM objects (well, apartment-threaded ones, anyway) are tied
to the thread that instantiates them. So you can't instantiate the THTTPRio
in your main thread and then use it in your worker thread (without custom
marshalling, anyway). It would be better to have the worker thread
instantiate the THTTPRio object directly instead. Then it will be used in
the correct thread context.

Also, your thread's Execute() code is not safe anyway. ShowMessage() is not
thread-safe, and you are not accessing the TEdit in a thread-safe manner.
Post by Fahd
am I doing it the right way?
No. Try this code instead:

--- TMyForm ---

private
FThread: TThread;
procedure OnTerminate(Sender:TObject)

procedure TClientForm.LoginButtonClick(Sender: TObject);
begin
FThread := TMyThread.Create(Edit1.Text);
FThread.OnTerminate := OnTerminate;
FThread.Resume;
end;

procedure TClientForm.OnTerminate(Sender: TObject);
begin
FThread := nil;
end;


--- TMyThread ---

type
TMyThread = class(TThread)
private
fText: String;
function GetService: IRealtimeSoap;
protected
procedure Execute; override;
procedure DoTerminate; override;
public
constructor Create(const AText: String); reintroduce;
end;

constructor TMyThread.Create(const AText: String)
begin
inherited Create(True);
FreeOnTerminate = true;
fText := AText;
end;

function GetService: IRealtimeSoap;
var
rio: THTTPRio;
begin
rio := THTTPRio.Create(nil);
// configure rio as needed...
Result := rio as IRealtimeSoap;
end;

procedure TMyThread.Execute;
var
svc: IRealtimeSoap;
begin
CoInitialize(nil);
svc := GetService;
try
Windows.MessageBox(0, PChar(svc.myMethod(fText)), 'myMethod',
MB_OK);
finally
svc := nil;
end;
end;

procedure TMyThread.DoTerminate;
begin
CoUninitialize;
inherited;
end;


Gambit
Fahd
2008-08-01 23:43:37 UTC
Permalink
Windows.MessageBox(0, PChar(svc.myMethod(fText)), 'myMethod', MB_OK);
Instead of the above line, will this be a thread safe operation?

ClientForm.Memo1.Text := svc.myMethod(fText);

Or do I need to pass the handle of the memo to the thread constructor?

I really appreciate all your hard work
Remy Lebeau (TeamB)
2008-08-04 17:21:25 UTC
Permalink
Post by Fahd
Instead of the above line, will this be a thread safe operation?
No. You cannot safely access any GUI control from a worker thread like
that. If the thread needs to update the UI, it has to use the
TThread.Synchronize() method, or other inter-thread synchronization, for
that, ie:

type
TMyThread = class(TThread)
private
fText: String;
procedure SetMemoText;
...

procedure TMyThread.Execute;
...
begin
...
fText := svc.myMethod(fText);
Synchronize(SetMemoText);
...
end;

procedure TMyThrea.SetMemoText;
begin
ClientForm.Memo1.Text := fText;
end;
Post by Fahd
Or do I need to pass the handle of the memo to the thread constructor?
That is not thread-safe, either. If the Memo's handle were ever to be
recreated internally, your thread would be holding on to an invalid HWND.


Gambit
Fahd
2008-08-05 15:53:46 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Fahd
Instead of the above line, will this be a thread safe operation?
No. You cannot safely access any GUI control from a worker thread like
that. If the thread needs to update the UI, it has to use the
TThread.Synchronize() method, or other inter-thread synchronization, for
type
TMyThread = class(TThread)
private
fText: String;
procedure SetMemoText;
...
procedure TMyThread.Execute;
...
begin
...
fText := svc.myMethod(fText);
Synchronize(SetMemoText);
...
end;
procedure TMyThrea.SetMemoText;
begin
ClientForm.Memo1.Text := fText;
end;
Post by Fahd
Or do I need to pass the handle of the memo to the thread constructor?
That is not thread-safe, either. If the Memo's handle were ever to be
recreated internally, your thread would be holding on to an invalid HWND.
Gambit
Worked like a charm, thanks a lot
keep it up

Loading...