Categories
Android devices News Source Code

WebBroker on Android and Raspberry Pi 3

I covered this previously in a few webinars and presentations, but never published the source code for WebBroker on Android. To be honest I hadn’t tested it with C++Builder before, but I completely expected it to work, and it did. I also updated the tests on Emteria.OS (FKA RTAndroid) and it also works there.

The process of porting a Delphi or C++Builder WebBroker project to Android is pretty straight forward, but I’m publishing the code anyway. You create a Windows FMX WebBroker project, then copy all the code into a regular FireMonkey project. You will need to copy a few files from the RTL folder locally so you can reference them since they aren’t included in the Android package.

  • Web.WebReq.pas
  • Web.WebBroker.pas
  • Web.WebConst.pas
  • IdHTTPWebBrokerBridge.pas
  • IdCompilerDefines.inc
  • For C++Builder you also need
    • Web.WebReq.hpp
    • Web.WebBroker.hpp
    • Web.WebConst.hpp
    • IdHTTPWebBrokerBridge.hpp

Here are the links for the Delphi and C++Builder projects. They were built and tested in with 10.3.1 Rio. I also compiled some updated details on how to build the project and how to install and test on Emteria.OS.

I mention this in the slide deck, but officially WebBroker isn’t supported on Android. I tested it, and it seems to work, but if you run into an instance where it doesn’t work as expected, then you are on your own. Please don’t contact support and tell them I said it should work. Thanks!

Previous webinars:

Delphi and C++Builder on Raspberry Pi and SBC

Slides

Revisiting Raspberry Pi, Android and the SBC

Slides

See also: Targeting Chrome OS with Delphi via Android and Linux

Categories
News Source Code

Unexpected Benefit of Inline Variables: Conditional Blocks

Inline variables is one of the cool new feature coming in 10.3. The obvious huge use case is loop control variables, but I just discovered another great use case while reviewing some code. 

procedure DoesSomething;
var
  var1, var2: Integer;
begin
  // use var1
  {$IFDEF Something}
  // use var1 & var2
  {$ENDIF Something}
end;

This is a pattern I see a lot, and it generates a hint on var2 being unused based on the current compiler directive status.

[dcc32 Hint] myUnit.pas(123): H2164 Variable 'var2' is declared but never used in 'DoesSomething'

Now there are a number of ways to deal with this with more compiler directives, which is what I’ve done in the past, but I never like adding more compiler directives. It makes the code way more complicated and harder to maintain. Now with Inline Variables I can simplify it, make it easier to maintain, and hande the hint! (all of which makes me so happy!)

procedure DoesSomething;
var
  var1: Integer;
begin
  // use var1
  {$IFDEF Something}
  var var2: Integer;
  // use var1 and var2
  {$ENDIF Something}
end;
Happy dance commencing in T-minus 10 seconds. 

What are some interesting ways you see inline variables benefiting you?

Categories
Cloud FireDAC REST Source Code

API Limits with #FDEC

API Limits with FireDAC Enterprise ConnectorsThe FireDAC Enterprise Connectors (#FDEC) by CData and Embarcadero make it really easy to work with various APIs just like you would any SQL database. For example if you want to publish the results of a query to a Google Sheet (which I find incredibly useful) then it is just a few FireDAC components and you are off to the races. You might run into an API limit though.

What is an API limit? Most rest services have a limit to how often a client can call a specific API within a certain amount of time. Google calls this their usage limit:

This version of the Google Sheets API has a limit of 500 requests per 100 seconds per project, and 100 requests per 100 seconds per user. Limits for reads and writes are tracked separately. There is no daily usage limit.

That may seem like a lot, but I found I was running into that limit pretty quick once I moved my project into production. Luckily FireDAC and the FireDAC Enterprise Connectors have a simple workaround: Batch Processing.

Using the Array DML features of FireDAC you can batch multiple DML (Data Manipulation Language) operations into a single API call. The FDEC Google Sheets documentation from CData doesn’t cover Array DML, but the component supports this (they are updating the documentation). The Elasticsearch documentation does cover Batch Processing with an example, and I’ve used this with Sheets and it works great!

Bulk Insert

The following example prepares a single batch that inserts records in bulk.

FDConnection1.ResourceOptions.ServerOutput := True;
FDQuery1.SQL.Text := 'insert into Account values (:Name, :Id )';
FDQuery1.Params.ArraySize := 100;
FDQuery1.Params[0].AsStrings[0]:= 'MyName1';
FDQuery1.Params[1].AsStrings[0]:= 'MyId1';

//next statement
FDQuery1.Params[0].AsStrings[1]:= 'MyName2';
FDQuery1.Params[1].AsStrings[1]:= 'MyId2';
...

FDQuery1.Execute(FDQuery1.Params.ArraySize);
ShowMessage(IntToStr(FDQuery1.RowsAffected));

To retrieve the Ids of the new records, query the LastResultInfo#TEMP table:

sName := FDQuery1.Open('SELECT * FROM [LastResultInfo#TEMP]');

Bulk Update

The following example prepares a single batch that inserts records in bulk.

FDQuery1.SQL.Text := 'update Account set Name = :Name WHERE Id = :Id';
FDQuery1.Params.ArraySize := 100;
FDQuery1.Params[0].AsStrings[0]:= 'Floppy Disks';
FDQuery1.Params[1].AsStrings[0]:= 'Id1';

//next statement
FDQuery1.Params[0].AsStrings[1]:= 'Jon Doe';
FDQuery1.Params[1].AsStrings[1]:= 'Id2';
...

FDQuery1.Execute(FDQuery.Params.ArraySize);
ShowMessage(IntToStr(FDQuery1.RowsAffected));

Bulk Delete

The following example prepares a single batch that inserts records in bulk:

FDQuery1.SQL.Text := 'delete Account where Id = :Id';
FDQuery1.Params.ArraySize := 100;
FDQuery1.Params[0].AsStrings[0]:= 'MyId1';

//next statement
FDQuery1.Params[0].AsStrings[1]:= 'MyId2';
...

FDQuery1.Execute(FDQuery.Params.ArraySize);
ShowMessage(IntToStr(FDQuery1.RowsAffected));

If you want to learn more about Array DML check out these videos:

Array DML Skill Sprint with Pawel Glowacki

FireDAC in Depth with Cary Jensen

Also check out Cary Jensen’s book on the topic of FireDAC in Depth.

Categories
Source Code Tools

Use the Source!

One of the great things about Delphi is not only is it written in Delphi (mostly), but it ships with the VCL, RTL, & FMX source code. You can use this source code in lots of different ways, which I’ll cover in a bit, but sometimes it is a matter of finding the source file with the code you want. There are over 2,234 Delphi source files in the source folder, so it can take a while to find the right file if you don’t know where to look. Not to mention the 1,711 C files, and a few thousand other assorted files.

Source Folder Files

I used to use various GREP and full text searching tools to find the code I was looking for, but then I realized Windows 10 (and earlier versions) has a search function built into it, but you need to make a few configuration changes to use it effectively. So I thought I would outline those for you.

Your source folder is usually located in
C:\Program Files (x86)\Embarcadero\Studio\19.0
but it might be different depending on your installation. I usually Pin it to Quick Access, which makes it easy to access since I find I’m there a lot.

Source Folder

Windows Search and Indexing Options

The next thing to do is configure your Indexing Options. This is what makes it quick to search for files you need. You can find Indexing Options in Control Panel, or just runcontrol.exe srchadmin.dll.

control.exe srchadmin.dll

There are two steps to get the most out of Windows search for your source code. The first is you need to activate Windows search for the file extensions you want, and the second it to tell it to index your folders.

Indexing Options

To add PAS files to indexing click Advanced and go to File Types. You can just type PAS in the list of file types and it will find it for you. You don’t need to add a new extension. PAS files are already listed, but just not indexed by default. Just put a check next to it, and change the “How should this file be indexed?” to “Index Properties and File Contents” that will index all the text contents of the file

Indexing File Types

By default all your user folders are indexed, but Source is in your Program Files folder, so you need to explicitly add it. From the main Indexing Options window click Modify and from there you can add any folder you want to search.

Indexed Locations - Source

It takes a while a while to build and update the index after this change. You just gave it 2,000 more files to index. It has to read all the text out of them, which takes a while. Once it is done then just go to your source folder and use the search box to quickly find the code you are looking for.

Search Results

Everything Search

Everything Software IconAnother great tool to use, to search by file name is the Everything desktop search engine by Voidtools. The thing I love about using Everything is it searches all the file names on your system quickly. The difference between it and Windows Search is Everything only searches the file names, but it searches all the file names.

Everything Search

I find I usually remember the name of a file, but not exactly where I left it. This makes Everything indispensable for me.

Using the Source

So how useful is it that Delphi, RAD Studio and C++Builder includes all this source code? What all can you do with it?

  • Finding that function – You know how I said I can remember the name of a file, but not where I put it? I do the same things with classes and functions. I can remember the name of the method or class, but not which unit it is in. Being able to search all of the source instantaneously to find the source file you need is a huge help.
  • Learning – Just reading source isn’t the best way to learn to program, but having access to the source code is a great way to gain a deeper understanding.
  • Debugging – Sometimes your program doesn’t behave the way you expect it to. When all you have is a blackbox then you are unable to figure out what is happening behind the scenes. When you have the source code you can not only see how that method is implemented, but also debug into the source code, using all the great debugger features.
  • Adding New Features – Maybe there is a new API that was just released, or you need to access an obscure API or 3rd party feature. Since you have access to all the source you can see how other similar APIs are accessed and use that as a roadmap.
  • Fixing or Changing Functionality – One man’s bug is another man’s feature. Sometimes there are bugs you need to fix in the shipping source code, other times you just want to change the way things work. For small changes I just copy the source file out into my project directory. Then my code will use my modifications instead of the original implementation. While you can’t redistribute changed source code, you can compile the code into your program. If you do need to share your changes or fixes you can share a change-set that only contains your changes.

What else do you use the source for?

Categories
Funny Source Code

3D Credits Scroll with Delphi

A little fun with Delphi for today’s Star Wars day!

3D Credits Scroll with Delphi - May The Fourth

This is a pretty simple 3D form with a 3D layout at an angle, and then a 2D layout with text and images is animated up the 3D layout. The only code is populating the labels as the animation kicks off automatically.

3D Scroll - MayTheFourth-StructureDownload the code and run the full credit scroll, or change the message to share with your friends. It is FireMonkey, and while I only tested it on Windows, it should work on Android, iOS, macOS, and even Linux if you are running FMX Linux.

CreditScroll3D Source for 10.2.3 Delphi

May the Fourth be with You!

Categories
Source Code

Advanced HTTP Hacking Webinar Code

You can find all the code from my HTTP webinar in my special HTTP folder.

[Project source code] [YouTube Replay]

This is the script I use to demonstrate HTTP Range requests through Telnet. Just copy and paste each block of code (including the trailing blank line) into a command window and it will run telnet and make the HTTP request. You can view the test file here. Read HTTP Status Codes (including 418), Methods, Headers, and Access Control (CORS).

telnet delphi.org 80
HEAD /http/httptestfile.txt HTTP/1.1
Host: delphi.org
Connection: close
telnet delphi.org 80
GET /http/httptestfile.txt HTTP/1.1
Host: delphi.org
Connection: close
telnet delphi.org 80
GET /http/httptestfile.txt HTTP/1.1
Host: delphi.org
Range: bytes=0-77
Connection: close
telnet delphi.org 80
GET /http/httptestfile.txt HTTP/1.1
Host: delphi.org
Range: bytes=115-154
Connection: close
telnet delphi.org 80
GET /http/httptestfile.txt HTTP/1.1
Host: delphi.org
Range: bytes=78-113
Connection: close
telnet delphi.org 80
GET /http/httptestfile.txt HTTP/1.1
Host: delphi.org	
Range: bytes=115-154,127-127
Connection: close

This last one stopped working because of a change on the web server.

Here are the images. They all are available as both JPG and BMP and are 640×472 in resolution.


original.jpg

blue.jpg

green.jpg

red.jpg

purple.jpg

teal.jpg

yellow.jpg

Here is the code I used to stream all 7 images into one image. HTTPClient is a TNetHTTPClient and HTTPReq is a TNetHTTPRequest on the form. The reason it uses Bitmap images is that they are an uncompressed stream of pixel data, so are easier to recombine into one image.

const
  baseurl: string = 'http://delphi.org/http/';
  files: array[0..5] of string = ('red.bmp','green.bmp','blue.bmp',
    'yellow.bmp','purple.bmp','teal.bmp');

procedure TForm34.Button1Click(Sender: TObject);
var
  resp: IHTTPResponse;
  chunk: Integer;
  mem: TMemoryStream;
  I: Integer;
begin
  resp := HTTPReq.Head(baseurl + 'original.bmp');
  chunk := resp.ContentLength div 12;
  ProgressBar1.Max := resp.ContentLength;
  resp := HTTPReq.Get(baseurl + 'original.bmp');
  mem := TMemoryStream.Create;
  try
    mem.LoadFromStream(resp.ContentStream);
    for I := 5 to 11 do
    begin
      ProgressBar1.Tag := chunk*i;
      if I < 11 then
        httpreq.CustomHeaders['Range'] := 'bytes=' + IntToStr(chunk*i) +'-' + IntToStr(chunk*i+chunk-1)
      else
        httpreq.CustomHeaders['Range'] := 'bytes=' + IntToStr(chunk*i) +'-';
      HTTPReq.MethodString := 'GET';
      button1.Text := files[i mod 6];
      HTTPReq.URL := baseurl + files[i mod 6];
      resp := HTTPReq.Execute();
      mem.Position := chunk*i;
      TMemoryStream(resp.ContentStream).SaveToStream(mem);
    end;
    Image1.Bitmap.LoadFromStream(mem);
  finally
    mem.DisposeOf;
  end;
end;

 

 

Categories
REST Source Code Tools

FireDAC MemTable Editing – New in 10.2 Tokyo

One of my favorite new features (beyond Linux) is the ability to edit the data in a TFDMemTable at design time. You could already edit the field defs, to set it up. This makes the TFDMemTable, which was already super flexible and powerful, even more flexible. You can use it to capture some small data at design time and then use visual live bindings or data binding to connect it to visual controls.

Edit TFDMemTable

Holger Flick has a great blog post on the topic too. Here is a video I made with Sarina that shows it in action.

What is your favorite new feature in Tokyo?

Categories
Source Code

How to Reference a Procedure

It was pointed out in the comments and on G+ that this covers type aliases, but not procedure aliases. Instead it shows how to have a variable that references a procedure and then allows you to call it by this new variable. While I used alias as meaning “reference by a different name” it does in fact have a specific definition in this context. Sorry if there was any confusion or undue frustration at imprecise terminology.

I just answered a question about how to alias a procedure. I thought it was interesting enough to share here.

It is easy enough to alias a type . . .

type
  my_integer = integer;

then we can just use my_interger in place of integer as a type. But what about a procedure (or function for that matter)?

There are two different ways, depending on if the procedure is a member of a class. For straight up procedural procedures and functions it looks a little something like this:

procedure HelloWorld; // declare our procedure
begin
  ShowMessage('Hi');
end;

var
  my_proc: Procedure; // declare our alias

begin
  my_proc := HelloWorld; // assign the alias
  
  // ...

  if assigned(my_proc) then // verify the reference
    my_proc; // call the alias
end;

This is pretty straight forward. We just create an alias reference variable of the right type, and assign it to the procedure we want to alias reference. If you call an alias reference variable that is unassigned you will get a null reference access violation.

You can also streamline it a little like this

var // or as const
  my_proc: procedure = HelloWorld;

Then you know it is assigned. I guess this would be useful if you want to alias reference a procedure declared in a different unit.

This works the same for functions or procedures with parameters.

procedure Hello(name: String);
begin
  ShowMessage('Hello ' + name);
end;

function Nine: integer;
begin
  Result := 9;
end;

var
  argumentative: procedure(s: string) = Hello;
  number: function: integer = Nine;

Notice that the name of the argument doesn’t have to match, but the number, order and types do. If they don’t then you will get the error

E2009 Incompatible types: ‘Parameter lists differ’

Now what if you are dealing with procedures or functions that are members of an object? If you try to assign them to the above types you will get the error

E2009 Incompatible types: ‘regular procedure and method pointer’

And that is because members of an object are method pointers. Fear not, you can handle them with just a slightly different type declaration:

type
  TMethod = procedure of object;
  TFunc = function: integer of object;
  TNotifyEvent = procedure(Sender: TObject) of object;

In this case they are declared as types with “of object” added to the end. This indicates that they are procedures of an object. AKA members or method pointers.

You can read more about procedural types and method pointers in the DocWiki.

Why would you want to do any of this? First of all, the method pointer is how the VCL & FMX handles dependency injection through event handlers. This is also really useful when combined with anonymous methods and the parallel programming library.

Categories
DataBase Source Code

New Delphi Seattle MongoDB Sample

I created some more Delphi 10 Seattle samples to show off MongoDB and FireDAC functionality: LocalSQL, Indexing & Geospatial.

FireDAC MongoDB NoSQL

The first one queries some data from MongoDB allowing you to specify the match, sort and projection, then it stores the results in a DataSet. At that point you can use LocalSQL to write a SQL query against the result set. While FireDAC gives you full native support for MongoDB, it also puts the SQL back into NoSQL.

MongoDB FireDAC LocalSQL

Indexing is used to improve your query performance. It is really easy to work with MongoDB queries with FireDAC.

MongoDB FireDAC Indexes

And one of the cool features of MongoDB is that you can do spatial queries. Here is an example that shows how to create a Spatial index and then do a spatial query with FireDAC. This uses the restaurant data that is included with the shipping samples, so make sure you load the restaurant data first.

Geospatial MongoDB FireDAC

If you missed my previous post I had a MongoDB FireDAC and C++Builder sample.

[You can download my new samples here.]

Categories
News REST Source Code

CORS on DataSnap REST Server

Cross-origin resource sharing (CORS) is a mechanism that enables resources to be shared across domains. Typically this isn’t allowed to prevent security issues. To enable on your DataSnap REST server you can use the following code per MVP Nirav Kaku from India.

All you need to do is add a custom header in the Response before dispatching the result on the DataSnap server…

procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  //allows cross domain calls
  Response.SetCustomHeader('Access-Control-Allow-Origin','*');
  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;

It is useful for DataSnap server developers who want
their REST calls to be supported via AJAX using JavaScript from a
different server.

[Reference]

Note: CORS is security feature of the browser so there could be some
dependency there. Tested with Firefox, Chrome and IE and it seems to
be working fine.