Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: Couldn't use LoadFromStream inside Indy OnGetCommand?


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 3 - Last Post: Nov 16, 2017 2:03 PM Last Post By: Remy Lebeau (Te...
Mauricio Lima

Posts: 6
Registered: 4/25/17
Couldn't use LoadFromStream inside Indy OnGetCommand?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 15, 2017 7:38 PM
I'm making a server that receives a data, send a GET command to a Google Places API, load the xml data with XML Data Binding, but stuck when using LoadFromStream.

procedure TfrmMain.LavaServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
i:integer;
latF,lonF:double;
poss:integer;
fmt: TFormatSettings;
s:string;
bufferList:TStringList;
EnvioBuffer:TStringList;
ApenasQuinze:TStringList;
EnvioStream: TMemoryStream;
latP,longP:string;
strm: TMemoryStream;
Listings1: bus_station.IXMLPlaceSearchResponseType;
lavaXML: IXMLDocument;
nomeXML,longXML,latXML:string;
begin
SpinEdit4.Value := SpinEdit4.Value+1;
  try
    s := Copy(ARequestInfo.URI,2,ARequestInfo.URI.Length-1);
  except
    exit;
  end;
 
  if s = '' then Exit;
 
  SpinEdit4.Value := SpinEdit4.Value+1;
 
  fmt := TFormatSettings.Create;
  fmt.DecimalSeparator := '.';
 
  try
    latF := StrToFloat(Fetch(s, ','), fmt);
    lonF := StrToFloat(s, fmt);
  except
    Exit;
  end;
 
latP := StringReplace(FloatToStr(latF),',','.',[rfReplaceAll, rfIgnoreCase]);
longP := StringReplace(FloatToStr(lonF),',','.',[rfReplaceAll, rfIgnoreCase]);
try
strm := TMemoryStream.Create;//('',TEncoding.UTF8);
IdHTTP1.Get('https://maps.googleapis.com/maps/api/place/nearbysearch/xml?location='+latP+','+longP+'&rankby=distance&type=car_wash&language=pt-BR&key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', strm);
strm.Position := 0;
*lavaXML.LoadFromStream(strm);*  // <<<- Stuck in this line
Listings1 := bus_station.GetPlaceSearchResponse(lavaXML);
Label3.Caption := '1';
finally
strm.Free;
strm := nil;
end;
Label3.Caption := '3';
 
if Listings1.Status = 'OK' then
begin
      for I := 0 to Listings1.Result.Count-1 do
      begin
        longXML := StringReplace(Listings1.Result[I].Geometry.Location.Lng,',','.',[rfIgnoreCase, rfReplaceAll]);
        latXML := StringReplace(Listings1.Result[I].Geometry.Location.Lat,',','.',[rfIgnoreCase, rfReplaceAll]);
        EnvioBuffer.Add(longXML+','+latXML);
        EnvioBuffer.Add(Listings1.Result[I].Name);
      end;
end;
 
  ApenasQuinze := TStringList.Create;
 
  if EnvioBuffer.Count <= 30 then
    for i := 0 to EnvioBuffer.Count-1 do ApenasQuinze.Add(EnvioBuffer[i])
  else
    for i := 0 to 29 do ApenasQuinze.Add(EnvioBuffer[i]);
  try
    EnvioStream := TMemoryStream.Create;
    try
      ApenasQuinze.SaveToStream(EnvioStream, TEncoding.UTF8);
    except
      EnvioStream.Free;
      raise;
    end;
    AResponseInfo.ContentStream := EnvioStream;
    AResponseInfo.ContentType := 'text/plain';
    AResponseInfo.CharSet := 'utf-8';
  finally
    EnvioBuffer.Free;
  end;
end;


There is something I'm missing?

Ps: If I try to load the xml with a Button.OnClick it works.

Edited by: Mauricio Lima on Nov 15, 2017 7:38 PM

Edited by: Mauricio Lima on Nov 15, 2017 7:39 PM
Antonio Estevez

Posts: 613
Registered: 4/12/00
Re: Couldn't use LoadFromStream inside Indy OnGetCommand? [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 15, 2017 11:57 PM   in response to: Mauricio Lima in response to: Mauricio Lima
El 16/11/2017 a las 4:40, Mauricio Lima escribió:
I'm making a server that receives a data, send a GET command to a Google Places API, load the xml data with XML Data Binding, but stuck when using LoadFromStream.

*lavaXML.LoadFromStream(strm);*  // <<<- Stuck in this line</div>
 
<div class="jive-quote">


There is something I'm missing?

You are calling LoadFromStream from an uninitialized object:
lavaXML:= NewXMLDocument();
lavaXML.LoadFromStream(strm);


On the other hand, I think that LavaServer1CommandGet is not executed in the context of the main thread.
So you must initialize the COM library before use any XML stuff.
uses
   ActiveX;
....
 
   CoInitialize(nil);
   // use XML
   CoUninitialize;


https://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx

Perhaps, the best place to call CoInitialize is the OnConnect event and the OnDisconnect event to call CoUninitialize.

Also you must synchronize with the main thread the accesses to visual components like SpinEdit4.
Remy Lebeau (Te...


Posts: 8,950
Registered: 12/23/01
Re: Couldn't use LoadFromStream inside Indy OnGetCommand? [Edit]  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 16, 2017 2:03 PM   in response to: Antonio Estevez in response to: Antonio Estevez
Antonio Estevez wrote:

On the other hand, I think that LavaServer1CommandGet is not executed
in the context of the main thread.

Correct. It is executed in a worker thread created by Indy.

So you must initialize the COM library before use any XML stuff.

Yes, if you are using a COM-based XML parser, like MSXML (which is the
default on Windows).

However, I would not suggest initializing COM in the server's
OnCommand... events, like you have suggested. The OnCommand... events
can potentially be called multiple times per thread, if HTTP
keep-alives are used.

The best place to initialize COM is at the beginning of a thread, and
then uninitialized before the thread terminates.

To do that with Indy, yoou can derive a new class from
TIdThreadWithTask and override its virtual BeforeExecute() and
AfterExecute() methods to (un)initialize COM. Then assign a
TIdSchedulerOfThread... component to the TIdHTTP.Scheduler property,
and assign your thread class to the scheduler's ThreadClass property.

Perhaps, the best place to call CoInitialize is the OnConnect event
and the OnDisconnect event to call CoUninitialize.

Only if the server is using TIdSchedulerOfThreadDefault (which it does
by default if you don't assign a scheduler). It uses one thread per
connection, so OnConnect and OnDisconnect will execute once per thread.
However, if you use TIdSchedulerOfThreadPool instead, any given thread
could service multiple connections during its lifetime, thus calling
OnConnect and OnDisconnect multiple times per thread.

Also you must synchronize with the main thread the accesses to visual
components like SpinEdit4.

Correct.

--
Remy Lebeau (TeamB)
Mauricio Lima

Posts: 6
Registered: 4/25/17
Re: Couldn't use LoadFromStream inside Indy OnGetCommand?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 16, 2017 1:44 PM   in response to: Mauricio Lima in response to: Mauricio Lima
Thank you Antonio!
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02