RTC Forums

Dashboard => Quick Start => Topic started by: D.Tkalcec (RTC) on November 24, 2009, 06:50:39 PM



Title: Calling remote functions in blocking mode
Post by: D.Tkalcec (RTC) on November 24, 2009, 06:50:39 PM
RealThinClient SDK 3.38 introduces new properties and methods to simplify writing code for calling server-side functions from the client in blocking mode (stop code execution until a result arrives). If you want to call a remote function prepared on the Server and wait for the result so you can process it immediately when it arrives, provided you are using the RTC SDK 3.38 or a later version, all you will need is one TRtcHttpClient component and one TRtcClientModule component with a few lines of code.

Here is a modified "Client Lesson 2" from the RTC SDK Quick Start guide, demonstrating now you can use the new "Execute" method to issue blocking remote calls instead of using a TRtcResult component and writing an event to process your result. This lesson continues where Server Lesson 5 stops. You can find the complete source code and textual version of this lesson below.

1. Start a new Project in Delphi

2. put one Edit field on your form (name "Edit1")
3. for Edit1, set Text = 'User'

4. put one Memo field on your form (name "Memo1")
5. for Memo1, set ScrollBars = ssBoth

6. from the "RTC Client" tab, put one RtcHttpClient component on your form (name "RtcHttpClient1").
7. for RtcHttpClient1, set AutoConnect = True
8. for RtcHttpClient1, set ReconnectOn.ConnectLost = True
9. for RtcHttpClient1, set ServerAddr = localhost
10. for RtcHttpClient1, set ServerPort = 80

11. from the "RTC Client" tab, put one RtcClientModule component on your form (name "RtcClientModule1").
12. for RtcClientModule1, set Client = RtcHttpClient1
13. for RtcClientModule1, set AutoRepost = 1
14. for RtcClientModule1, set ModuleFileName = '/mytest'

These are the minimum properties you should set for the RtcHttpClient and RtcClientModule components to use them for this example project. There are a lot more properties you can use, so feel free to experiment. And now that we have all the components set up, we are ready to make remote function calls to the Server. To test how blocking remote function calls work with the RTC SDK, we will write a few lines of code for the "OnKeyPress" event on our "Edit1" component ...

15. Define the "OnKeyPress" event on the "Edit1" component:

---- Standard example using the "with" statement ----

 if Key=#13 then
    begin
    Edit1.SelectAll;
    with RtcClientModule1 do
      begin
      // Prepare a new remote function call
      Prepare('hello');

      // set function call parameters
      Param.asString['name']:=Edit1.Text;

      // Execute the remote function and wait for a result
      Execute;

      // Use the result we have received from the Server ...
      Memo1.Lines.Add( LastResult.asString );
      end;
    end;

--- Now the same example without using "with" ---

  if Key=#13 then
    begin
    Edit1.SelectAll;

    // Prepare a new remote function object
    RtcClientModule1.Prepare('hello');

    // set function call parameters
    RtcClientModule1.Param.asString['name']:=Edit1.Text;

    // Execute the remote function and wait for a result
    RtcClientModule1.Execute;

    // Print the result out ...
    Memo1.Lines.Add( RtcClientModule1.LastResult.asString );
    end;

--- another example without using "with" ---

  if Key=#13 then
    begin
    Edit1.SelectAll;

    // Prepare a new remote function call and set function parameter
    RtcClientModule1.Prepare('hello').asString['name']:=Edit1.Text;

    // Execute the remote function, wait for the result and put it into our memo ...
    Memo1.Lines.Add( RtcClientModule1.Execute.asString );
    end;

----------

16. Start the Server (from Server Lesson 5), then Compile and Run this client project.

If everything went ok, after you start both projects, when you enter some text in the Edit field and press the Enter key, you will receive a response in your Memo component.

In this example, we are passing a single string parameter ('name') and receive a single string as a result. You can pass any kind of data to the server as parameters and receive any structure as the result. Please check the FAQ for more info.

Here is the explanation of all the methods and properties of the TRtcClientModule used in this example ...

*** function Prepare(const FunctionName:String):TRtcFunctionInfo;

      As an alternative to using the Data property directly for preparing data for
      the next function call, if you only want to prepare a single remote function call
      for which you would normally use "Data.newFunction()", you can also use "Prepare".

      The "Prepare" method returns a pointer to the TRtcFunctionInfo object, which you
      can use to prepare all function parameters. Using "Prepare" is also easier than
      using the "Data" property directly, because each call to "Prepare" will first
      make a call to "Data.Clear" to make sure there are no left-overs from
      possible problems from any prior use of the "Data" or "Prepare" methods.
      That is, as long as you only use the "Prepare" method and do NOT use "Data" directly.

      Using "Prepare" is identical to using "Data.Clear" and "Data.NewFunction()";

*** property Param:TRtcFunctionInfo;

      After using "Prepare" or "Data.newFunction" to create a new Function call object,
      you can use the "Param" property to prepare all your function call parameters.

      Using "Param" is identical to using "Data.asFunction".

*** function Execute(AutoFreeResult:boolean=True; _Timeout:cardinal=0;
                              AllowMessageProcessing:boolean=True):TRtcValue;

      If you want to send a remote function call to the Server and wait for the result without
      using a separate TRtcResult component and without writing the OnResult and OnAbort events,
      you can prepare a synchronized (blocking) call by using the "Prepare" method and then
      (once prepared) execute the remote call and get a result by using the "Execute" method.

      In case of a communications error during the remote call, the "Execute" function will
      raise an exception with the appropriate error text. If everything goes well (no exeception),
      you will get your result directly from the "Execute" method in form of the TRtcValue object.

      There are two ways one can use "Execute":
        1) The easy way is to use "Execute" or "Execute(TRUE)" (meaning that AutoFreeResult=TRUE),
           in which case the Result object you receive will be automatically destroyed the next time
           you use the "Execute" method, or when the TRtcClientModule component is being destroyed.
        2) The advanced way is to use "Execute(FALSE)", in which case you will have to FREE the
           result object received from "Execute(FALSE)" manually, once you are finished using it.

      Using the "Execute" method is easier than using the "Call" method because:
        (A) You will have access to the result data immediately after the "Execute" line,
            in the same code segment where you have "executed" your remote function call.
        (B) You can either keep the result object for as long as you want and free it manually
            when using "Execute(FALSE)", or ... you can let the TRtcClientModule component free
            the result object automatically for you by using "Execute" or "Execute(TRUE)".

      You can also specify a timeout (in seconds) how long you are ready to wait for a result.
      Should you get no result in your specified time period, request will be cancelled and an exception will be raised.

      And ... if you are using a MultiThreaded connection and you do NOT WANT paint and other non-user messages to
      be processed while waiting for the result, call the "Execute" method with "AllowMessageProcessing=FALSE".
      For example: Execute(True, 30, False); // Auto free result, wait max 30 seconds, do NOT allow message processing.

      Also take a look at the "LastResult" property, which is available to you if you call Execute with
      its default value for the AutoFreeResult parameter (TRUE) and gives you direct access to the result
      object received from the Execute method without having to use local variables or the "with" statement.

      WARNING: Because of its blocking implementation, "Execute" method can NOT be used from events triggered
      by the RTC SDK (for example, from the OnResult or OnAbort events triggered by a remote call issued by
      using the "Call" method) and ... the "Execute" method can ONLY be used from one thread at a time.

***  property LastResult:TRtcValue read FExecuteResult;

      After the last "Execute" call, if you have used the default value (TRUE) for the "AutoFreeResult" parameter
      ("Execute" or "Execute(True)") to signal the component that you do NOT want to keep the result object to yourself
      and that you want the result object to be freed automatically by the component before the next "Execute" call,
      you can use the "LastResult" property to access the last object returned from the "Execute" method.

      Please note that the "LastResult" property will point to the result returned from the LAST "Execute" call.
      "LastResult" will return NIL if there was an error during the last Execute call (the call was not finished)
      or if you have last used Execute(FALSE) -> using "AutoFreeResult=FALSE" in your Execute call parameters.


Here are three code usage examples, all of which are basically doing the same thing, but each using a different syntax ...

*** "Execute" example 1 ...

      with MyClientModule do
        begin
        Prepare('myfunctionname');
        Param.asString['par1']:='Hi';
        Param.asInteger['par2']:=12345;
        Execute;
        // use the "LastResult" property to access the result data
        end;

*** "Execute" example 2 ...

      with MyClientModule do
        begin
        with Prepare('myfunctionname') do
          begin
          asString['par1']:='Hi';
          asInteger['par2']:=12345;
          end;
        with Execute do // result object will be Freed automatically
          begin
          // ... access the result data here ...
          end;
        end;

*** "Execute" example 3 ...

      with MyClientModule do
        begin
        Data.Clear;
        with Data.newFunction('myfunctionname') do
          begin
          asString['par1']:='Hi';
          asInteger['par2']:=12345;
          end;
        myRes:=Execute(False); // you will need to Free "myRes"
        try
          .. use myRes ...
        finally
          myRes.Free;
          end;
        end;

Best Regards,
Danijel Tkalcec