Search code examples
multithreadingweb-servicesdelphidelphi-10-seattle

Extracting code from TWebAction for Synchronize()ing in main thread?


I'm having a hard time wrapping my head around using Synchronize from a TWebActionItem.

My webservice is a visual program with form TFormWebServices

var FormWebServices: TFormWebServices;

This has a property FWebBrokerBridge: TIdHTTPWebBrokerBridge
The FWebBrokerBridge registers TWebModuleWebServices as the classes to use for each incoming call:

FWebBrokerBridge.RegisterWebModuleClass(TWebModuleWebServices);

The TWebActionItem items in this class do the work reading a TWebRequest and writing a TWebResponse.

There is one handler that needs to use some VCL visual rendering (shared code with another app) and I want to Synchronize this with the main thread (FormWebServices).

Stub:

TWebModuleWebServices = class(TWebModule)
  procedure WebModuleWebServicesTTGetDynReportAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  procedure WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
 private
   FResponse: TResBase;     // JSON response object
 end;   

procedure TWebModuleWebServices.WebModuleWebServicesTTGetDynReportAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  lFrmDynRapport : TFormDynRapport;   // A form containing the common code, has VCL components
begin
  lResponse := (FResponse as TResGetDynReport);  // TResGetDynReport = class(TResBase)
  // ...  
  lFrmDynRapport := TFormDynRapport.Create(Self);
  try
     with lFrmDynRapport do
     begin
        // Do visual stuff with grids, read grid settings, fill lResponse with info specific for this webaction
     end
  finally
  end   
  // ...
end;  

procedure TWebModuleWebServices.WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var lJSO: ISuperObject;  // SuperObject library
begin
  lJSO := FResponse.ToJson;
  // ...
  Response.Content := lJSO.AsString;
  Handled := true;
end;

Now how can I properly Synchronize the try/finally block? I can get at the current thread and do something simple like:

lThread := TThread.CurrentThread;
TThread.Synchronize(lThread,procedure
  begin
    FormWebServices.MmoLog.Lines.Add('Synchronize test');
  end);

but for anything more complex I'm stuck.
Defining local procedures and feeding these into Synchronize does not work (E2555 cannot capture symbol).
How can I extract the try/finally block so that I can properly execute it in the context of FormWebServices?


Solution

  • The trick is to put the entire block in an anonymous method (thanks for the tip, David):

    if lFlexRapDM.DesignerCustomOpenDoc(lFlexRapDM.TimeTellReport) then
       begin
          .....
          lThread := TThread.CurrentThread;
          TThread.Synchronize(lThread,procedure
                var l,lIndex       : integer;
                ...
                begin
                   lFrmDynRapport := TFormDynRapport.Create(Self);
                   try
                      with lFrmDynRapport do
                      begin
                         ...
                      end; // with FrmDynReport
                   finally
                      lFrmDynRapport.Free;
                   end;
                end); 
          ...         
       end // if lFlexRapDM.DesignerCustomOpenDoc