Showing posts with label ICS. Show all posts
Showing posts with label ICS. Show all posts

September 29, 2021

ICS V8.67 adds Delphi 11 and OpenSSL 3 support

ICS V8.67 has been released at: http://wiki.overbyte.eu/wiki/index.php/ICS_Download

ICS is a free internet component library for Delphi 7, 2006 to 2010, XE to XE8, 10 Seattle, 10.1 Berlin, 10.2 Tokyo, 10.3 Rio, 10.4 Sydney and 11.0 and C++ Builder 2006 to XE3, 10.2 Tokyo, 10.3 Rio, 10.4 Sydney and 11.0. ICS supports VCL and FMX, Win32, Win64 and MacOS 32-bit targets. The distribution zip includes the latest OpenSSL 1.1.1i win32, with OpenSSL 3.0 and Win64 versions of OpenSSL being available from the download page.

Changes in ICS V8.67 include:

1 - Added support and packages for RAD Studio 11.0.  Updated SSL/TLS root certificate bundles, old certificates gone, new ones added, nothing major.

2 - Added support for OpenSSL 3.0 which is a major new release, primarily a lot of internal changes to ease long term support. There is an optional FIPS module with 3.0 but not available here since our DLLs are not built to the standards required for certification. The old engines for special extensions are replaced by new more versatile providers of which the FIPS module is one, a provider legacy.dll has obsolete ciphers and hash digests, including MD2, MD4, Blowfish, DES, IDEA, RC2, RC4, SEED, that most applications no longer need and which needs to loaded by the application by setting global variable GSSLEAY_LOAD_LEGACY to true before loading OpenSSL.

3 - OpenSSL 3.0 does not offer any specific new features of benefit to ICS at present, although HTTP/3 support is planned for 3.1 or later, so the main ICS distribution retains OpenSSL 1.1.1i which is fully supported until September 2023. OpenSSL 3.0 may be downloaded from the download page. There are two global variables to restrict which OpenSSL version is loaded, GSSLEAY_DLL_IgnoreNew set true will ignore 3.0, while GSSLEAY_DLL_IgnoreOld will ignore 1.1.1, if both sets of DLLs are available in the same directory. The main SSL samples all set these globals, which can be changed for  testing one version or the other, or set by the application, but must be before OpenSSL is initialised.

4 - The main implication for ICS with OpenSSL 3.0 is for SSL/TLS certificate private keys saved with password protection, which is required for PKCS12 certificates for importing into the Windows certificate store. The new PKCS12 default password encryption AES256 is not recognised until Windows Server 2016 v1709  and Windows 10 v1709, so Server 2012, Windows 10 RTM and earlier won't load AES passworded keys, only 3DES, for which the legacy.dll must be loaded.

5 - The TX509Base class has various improvements. The ValidateCertChain method reports CA roots for multiple certificate verification paths with two or more intermediate certificates, rather than only the last. The CertMainInfo method provides a single line with the main certificate information.

6 - There are two new classes to write and read SSL/TLS certificates to and from the Windows Certificate Store, including private keys. This is primarily so Let's Encrypt certificates can be installed automatically for use with the IIS web server.  TMsX509List descends from TX509List adding a method LoadFromStore to load the list from a Windows certificate store by store name TMsCertStore and location MsCertLocation. For My/Personal store, attempts to load private keys if they are allowed to be exported unencrypted. TMsCertTools descends from TSslCertTools adding methods SaveToStorePfx and LoadFromMyStore to access Windows certificate stores. Note access to the Local Machine Store for web server certificates requires administrator rights.

7 - Various improvements for the OverbyteIcsPemTool sample.  It includes new buttons to list the  contents of Windows certificate and private key stores and allow old items to be deleted.  This may be useful for cleaning up old certificates and private keys from the Windows stores. Added ResavePrivateKey and Resave Private Key menu option which prompt for a PFX or PEM file containing an encrypted  private key with a new cipher, renaming old file to .oldpem/pfx.  Specifically for files saved with old ciphers than OpenSSL 3.0 does not support as standard if required for older versions of Windows.  Displaying certificates and bundles is no longer a new modal window, but updates the existing log window. Improved import certificates from Windows certificate store to use TMsX509List instead of Windows API calls, and to access all Windows store locations instead of just user, specifically the Local Machine store where server certificates are located.

8 - For the TX509Certs component, the default cipher for encrypting PFX/P12 files is now PrivKeyEncAES256 with 3.0 unless the legacy DLL is loaded when still PrivKeyEncTripleDES so older versions of Windows can load them. Changed extraction of download PEM bundle so that main certificate does not need to be first in file, log them all, and ignore any self signed root certificates. If testing dns-01 challenge fails, rotate to next public server and three retries (previously only happened on timeout). When saving files with private keys, log encryption type used. Added more certificate output formats, OutFmtPwPem and OutFmtPwP12 specify whether to password PEM and P12/PFX private keys. Note Windows always needs passworded P12/PRX files, while Apache web server only accepts PEM files without a password. Allow automatic installation of new certificates to the Windows Certificate Store so they can be used by IIS web sites, by setting output format to OutFmtWinStore.  Note application must have administrator rights to do this.

9 - Fixed two problems in the FTP client, support option ftpFixPasvLanIP for PUT/APPE uploads as well as  downloads, and support IPv6 for PUT/APPE uploads as well as downloads.

10 - Fixed a problem in TIcsMailQueue with sequential number generation to avoid file locking errors and unicode BOM corrupting file, generate large random number for errors instead of reverting to 1.  Don't save BOM withunicode compilers.

11 - In the Application Web Server TSslHttpAppSrv, added an optional LastModified parameter to the AnswerStream, AnswerPage, and AnswerString methods to avoid adding a custom header line with the date.  Added NO_CACHE_EX and NO_STORE_EX literals. Added PUT and DELETE verb handlers, similar to GET and POST.

12 - For the HTTP client TSslHttpCli, fixed a relocation problem where the Location: header included a path with a space, encode the space. Fixed another relocation problem where HEAD sometimes stalled. Remove # fragment or anchor from URL in relocation, only used by browsers and not by servers.

13 - In the TIcsBlackList component, Internally use BlockedFlag instead of setting attempts to 9999 once the actual maximum failed attempts is reached, so we can keep counting attempts.

14 - Added a new SSL sample, OverbyteIcsDDWebService.dpr which is very similar to OverbyteIcsSslMultiWebServ.dpr, but designed as a Windows service, although it will also run as a GUI for debugging.  It requires DDService service framework to be installed from https://www.magsys.co.uk/delphi/ddservice. asp. It also includes a REST server with simple lookup responses from a SQL
database, which optionally requires DISQLite3 5.36.5 or later to be installed from http://www.yunqa.de. Note this sample in not in the project groups due to these pre-requisites.

15 - Moved TRestParams from the OverbyteIcsSslHttpRest unit to OverbyteIcsUrl to ease circular references. Added a new method AddItemNULL to add a null, in Json this will be unquoted. Added a new TRestParamsSrv component which provides methods for creating REST server Json responses from a SQL database resultset, one or more rows, also error responses. Note this is only compiled if DATABASE is defined in OverbyteIcsDefs.inc to avoid bringing in database units that are not available on all Delphi editions.  There is a REST server sample OverbyteIcsDDWebService.dpr that illustrates SQL lookups.

16 - In the proxy component TIcsHttpProxy, don't send an HTTP request header until after HTTP body has been processed in case the body length changes. HTTP Forward Proxy using HTTP works again, broken in V8.65. Using HTTP Forward Proxy, convert absolute URL to path only since some servers can not process an absolute URL and sulk.

17 - In the Jose unit, rewrote the functions converting private keys to and from Json Web Keys with new OpenSSL 3.0 provider functions. Use AnsiStrings and functions when dealing with binary data to avoid possible issues with string conversions and nulls.  Json now created with TRestParams.

18 - Added two new sample project groups, OtherDemos64 and SslDemos64 which include Win64 versions of all the main active samples with 64 added to the project name, so they can be regularly built alongside the Win32 versions without changing platforms and overwriting executables.

 

December 19, 2020

ICS V8.65 announced

 ICS V8.65 has been released at: http://wiki.overbyte.eu/wiki/index.php/ICS_Download

ICS is a free internet component library for Delphi 7, 2006 to 2010, XE to XE8, 10 Seattle, 10.1 Berlin, 10.2 Tokyo, 10.3 Rio and 10.4 Sydney, and C++ Builder 2006 to XE3, 10.2 Tokyo, 10.3 Rio and 10.4 Sydney. ICS supports VCL and FMX, Win32, Win64 and MacOS 32-bit targets.

The distribution zip includes the latest OpenSSL 1.1.1i win32, with other versions of OpenSSL being available from the download page.

Major Changes in ICS V8.65 include:

1 - The ReadMe8.txt file has a new 'Getting Started with ICS' section listing the types of projects ICS may be used for, and suggesting the correct components to use, and their related sample applications for  testing.  This is recommended reading for anyone doing new ICS development since it discusses all the new high level components like TSslHttpRest added in the last few years which can reduce development effort considerably.  It may also be viewed at: http://wiki.overbyte.eu/wiki/index.php/ICS_Getting_Started

2 - Added new TIcsRestEmail component that provides basic support for Google and Microsoft Outlook email REST APIs including OAuth2 login and refresh to get an access token for SMTP and POP3 XOAuth2 and 0AuthBearer authentication. TIcsRestEmail has methods to send and read email, to list IDs in a mailbox, read headers and message bodies by ID, send emails and delete emails.

3 - The SMPT, POP3 and MailQueue samples all now support XOAuth2 and 0AuthBearer authentication using the TIcsRestEmail component. The low level component call an event to get the authentication access token, which is provided by IcsRestEmail, together with a refresh token which is saved  instead of a password.

4 - To access email using REST APIs or OAuth2/SMTP/POP3 an 'application account' needs to be created though the Google or Microsoft provider console.

5 - Added a new TIcsTwitter component and sample, requires a developer account from Twitter. Includes login to Twitter, send tweet, search tweets and get specific tweets, all responses are Json which the application needs to untangle.

6 - Improved TRestParams allowing them to save more Delphi types correctly without conversion to strings, and to save parameters in new formats.

7 - There are various OAuth2 improvements to make it easier to implement. Added several TOAuthUri records designed to set-up common OAuth2 account  settings for providers like Google, Twitter, Microsoft  and Sipgate, by using the LoadAuthUri method.

8 - TSimpleWebSrv continues to get less simple, it has aWebSrvIP2 property for a second address so it can listen on IPv4 and IPv6 at the same time, with and without SSL if necessary.  Setting WebSrvIP to localhost sets both 127.0.0.1 and [::1] so the browser OAuth2 redirect can choose IPv4 or IPv6.

9 - Made some improvements to SuperObject, used for Json creation and parsing.  When parsing Json there are new functions that return a sensible error message about parse errors and the location.  There is a new object type DateTime or DT which reads or writes TDateTime to avoid the application needing to do the ISO string conversion. Json can now be parsed to a depth of 64 levels.

10 - Rewrote and improved the way ICS reads SSL/TLS certificates and bundles, simplifying code that has got partly duplicated over the years as new methods were added, and improving error handling so the infamous stack error should no longer appear, instead more useful messages.  All certificate files are now written with the UTF8 character set for the added comments that may include non-ASCII characters.

11 - All the ICS root bundles are now created cleanly by an application, rather than mostly manually by copy and editing, to reduce errors. This fixed four corrupted root certificates in the older bundles, see http://wiki.overbyte.eu/wiki/index.php/FAQ_SSL/TLS_Certificate_Authority_Root_Stores

12 - Previously the Jose unit offered functions primarily for client JWS/JWT REST applications, it now includes extra functions for REST servers to check and verify the JWS/JWT sent by clients.  The Jose sample application has new tests for JWS/JWT, and to parse and display Json.

13 - Added a new Multi Host FTP Server sample using IcsHosts. Really designed to be a Windows service application. It supports multiple SSL hosts with multiple listeners, can order it's own SSL certificates and will create self signed certificates for any missing,  and will email status information and errors to an administrator.

14 - Fixed a long term external SSL session cache issue in some components and samples that meant if an SSL handshake fails due to a bad certificate or chain, it is necessary to remove the SSL session from cache so an immediate retry does not succeed by skipping the certificate checks. This is only a short term issue, because the cache is usually cleared after a few minutes. This will effect any client applications using the external SSL session cache including HTTPS.

15 - Increased the TCP send and receive buffer size to 64K in all components and samples, and generally don't allow it to be set lower.  Unfortunately the default buffer size never kept up with faster internet speeds which meant some components transferred data slowly.

16 - Made some improvements ordering SSL/TLS certificates. Made Windows Server DNS updating using WMI more robust so wild card Acme orders work reliably.

17 - Made some internal changes loading OpenSSL, to avoid the two DLLs being loaded from different directories and to give better exceptions if they are missing.

More detailed release notes are at: http://wiki.overbyte.eu/wiki/index.php/ICS_V8.65

To get help, use the forum:  https://en.delphipraxis.net/forum/37-ics-internet-component-suite/


March 26, 2019

ICS V8.60 has been released

ICS V8.60 has been released at:

http://wiki.overbyte.eu/wiki/index.php/ICS_Download

ICS is a free internet component library for Delphi 7, 2006 to 2010, XE  to XE8, 10 Seattle, 10.1 Berlin, 10.2 Tokyo and 10.3 Rio, and C++Builder 2006 to XE3, 10.2 Tokyo and 10.3 Rio. ICS supports VCL and FMX, Win32, Win64 and MacOS targets.

The distribution zip includes the latest OpenSSL 1.1.1 win32, with other versions of OpenSSL being available from the download page.

Major changes in ICS V8.60 include:

1 - Added several new components and sample applications created by Magenta Systems Ltd over the past 17 years and previously distributed and installed separately to the ICS distribution.  Bundling them with ICS makes installation and updating easier, and allows existing ICS samples to make use of many the new components, such as UTF-8 file logging. All the components have new names so existing applications using the originally distributed versions will still work, but it's recommended updating existing applications for the new ICS versions. The added components include IP stream logging, SMTP Mail Queue, Time Server and Client,  Whois client, blacklisting of malicious IP addresses, file logging functions, file indexing, copying and deleting, FTP indexing, multiple file uploading and downloading, HTTP page parsing and URL downloading.

2 - In THttpCli, only follow relocation for 3xx response codes, not 201 Created, but keep Location property for 201 which is often response to a POST and may be needed by the application.

3 - When starting TWSocket Connect, the IP address chosen for DNS lookup is now saved in ASCII as AddrResolvedStr which is useful in connect OK or failed events to see whether an IPV4 or IPv6 address was chosen, and which was used if DNS offered multiple IPs. AddrResolvedStr is exposed as a property in TWSocket, THttpCli, TSslHttpRest and TFFtpClient and reports in failed connection events.  Other clients will be added soon.

4 - Various samples have been updated to ease testing of IPv6 and to save the diagnostic window activity to a disk log file, OverbyteIcsHttpsTst, OverbyteIcsHttpRestTst, OverbyteIcsTimeTst, OverbyteIcsX509CertsTst.

5 - Added round robin DNS lookup if DNSLookup returns multiple IP addresses, so they are used in turn after a failure when a component is called repeatedly without being freed.  This is implemented in THttpCli,
TSslHttpRest TFFtpClient and TIcsTimeClient.  Other clients will be added soon.  There is a new OnSelectDns event to override round robin lookup and make your own choice.

By default, the DNS lookup in ICS components ignores IPv6 addresses and  always use the first IPv4 address offered, when there is more than one. This is usually implemented in the OnDnsLookupDone event in the application
or high level component.  So if that first address does not respond, the application never tries any other addresses.

This has become more of a problem when enabling applications for IPv6, by changing SocketFamily from the default sfIPv4, to sfAny, sfAnyIPv4 (prefer IPv4), sfAnyIPv6 (prefer IPV6) or sfIPv6 (only IPv6), when IPv6 addresses
may also be returned as well as IPv4.  Due to routing or firewall issues, IPv4 and IPv6 might not both be available and so connection will fail if that address is chosen first.  Previously it was necessary to restrict SocketFamily so only the working family is attempted.

The DNS round robin implementation relies on keeping the last successful connected IP address, so it can be re-used for subsequent connections, but looping through any alternative addresses if the last connection failed, for subsequent connection attempts. Most existing applications use the OnDnsDone event to select the connection IP address so the round robin code is added there.

Newer applications making use of TWSocket ComponentOptions wsoAsyncDnsLookup should added code in OnDNSLookupDone and update the DnsResult property which is then used by Connect.

6 - IcsHosts supports two new TSslSrvSecurity server security levels, sslSrvSecTls12Less and sslSrvSecTls13Only, the former disables TLS1.3 in ICS servers if TLSv1.3 fails (perhaps a bad client implementation) while the second only supports TLSv1.3.

7 - Up to date C++ packages are included for 10.2 Tokyo and 10.3 Rio. Information on installing ICS for C++ 2007 may be found at: https://en.delphipraxis.net/topic/844-use-ics-with-c-builder-2007/
Sorry, currently don't believe it's easy to update the ICS source code to avoid the changes needed for C++ 2007.
 
Full release notes are at: http://wiki.overbyte.eu/wiki/index.php/ICS_V8.60
 

March 24, 2019

New home for ICS support

ICS (Internet Component Suite) is used by hundreds of thousands developers around the world. After more than 20 years, ICS is still actively developped. Lastest version can be downloaded from ICS download page and from the subversion repository if you need the very latest update. And if you need easy download and install, get it from Embarcadero GetIT! directly from RAD Studio/Delphi IDE.

You may need help using ICS and for that purpose, a lot of demos are included in the distribution. Of course you may ask the community using the web forum at Delphi-PRAXiS. This is an English forum.

Enjoy!
François PIETTE
Embarcadero MVP
ICS Author

January 2, 2016

DirWatch: Delphi 10 in action

In this article, I will show you how I used Delphi 10 Seattle to create a nice application aimed at watching a directory tree. Source code provided, see at the end.


Background:


I frequently use shared directories as repository for files. Some user on the network just drops the file in the directory where I can grab it. The user is supposed to notify me when he has dropped a file.  Unfortunately some user forgets to notify e and the file stay there for a long time before I notice it myself. In other situation, I like to know which files are affected by a given application or by a setup.


DirWatch will be of great help in those situation and many others. DirWatch will monitor a whole directory tree, even a full disk, for changes and send me an email when a change is detected. A change means a file or subdirectory is added / renamed / deleted / modified.


How it works:


Part 1: Getting notifications


Using Windows, it is actually very easy to be notified of changes in the file system. There is a specific and simple API for the purpose. The main API functions are FindFirstChangeNotification, FindNextChangeNotification and ReadDirectoryChanges. The actual work is done by Windows!


This notification API works much like listing a directory: you do a FindFirst, then if successful, you read the changes that occurred and then call FindNext to be notified for the next change. As simple as that.


OK, things can be a little bit more complex if you want your application to stay responsive you should resort to asynchronous Input/Output and multithreading. That code already exists on the web in numerous instances. I selected to use DWScript implementation because it is simple and efficient and also because I use DWScript for other project and find this open source product excellent. DWScript is available under Mozilla Public License Version 1.1.


dwsDirectoryNotifier is available from DWScript project (https://bitbucket.org/egrange/dwscript). Direct link to the source file at the time of writing is


Part 2: Sending an email


To programmatically send an email, you have many solutions available. In my opinion, the best solution is to directly address the email server, either company email server or Internet Service Provider email server. This may sound difficult but it is not much. An email server makes use of a protocol named SMTP, just like your browser make use of a protocol name HTTP. SMTP stands for Simple Mail Transfer Protocol and as its name implies, it is simple! SMTP is a text based protocol and involves the exchange of a few messages to send an email.


SMTP becomes a little bit more complex when you start talking authentication, compression, and security but it basically remains simple.


I have implemented the SMTP protocol in an open source freeware I published nearly 20 year ago. I mean the Internet Component Suite (ICS for short). See http://wiki.overbyte.be. ICS is a huge component library. We will only use the SMTP component.


ICS (Internet Component Suite) is available from http://wiki.overbyte.be/wiki/index.php/ICS_Download and also can be installed directly in Delphi 10 Seattle using Embarcadero GetIT (Delphi menu Tools / GetIi Package Manager, type ICS into the search box and select "ICS for VCL").


Part 3: be discreet


When I use a utility which I don't have to interact with, I like to have it very discreet. I don't even want to see it on my screen. There are two good solutions for this: write a service application or write a "tray-icon" application. I selected the second which is slightly easier to write and debug.


A tray-icon application is a normal Windows application which once minimized is only noticeable by its icon displayed in the systray (You know, this little area usually on the bottom right of the screen where the time is displayed).


Using Delphi 10, you create an application as a "VCL form application" and drop a TTrayIcon component on it. You have just two event handler to write: FormResize and TrayIconDoubleClick. FormResize is used to hide the form once it is minimized and TrayIconDoubleClick is used to make the form visible again when the user double clicks on the small icon.


There is a side problem when building a tray-icon application: you need a small 16x16 icon. If you don't create your own 16x16 icon, Windows will shrink the large icon to create the small one. The result is frequently horrible.


To create the multiple resolution icon file, I used IconMagick (http://www.imagemagick.org) command line utility "convert" to combine icons created using my favorite paint application.


convert DirWatchIcon32x32.ico DirWatchIcon16x16.ico DirWatchIcon.ico


Once DirWatchIcon.ico was created, I associated it with the application using Delphi project options dialog.


Please note that TrayIcon component dropped on the form must be loaded with the DirWatchIcon16x16.ico file otherwise window will use the 32x32 icon file resized to 16x16 which gives an horrible result.


Part 4: Work offline


I wanted to be notified even for changes occurring while DirWatch was not running. This is especially useful if the watched directory is not local but a shared resource on a file server.


When DirWatch starts, I create notifications for the changes occurred since last run by comparing the list of files actually on disk with the list of files that where on disk when DirWatch was stopped.


This means that at program startup, DirWatch enumerates all the files and directories in the watched directory. This could take some time if the directory contains millions of files!  On my hard drive with about one million files, it takes approximately 15 seconds to enumerate the files, compare that list with the previous list and send the email for the changes.


The implementation I made makes use of Delphi 10 Seattle generics. Internally the list of files is not a list but a dictionary. This gives fast access to each file data. Each file (Or directory entry) is stored with his name, attributes, size and timestamp. This is used to find out what has changed with a very low chance to miss something. Of course it could be possible to write a program which saves the timestamp of a file, update some bytes in the file without changing the size and then restore the timestamp. Actually this never occurs in real life!


It is interesting to look at the code I wrote for the dictionary. There are a few involved classes: TDirDictionary, TDirEntry, TDirDictionaryPair, TDirDictionaryObject and TDirDictionaryEnumerator.


I implemented TDirDictionary by delegation instead of inheritance. I frequently do that to have complete freedom about the interface I expose. In most cases, delegation is a better encapsulation that inheritance. Look at the source code for details and you'll quickly see the code is really very simple.


Part 5: Data persistence


DirWatch must be configured with some parameters. Of course, the directory which must be watched but also the SMTP server information (Those information are the same that Outlook uses for a classical POP3/SMTP account).


I also like that my applications remember where their window was and which size they had when I run the application again.


For all those purposes, I like to use the good old INI file. It is easy to use within the program and easy you me to edit with my favorite text editor. I store the INI file in the "Loacl AppData" folder in the user profile.  The Windows API function SHGetFolderPath is used to query the exact location of the "Local AppData" folder.


Part 6: Hide sensitive data


Sending an email may sometimes involve a usercode and password that the DirWatch must know. As explained above, those values are stored in the INI file and this is a problem if they are saved in clear text.


I wrote two functions Decrypt and EncryptRestore for the purpose. One trick is used to allow encryption without using a special program. When the data is enclosed in angle bracket in reverse order, then it's assumed not encrypted but needing to be encrypted. Then once encrypted the resulting value is saved between angle brackets.  So "]MyName[" will result in "MyName" and a flag is set so that EncryptRestore will produce "[Qyaze2==]" which is an encrypted value. If no angle bracket is used at all, then the value is assumed not encrypted. Here in this implementation I used very weak encryption: base64 encoding. In a fully secured application you should use a real encryption mechanism providing strong security.


Part 7: Command line options


Some information cannot be stored in the INI file and yet be changed at run time. For example the name and location of the INI file itself can be changed.
-i IniFileName        Select INI file
-h            Show this help
-m            Start minimized
-a AppName        Select application name
-c CompanyName    Select company name


To parse command line argument, I used code largely inspired by by code from Stehan Huberdoc  (http://stefan.huberdoc.at/comp/software/delphi/sandkasten.html) which is an implementation of the well-known GNU GetOpt command line parsing function (https://en.wikipedia.org/wiki/Getopt).


Part 8: Source code


Source code is split into two units: DirWatchMain.pas which is the main and only form of the application and DirWatchProcess.pas which contains the code which does the actual work without any user interface. Of course other units are used as explained above: some from ICS, some from DWScript and GetOpt.


Download links:




Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be


April 17, 2014

ICS updated for Delphi XE6

ICS has been updated for Delphi XE6. No new feature, just a simple update for the latest Delphi version. VCL and FMX supported for desktop platforms. Mobile still under way.

Download as usual. See http://wiki.overbyte.be


Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

November 28, 2013

What is ProcessMessages?

Sometimes, when you browse some application source code, you find loops calling Processmessages. What’s this? Well, it is a “Message pump” which retrieves and process messages from Windows message queues. More exactly from the current thread message queues.

Quick note: At the end of this article, you'll find why you should NOT use ProcessMessages. Before that, let's understand the concept.

The code you see is frequently like this:

FSomeFlag := TRUE;
while FSomeFlag do begin
    Application.ProcessMessages;
    Sleep(100);
end;

When looking at that code, you may think it will loop forever. Actually it will not! The loop variable “FSomeFlag” is for sure set to false in an event handler.

Application.ProcessMessages is implemented in Delphi runtime (Forms unit) as a loop checking Windows message queue for the current thread. The loop removes any message from the queue and dispatch processing, until the queue is emptied.

In the message queue, you have messages from the user interface (for example mouse move, button click, menu actions and much more) and from many activities occurring in the system such as inserting a removable disk, user wanting shutting down the computer, data received from a serial port, data packet received on the network, battery running out of charge and much more.

ProcessMessages will fetch all those events and process them. Translated to more common Delphi speaking, your event handlers will be called from ProcessMessages.

Try this:
Create a new VCL forms application, drop two buttons on the form and a memo. Declare a private Boolean member variable named “FSomeFlag”. Then write and run this code:

procedure TForm1.Button1Click(Sender: TObject);
begin
    FSomeFlag := TRUE;
    Memo1.Lines.Add('First message: Start looping, click on Button2');
    while FSomeFlag do begin
        Application.ProcessMessages;
        Sleep(100);
    end;
    Memo1.Lines.Add('Second message: Loop done');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    Memo1.Lines.Add('You clicked on Button2');
    FSomeFlag := FALSE;
end;

When running, click on Button1. This will execute Button1Click event handler which contains the loop. It will display the first message and then loop until FSomeFlag becomes FALSE.
After clicking on button1, then click on button2. This will execute Button2Click event handler which sets FSomeFlag to FALSE which in turn will break the loop still running in Button1Click event handler. You’ll see the message display by Button2Click event handler, followed by the second message in Button1Click event handler and you know the loop has ended.

This code has some surprising results. For example, click twice on Button1 before clicking on Button2. You will see the first message shown for each click on Button1. Then the message from Button2 when you click on it. And finally twice the second message from Button1Click event handler. What you see is the Button1Click event handler reentered. This is a king of recursive call. ProcessMessages will trigger all events, including new event of the same kind already executing.

Having you event handler reentered may have adverse effect on your code. You should probably avoid it by rewriting Button1Click like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
    if FSomeFlag then begin
        Memo1.Lines.Add('You already clicked Button1');
        Exit;
    end;

    FSomeFlag := TRUE;
    Memo1.Lines.Add('First message: Start looping, click on Button2');
    while FSomeFlag do begin
        Application.ProcessMessages;
        Sleep(100);
    end;
    Memo1.Lines.Add('Second message: Loop done');
end;

Checking FSomeFlag before the loop let us know that the loop is already looping and just exit instead of beginning a new loop.

There are more surprises: once you clicked on Button1, before clicking Button2, try to stop your application by clicking the close window button. It won’t stop!

The application doesn’t stop because VCL is written in such a way that an application is terminated when the main window closes. And that window cannot close while an event handler is still executing. Which event handler is execution? It is Button1Click which is still looping waiting for FSomeFlag to become FALSE. Click on Button2 and the loop will break, letting the window close and the application terminate.

It is likely that this behavior is unwanted. Easy to change. Rewrite Button1Click event handler like this:

procedure TForm1.Button1Click(Sender: TObject);
begin
    if FSomeFlag then begin
        Memo1.Lines.Add('You already clicked Button1');
        Exit;
    end;

    FSomeFlag := TRUE;
    Memo1.Lines.Add('First message: Start looping, click on Button2');
    while FSomeFlag do begin
        Application.ProcessMessages;
        if Application.Terminated then
            break;
        Sleep(100);
    end;
    Memo1.Lines.Add('Second message: Loop done');
end;

The check for Application.Terminated does exactly what it name implies. Actually “Terminated” is a flag which is set when you click on the form’s close button. This is exactly the same mechanism as the one we implemented with FSomeFlag.

Finally, there is one more glitch: what happens if the user never clicks on Button2? Answer: the application loops forever. This is probably unwanted behavior (Remember that is a real application the flag FSomeFlag is set to FALSE no when a user clicks on a button but when some condition is met, for example when a removable media is inserted).

To fix this behavior, we may add a timeout. There are several possibilities to implement it. You may get the time within the loop and break if too much time elapsed. Or you may use a TTimer for that purpose. The code looks simpler with a timer but is not really visible so I prefer to get the time within the loop like this:

procedure TForm1.Button1Click(Sender: TObject);
var
    Timeout : TDateTime;
begin
    if FSomeFlag then begin
        Memo1.Lines.Add('You already clicked Button1');
        Exit;
    end;

    FSomeFlag := TRUE;
    Memo1.Lines.Add('First message: Start looping, click on Button2');
    Timeout := Now + OneSecond * 10;
    while FSomeFlag do begin
        Application.ProcessMessages;
        if Application.Terminated then
            break;
        if Now > Timeout then begin
            Memo1.Lines.Add('Timeout');
            break;
        end;
        Sleep(100);
    end;
    Memo1.Lines.Add('Second message: Loop done');
end;

I have not explained yet why there is a Sleep(100) within the loop. If you remove it, everything seems to work the same way. Really? Not! use the task manager to see how much CPU is used by your application. Since it does almost nothing, task manager shows 0% of CPU used. If you click on Button1, you still see 0%.

Now remove Sleep(100) and test again. Task manager will show your application consuming a lot of CPU just for doing nothing sensible. Why is this?

The loop in Button1Click event will run at the maximum speed when there is no Sleep call. Depending on your CPU (Number of cores), this means the loop will consume almost all CPU time available using a single core. The loop will loop thousand or million times per second depending on your hardware.

When there is a call to Sleep, it will instruct the operating system to make the thread sleep for the remaining time slice and for approximately the time in millisecond. Using Sleep(100) will make your loop not run more than 10 times per second which will no use to much CPU.


Better solution?

I explained how ProcessMessages works and how to use it. Now it’s time to ask the good question: Should I really use ProcessMessages?

Most of ProcessMessages calls should and can be avoided. They should be avoided because there is a waste of CPU which slows down your application and the computer in general. They can be avoided by redesigning your application using event driven asynchronous pattern.

Most use of ProcessMessages are used when the developer has not correctly understood or is not even aware of the event driven asynchronous pattern. Such developer has a problem with asynchronous operation where your calls merely start an operation and give control back immediately while the operation itself is executed in the background and triggers an event when done.

In the simple case I used above, it is very easy to change the code so that no wait loop calling ProcessMessages is required. Let’s assume that some processing has to be done when the user has clicked Button3 and Button4 in any order.

The idea is to use a flag per event (Button click here) that must have occurred before the processing can take place. For each event, check all flag to see if processing can take place.

Here is the code:

procedure TForm1.Button3Click(Sender: TObject);
begin
   if FFlag3 then
       Memo1.Lines.Add('You already clicked Button3')
   else begin
       Memo1.Lines.Add('You clicked Button3');
       FFlag3 := TRUE;
   end;
   TryToExecute;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
   if FFlag4 then
       Memo1.Lines.Add('You already clicked Button4')
   else begin
       Memo1.Lines.Add('You clicked Button4');
       FFlag4 := TRUE;
   end;
   TryToExecute;
end;

procedure TForm1.TryToExecute;
begin
    // Check if both buttons have been clicked
    if (not FFlag3) or (not FFlag4) then
        Exit;
    // Both have been clicked
    Memo1.Lines.Add('Processing done');
    // Reset the flags so that the user can start again
    FFlag3 := FALSE;
    FFlag4 := FALSE;
end;

There can be cases where order of event is important or there are a large number of conditions. In those cases, the whole application should make use of a finite state machine. This will probably be the subject of a future article. For now, have a look at what Wikipedia says about it: http://en.wikipedia.org/wiki/Finite-state_machine

Update 16/12/2013: I wrote another article about how to make PostMessage multi-platform for Android and Windows: http://francois-piette.blogspot.be/2013/12/firemonkey-android-windows-and.html


Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
This article is available from http://francois-piette.blogspot.be

October 3, 2013

ICS version 8 released

ICS which exists since 1996, has been released in his version 8. V8 was at beta stage for about one year. Now it is released!

The new ICS-V8 is for Delphi 7 / C++ Builder 2006 to Delphi XE5 / C++ Builder XE3 with FireMonkey cross platform support for POSIX/MacOS, also IPv6 support (main development tree, 32 and 64-bit). Note that latest C++ Builder version supported is XE3 (lack of spare time, sorry).

The major changes in V8 include:
- Support for Delphi 7 to RAD Studio XE5
- Support for Firemonkey and Mac OS-X (still beta)
- New support for IPv6 in most components
- New SocketFamily property (sfAny, sfAnyIPv4, sfAnyIPv6, sfIPv4, sfIPv6)
- New MultiListenSockets property for most servers to add extra listening sockets, each with Addr/Port/SocketFamily/SslEnable
- New IPv4 and IPv6 string addresses helpers
- New SMTP mail server component
- New threaded ping component and trace route demo
- Revised directory structure with more, smaller sample directories

You can download ICS-V8 from http://www.overbyte.be as well as from the wiki download page at http://wiki.overbyte.be/wiki/index.php/ICS_Download

If you use the SVN repository, you will get the V8 automatically since it has been merged to the trunk.

Look at the readme: http://svn.overbyte.be:8443/svn/ics/trunk/ReadMe8.txt (User code and password are both "ics").

If you need help please don't ask thru this blog, but use the support mailing list. I will NOT answer to questions posted as comment to this blog post. If you don't know the mailing list, just go to the support page at http://www.overbyte.be.

By the way, there is a ICS-V9 in preparation. It will support Android. Stay tuned!

Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

August 20, 2013

Delphi XE5 to develop Android applications

Delphi XE5 is about to be announced. I have the chance to use a preview version for several weeks now. I’m very impressed!

Using Delphi XE5 I was able to write my first Android application and deploy it to my Nexus 7 in a couple of minutes. OK, for now it is a very basic application: I just took one of the predefined application templates, added a few buttons and labels, hit F9. After a few seconds, the application was compiled and deployed on my Nexus 7 connected to my PC with an USB cable. The application was up and running and I was able to debug it. Waouw!

As you know, I the author of the Internet Component Suite (aka ICS). ICS is about internet programming according to the asynchronous non-blocking programming model. I already started porting my code to Android.

Delphi XE5 is a native compiler for Android. It means Delphi XE5 compile into ARM machine language. It is neither Java nor running under a Java Virtual Machine (Renamed Dalvik on Android). No, Delphi XE5 produces native code which runs fast.

This will help me porting ICS to Android because Delphi XE5 gives me access to the underlying operating system which is… Linux! ICS is all about low level programming and I’m very happy about how it is done in XE5.

Of course XE5 comes with the famous FireMonkey framework (FMX) which covers a lot of thing. The most noticeable is the user interface. For ICS I don’t care about the user interface but of course most application using ICS do. They will simply use FMX. And guess what? FMX is cross platform: the same components (You know edits, labels and hundreds of UI components) can be used for Windows, Android, iOS and MAC OSx.

In the upcoming blog articles, I will enter in more details about Delphi XE5. Stay tuned!

Note: This article is based on a preview version (beta). Retail product may be different. I have been given special permission from Embarcadero to speak about XE5 despite the NDA covering the beta version.

Visit the official RAD Studio/Delphi Android page: http://embt.co/RADAndroid
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

June 9, 2013

Dynamic web page using Delphi, ICS and DWScript


ICS has a web application server component which allows you to build dynamic web page very easily. Delphi code for each web page is encapsulated in a TUrlHandler class and compiled into your application, making a standalone webserver application.

In the article, we will create a TUrlHandler class which will read a DWScript script from disc and execute it. The script is responsible for build a valid answer which ICS will send back to the client.

A simple “Hello World” script is made of a single line like this:

   Response.Write('Server time is ' + DateTimeToStr(Now) + ' ');

To invoke it, assuming the script is located in “Hello.pas”, the user must enter this URL into his browser:
http://localhost:20105/DWScripts/Hello.pas

Remember it is a script. You can change the file on disc and the changes will be immediately reflected for the next request, without recompiling your application. This looks much like PHP but use Delphi syntax instead of PHP. To be honest, PHP may use embedded HTML code within the script which is not supported here.

A more complex script may make use of request parameters. For example, the script “Add.pas” could looks like:

var Value1 : String;
var Value2 : String;
Response.ContentType := 'text/html';
Response.Status := '200 OK';
Response.Write('');
Response.Write('ICS and DWScript demo
');
Response.Write('Server time is ' + DateTimeToStr(Now) + '
');
if not Request.CheckParamByName('Value1', Value1) then
    Response.Write('Missing Value1 parameter
')
else if not Request.CheckParamByName('Value2', Value2) then
    Response.Write('Missing Value2 parameter
')
else Response.Write(Value1 + ' + ' + Value2 + ' = ' + 
    IntToStr(StrToIntDef(Value1, 0) + StrToIntDef(Value2, 0)));
Response.Write('');

The URL to use in the browser looks like:
http://localhost:20105/DWScripts/Add.pas?Value1=123&Value2=456

Getting ICS and DWScript

ICS:    http://wiki.overbyte.be/wiki/index.php/ICS_Download
DWScript: http://code.google.com/p/dwscript/

Implementation


Less than 200 Delphi code is required to implement this behavior in an ICS based web application server. The code I will show you below can be plugged in OverbyteIcsWebAppServer demo application you can find in ICS V8 distribution. Two lines must be added to the main file in order to add the feature: add the unit in the uses clause and add a line to map “/DWScript/*” to the TUrlHandler taking care of the script.

Using the above wild card mapping, the TUrlHandler will be invoked for any URL beginning with “/DWScript/”. It will then access the full path to get the script filename. In the above examples, I used a “.pas” file extension for ease, but this is not mandatory at all. You can use any extensions and even no extension if you don’t like to have the user know you are using a DWScript.

In the implementation, I managed to have the DWScript not directly accessible thru an URL. This protects your source code. The user can’t access it. He can just execute it (Note that ICS THttpServer component has an option which allows access to any file, so what I just said maybe wrong).

The script has two objects instances readily available: “Request” and “Response”. They maps to the corresponding Delphi object instances and classes:

    THttpResponse = class(TPersistent)
    private
        FStatus      : String;
        FContentType : String;
    public
        DocStream    : TStream;
    published
        property Status      : String read FStatus      write FStatus;
        property ContentType : String read FContentType write FContentType;
        procedure Write(const S : String);
    end;

    THttpRequest = class(TPersistent)
    public
        Params : String;
        class function ReadTextFile(const FileName : String) : String;
    published
        function  GetParamByName(const ParamName: String): String;
        function  CheckParamByName(const ParamName  : String;
                                   var   ParamValue : String): Boolean;
    end;

When using DWScript ExposeRTTI function, you are exposing a Delphi class. With the options I selected, only the published methods and properties will be exposed to the script.

THttpResponse exposes Status and ContentType properties as well as Write method. The two properties allows the script to select the HTTP status return and the HTTP content type. They default to “200 OK” and “text/html” which are the most common values. You can use anything you need for your application.

The Write method will be used by the script to produce the document part of the HTTP response sent back to the client. Obviously, the document format must match the content type.

THttpRequest exposes the incoming request. Using it you may access the parameters passed thru the URL. One method gets a parameter value when you provides his name while the other return the value as well as a Boolean value telling if the parameter exists or not.

The most important part of the code is a TUrlHandler derived class. I named it TUrlHandlerDWScript. His declaration looks like this:

    TUrlHandlerDWScript = class(TUrlHandler)
    protected
        FScript                  : IdwsProgram;
        FCompileMsgs             : String;
        FDelphiWebScript         : TDelphiWebScript;
        FUnit                    : TdwsUnit;
        FExec                    : IdwsProgramExecution;
        FHttpRequest             : THttpRequest;
        FHttpResponse            : THttpResponse;
        procedure ExposeInstancesAfterInitTable(Sender: TObject);
    public
        procedure Execute; override;
    end;

The five first member variables are required for DWScript operation. You should look at DWScript documentation for help about their use.

The last two member variables are the object instances we talk above. Their published parts are exposed to the script.

There is also an event handler ExposeInstancesAfterInitTable which is required by DWScript engine to expose the Delphi object instances to the script.

Finally, there is a single method which is called by ICS web application server framework to handle the mapped URL. This is where almost everything happens.

procedure TUrlHandlerDWScript.Execute;
var
    SrcFileName : String;
    Source      : String;
begin
    FDelphiWebScript  := TDelphiWebScript.Create(nil);
    FUnit             := TdwsUnit.Create(nil);
    FHttpResponse     := THttpResponse.Create;
    FHttpRequest      := THttpRequest.Create;
    try
        DocStream.Free;
        DocStream := TMemoryStream.Create;

        FHttpResponse.DocStream    := DocStream;
        FHttpResponse.Status       := '200 OK';
        FHttpResponse.ContentType  := 'text/html';
        FHttpRequest.Params        := Params;

        FUnit.OnAfterInitUnitTable := ExposeInstancesAfterInitTable;
        FUnit.UnitName             := 'WebPage';
        FUnit.Script               := FDelphiWebScript;
        FUnit.ExposeRTTI(TypeInfo(THttpResponse), [eoNoFreeOnCleanup]);
        FUnit.ExposeRTTI(TypeInfo(THttpRequest),  [eoNoFreeOnCleanup]);
        // In this application, we have placed DWScripts source code in
        // a directory at the same level as the "Template" folder.
        SrcFileName := ExcludeTrailingPathDelimiter(
                           ExtractFilePath(Client.TemplateDir)) +
                       StringReplace(Client.Path, '/', '\', [rfReplaceAll]);
        if not FileExists(SrcFileName) then
            FHttpResponse.Write('Script not found')
        else begin
            Source       := FHttpRequest.ReadTextFile(SrcFileName);
            FScript      := FDelphiWebScript.Compile(Source);
            FCompileMsgs := FScript.Msgs.AsInfo;
            if FScript.Msgs.HasErrors then begin
                FHttpResponse.Write('' + FCompileMsgs + '');
            end
            else begin
                FExec    := FScript.Execute;
            end;
        end;
        AnswerStream(FHttpResponse.Status, FHttpResponse.ContentType, NO_CACHE);
    finally
        FreeAndNil(FUnit);
        FreeAndNil(FDelphiWebScript);
        FreeAndNil(FHttpResponse);
        FreeAndNil(FHttpRequest);
    end;
    Finish;
end;


Basically the code creates all the required object instances, initializes default values, expose Delphi classes, read the script source code from file, compile the script and either create an error message should any compilation fails, or execute the script so that it can produce the document. Finally, the document is sent back and all object instances are destroyed.

Full source code:


The code is available from my website, see
   http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html


unit OverbyteIcsWebAppServerDWScriptUrlHandler;

interface

uses
    Classes, SysUtils, OverbyteIcsHttpAppServer, OverbyteIcsHttpSrv,
    dwsVCLGUIFunctions,
    dwsMagicExprs,
    dwsRTTIExposer,
    dwsFunctions,
    dwsSymbols,
    dwsExprs,
    dwsComp;


type
    THttpResponse = class(TPersistent)
    private
        FStatus      : String;
        FContentType : String;
    public
        DocStream    : TStream;
    published
        property Status      : String read FStatus      write FStatus;
        property ContentType : String read FContentType write FContentType;
        procedure Write(const S : String);
    end;

    THttpRequest = class(TPersistent)
    public
        Params : String;
        class function ReadTextFile(const FileName : String) : String;
    published
        function  GetParamByName(const ParamName: String): String;
        function  CheckParamByName(const ParamName  : String;
                                   var   ParamValue : String): Boolean;
    end;

    TUrlHandlerDWScript = class(TUrlHandler)
    protected
        FScript                  : IdwsProgram;
        FCompileMsgs             : String;
        FDelphiWebScript         : TDelphiWebScript;
        FUnit                    : TdwsUnit;
        FExec                    : IdwsProgramExecution;
        FHttpRequest             : THttpRequest;
        FHttpResponse            : THttpResponse;
        procedure ExposeInstancesAfterInitTable(Sender: TObject);
    public
        procedure Execute; override;
    end;

implementation


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}

{ TUrlHandlerDWScript }

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TUrlHandlerDWScript.Execute;
var
    SrcFileName : String;
    Source      : String;
begin
    FDelphiWebScript  := TDelphiWebScript.Create(nil);
    FUnit             := TdwsUnit.Create(nil);
    FHttpResponse     := THttpResponse.Create;
    FHttpRequest      := THttpRequest.Create;
    try
        DocStream.Free;
        DocStream := TMemoryStream.Create;

        FHttpResponse.DocStream    := DocStream;
        FHttpResponse.Status       := '200 OK';
        FHttpResponse.ContentType  := 'text/html';
        FHttpRequest.Params        := Params;

        FUnit.OnAfterInitUnitTable := ExposeInstancesAfterInitTable;
        FUnit.UnitName             := 'WebPage';
        FUnit.Script               := FDelphiWebScript;
        FUnit.ExposeRTTI(TypeInfo(THttpResponse), [eoNoFreeOnCleanup]);
        FUnit.ExposeRTTI(TypeInfo(THttpRequest),  [eoNoFreeOnCleanup]);
        // In this application, we have placed DWScripts source code in
        // a directory at the same level as the "Template" folder.
        SrcFileName := ExcludeTrailingPathDelimiter(
                           ExtractFilePath(Client.TemplateDir)) +
                       StringReplace(Client.Path, '/', '\', [rfReplaceAll]);
        if not FileExists(SrcFileName) then
            FHttpResponse.Write('Script not found')
        else begin
            Source       := FHttpRequest.ReadTextFile(SrcFileName);
            FScript      := FDelphiWebScript.Compile(Source);
            FCompileMsgs := FScript.Msgs.AsInfo;
            if FScript.Msgs.HasErrors then begin
                FHttpResponse.Write('' + FCompileMsgs + '');
            end
            else begin
                FExec    := FScript.Execute;
            end;
        end;
        AnswerStream(FHttpResponse.Status, FHttpResponse.ContentType, NO_CACHE);
    finally
        FreeAndNil(FUnit);
        FreeAndNil(FDelphiWebScript);
        FreeAndNil(FHttpResponse);
        FreeAndNil(FHttpRequest);
    end;
    Finish;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure TUrlHandlerDWScript.ExposeInstancesAfterInitTable(Sender: TObject);
begin
    FUnit.ExposeInstanceToUnit('Response', 'THttpResponse', FHttpResponse);
    FUnit.ExposeInstanceToUnit('Request',  'THttpRequest',  FHttpRequest);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}

{ THttpResponse }

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
procedure THttpResponse.Write(const S: String);
var
   Ch : Char;
   B  : Byte;
begin
    for Ch in S do begin
        // We should convert the Unicode string to whatever the document
        // is supposed to be. Here we just convert it, brute force, to ASCII.
        // This won't work eastern character sets.
        B := Ord(AnsiChar(Ch));
        DocStream.Write(B, 1);
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}

{ THttpRequest }

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function THttpRequest.CheckParamByName(
    const ParamName  : String;
    var   ParamValue : String): Boolean;
begin
    Result := ExtractURLEncodedValue(Params, ParamName, ParamValue);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
function THttpRequest.GetParamByName(const ParamName: String): String;
begin
    ExtractURLEncodedValue(Params, ParamName, Result);
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
class function THttpRequest.ReadTextFile(const FileName : String) : String;
var
    Stream : TFileStream;
    AnsiBuf : AnsiString;
begin
    Stream := TFileStream.Create(FileName, fmOpenRead);
    try
        SetLength(AnsiBuf, Stream.Size);
        Stream.Read(AnsiBuf[1], Stream.Size);
        Result := String(AnsiBuf);
    finally
        FreeAndNil(Stream);
    end;
end;


{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}

end.


Download source code from: http://www.overbyte.be/frame_index.html?redirTo=/blog_source_code.html
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be

June 2, 2013

Installing ICS for FireMonkey in Delphi XE4

Arno Garrels recorded a tutorial video showing the installation steps required to make ICS v8 works with FireMonkey in Delphi XE4.



Full resolution video here (70 MB, zipped)
Download ICS
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be