Parcourir la source

First release

Unknown il y a 7 ans
Parent
commit
0b2f621aaf

+ 46 - 0
.gitignore

@@ -0,0 +1,46 @@
+
+#ignore thumbnails created by windows
+Thumbs.db
+*.bak
+*.cache
+# Delphi compiler-generated binaries (safe to delete)
+*.exe
+*.dll
+*.bpl
+*.bpi
+*.dcp
+*.so
+*.apk
+*.drc
+*.map
+*.dres
+*.rsm
+*.tds
+*.dcu
+*.lib
+*.a
+*.o
+*.ocx
+*.zip
+*.log
+
+# Delphi autogenerated files (duplicated info)
+*.cfg
+*.hpp
+*Resource.rc
+
+# Delphi local files (user-specific info)
+*.local
+*.identcache
+*.projdata
+*.tvsconfig
+*.dsk
+
+# Delphi history and backups
+__history/
+__recovery/
+*.~*
+
+# Castalia statistics file (since XE7 Castalia is distributed with Delphi)
+*.stat
+desktop.ini

+ 201 - 0
LICENSE.txt

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 526 - 0
Quick.Amazon.pas

@@ -0,0 +1,526 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.Amazon
+  Description : Amazon object operations
+  Author      : Kike Pérez
+  Version     : 1.2
+  Created     : 18/11/2016
+  Modified    : 20/11/2016
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Amazon;
+
+interface
+
+uses
+  Classes,
+  System.SysUtils,
+  System.Generics.Collections,
+  IPPeerClient,
+  Data.Cloud.CloudAPI,
+  Data.Cloud.AmazonAPI;
+
+type
+
+  TAmazonProtocol = (amHTTP,amHTTPS);
+  TAmazonStorage = TAmazonStorageService;
+  TAmazonACLAccess = TAmazonACLType;
+  TAmazonRegion = Data.Cloud.AmazonAPI.TAmazonRegion;
+
+  TAmazonObject = class
+    Name : string;
+    Modified : TDateTime;
+    Size : Int64;
+    IsDeleted : Boolean;
+  end;
+
+  TAmazonObjects = class(TObjectList<TAmazonObject>);
+
+  TAmazonResponseInfo = record
+    StatusCode : Integer;
+    StatusMsg : string;
+  end;
+
+  TQuickAmazon = class
+    private
+      fconAmazon : TAmazonConnectionInfo;
+      fAccountName : string;
+      fAccountKey : string;
+      fAmazonProtocol : TAmazonProtocol;
+      procedure SetAccountName(amAccountName : string);
+      procedure SetAccountKey(amAccountKey : string);
+      procedure SetAmazonProtocol(amProtocol : TAmazonProtocol);
+      function FileToArray(cFilename : string) : TArray<Byte>;
+      function StreamToArray(cStream : TStream) : TArray<Byte>;
+    public
+      constructor Create; overload;
+      constructor Create(amAccountName, amAccountKey : string); overload;
+      destructor Destroy; override;
+      property AccountName : string read fAccountName write SetAccountName;
+      property AccountKey : string read fAccountKey write SetAccountKey;
+      property AmazonProtocol : TAmazonProtocol read fAmazonProtocol write SetAmazonProtocol;
+      function StorageURL(amBucket : string) : string;
+      function PutObject(amBucket, cFilename, amObjectName : string; amACLType : TAmazonACLType; var amResponseInfo : TAmazonResponseInfo) : Boolean; overload;
+      function PutObject(amBucket : string; cStream : TStream; amObjectName : string; amACLType : TAmazonACLType; var amResponseInfo : TAmazonResponseInfo) : Boolean; overload;
+      function GetObject(amBucket, amObjectName, cFilenameTo : string; var amResponseInfo : TAmazonResponseInfo) : Boolean; overload;
+      function GetObject(amBucket, amObjectName : string; var amResponseInfo : TAmazonResponseInfo) : TMemoryStream; overload;
+      function ExistsObject(amBucket, amObjectName : string; amRegion : TAmazonRegion) : Boolean;
+      function DeleteObject(amBucket,amObjectName : string; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+      function ListObjects(amBucket : string; amObjectsStartWith : string; amRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : TAmazonObjects;
+      function ListObjectsNames(amBucket : string; amObjectsStartWith : string; amRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : TStrings;
+      function ExistsBucket(amBucketName : string) : Boolean;
+      function ListBuckets(var amResponseInfo : TAmazonResponseInfo) : TStrings;
+      function CreateBucket(amBucket : string; amBucketRegion : TAmazonRegion; amACLType : TAmazonACLAccess; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+      function DeleteBucket(amBucket : string; amBucketRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+  end;
+
+implementation
+
+constructor TQuickAmazon.Create;
+begin
+  inherited;
+  fconAmazon := TAmazonConnectionInfo.Create(nil);
+  fAmazonProtocol := amHTTP;
+end;
+
+constructor TQuickAmazon.Create(amAccountName, amAccountKey : string);
+begin
+  Create;
+  SetAccountName(amAccountName);
+  SetAccountKey(amAccountKey);
+end;
+
+destructor TQuickAmazon.Destroy;
+begin
+  if Assigned(fconAmazon) then fconAmazon.Free;
+  inherited;
+end;
+
+procedure TQuickAmazon.SetAccountName(amAccountName : string);
+begin
+  if fAccountName <> amAccountName  then
+  begin
+    fAccountName := amAccountName;
+    fconAmazon.AccountName := amAccountName;
+  end;
+end;
+
+procedure TQuickAmazon.SetAccountKey(amAccountKey : string);
+begin
+  if fAccountKey  <> amAccountKey   then
+  begin
+    fAccountKey  := amAccountKey ;
+    fconAmazon.AccountKey  := amAccountKey;
+  end;
+end;
+
+procedure TQuickAmazon.SetAmazonProtocol(amProtocol: TAmazonProtocol);
+begin
+  if fAmazonProtocol <> amProtocol then
+  begin
+    fAmazonProtocol := amProtocol;
+    if amProtocol = amHTTP then fconAmazon.Protocol := 'http'
+      else fconAmazon.Protocol := 'https';
+  end;
+end;
+
+function TQuickAmazon.FileToArray(cFilename : string) : TArray<Byte>;
+var
+  fs : TFileStream;
+  bs : TBytesStream;
+begin
+  fs := TFileStream.Create(cFilename, fmOpenRead);
+  try
+    bs := TBytesStream.Create(Result);
+    try
+      bs.LoadFromStream(fs);
+      Result := bs.Bytes;
+    finally
+      bs.Free
+    end;
+  finally
+    fs.Free;
+  end;
+end;
+
+function TQuickAmazon.StreamToArray(cStream : TStream) : TArray<Byte>;
+var
+  bs : TBytesStream;
+begin
+  bs := TBytesStream.Create(Result);
+  try
+    bs.LoadFromStream(cStream);
+    Result := bs.Bytes;
+  finally
+    bs.Free
+  end;
+end;
+
+function GetResponseInfo(amResponseInfo : TCloudResponseInfo) : TAmazonResponseInfo;
+begin
+  Result.StatusCode := amResponseInfo.StatusCode;
+  Result.StatusMsg := amResponseInfo.StatusMessage;
+end;
+
+function TQuickAmazon.StorageURL(amBucket : string) : string;
+begin
+  Result := fconAmazon.StorageURL(amBucket)
+end;
+
+function TQuickAmazon.PutObject(amBucket, cFilename, amObjectName : string; amACLType : TAmazonACLType; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorage;
+  Content : TArray<Byte>;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  if amBucket = '' then amBucket := '$root';
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    Content := FileToArray(cFilename);
+    if amObjectName = '' then amObjectName := cFilename;
+    if amObjectName.StartsWith('/') then amObjectName := Copy(amObjectName,2,Length(amObjectName));
+    Result := AmazonS3.UploadObject(amBucket,amObjectName,Content,False,nil,nil,amACLType,CloudResponseInfo);
+    amResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.PutObject(amBucket : string; cStream : TStream; amObjectName : string; amACLType : TAmazonACLType; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorage;
+  Content : TArray<Byte>;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  amResponseInfo.StatusCode := 500;
+  if amBucket = '' then amBucket := '$root';
+  if amObjectName.StartsWith('/') then amObjectName := Copy(amObjectName,2,Length(amObjectName));
+  try
+    AmazonS3 := TAmazonStorage.Create(fconAmazon);
+    try
+      //AmazonS3.Timeout := fTimeout;
+      CloudResponseInfo := TCloudResponseInfo.Create;
+      Content := StreamToArray(cStream);
+      Result := AmazonS3.UploadObject(amBucket,amObjectName,Content,False,nil,nil,amACLType,CloudResponseInfo);
+    amResponseInfo := GetResponseInfo(CloudResponseInfo);
+    finally
+      AmazonS3.Free;
+      CloudResponseInfo.Free;
+    end;
+  except
+      Result := False;
+  end;
+end;
+
+function TQuickAmazon.GetObject(amBucket, amObjectName, cFilenameTo : string; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorage;
+  fs : TFileStream;
+  CloudResponseInfo : TCloudResponseInfo;
+  amParams : TAmazonGetObjectOptionals;
+begin
+  Result := False;
+  if amBucket = '' then amBucket := '$root';
+  if amObjectName.StartsWith('/') then amObjectName := Copy(amObjectName,2,Length(amObjectName));
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  try
+    //AmazonS3.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    if FileExists(cFilenameTo) then fs := TFileStream.Create(cFilenameTo,fmOpenWrite)
+      else fs := TFileStream.Create(cFilenameTo,fmCreate);
+    try
+      try
+        AmazonS3.GetObject(amBucket,amObjectName,fs,CloudResponseInfo);
+        amResponseInfo := GetResponseInfo(CloudResponseInfo);
+        if amResponseInfo.StatusCode = 200 then Result := True;
+      except
+        Result := False;
+      end;
+    finally
+      fs.Free;
+      CloudResponseInfo.Free;
+    end;
+  finally
+    AmazonS3.Free;
+  end;
+end;
+
+function TQuickAmazon.GetObject(amBucket, amObjectName : string; var amResponseInfo : TAmazonResponseInfo) : TMemoryStream;
+var
+  AmazonS3 : TAmazonStorage;
+  fs : TFileStream;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := TMemoryStream.Create;
+  if amBucket = '' then amBucket := '$root';
+  if amObjectName.StartsWith('/') then amObjectName := Copy(amObjectName,2,Length(amObjectName));
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  try
+    //AmazonS3.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    try
+      AmazonS3.GetObject(amBucket,amObjectName,Result,CloudResponseInfo);
+      amResponseInfo := GetResponseInfo(CloudResponseInfo);
+    except
+      Result := nil;
+    end;
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.ExistsObject(amBucket, amObjectName : string; amRegion : TAmazonRegion) : Boolean;
+var
+  amObject : string;
+  amObjects : TStrings;
+  ResponseInfo : TAmazonResponseInfo;
+begin
+  Result := False;
+  amObjects := ListObjectsNames(amBucket,amObjectName,amRegion,ResponseInfo);
+  try
+    if (ResponseInfo.StatusCode = 200) and (Assigned(amObjects)) then
+    begin
+      for amObject in amObjects do
+      begin
+        if amObject = amObjectName then
+        begin
+          Result := True;
+          Break;
+        end;
+      end;
+    end;
+  finally
+    amObjects.Free;
+  end;
+end;
+
+function TQuickAmazon.DeleteObject(amBucket,amObjectName : string; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorage;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if amBucket = '' then amBucket := '$root';
+  if amObjectName.StartsWith('/') then amObjectName := Copy(amObjectName,2,Length(amObjectName));
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  try
+    //AmazonS3.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := AmazonS3.DeleteObject(amBucket,amObjectName,CloudResponseInfo);
+    amResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.ListObjects(amBucket : string; amObjectsStartWith : string; amRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : TAmazonObjects;
+var
+  AmazonS3 : TAmazonStorage;
+  amObject : TAmazonObject;
+  i : Integer;
+  amBucketResult : TAmazonBucketResult;
+  CloudResponseInfo : TCloudResponseInfo;
+  cNextMarker : string;
+  amParams : TStrings;
+  a : TAmazonBucketResult;
+begin
+  Result := TAmazonObjects.Create(True);
+  cNextMarker := '';
+  if amBucket = '' then amBucket := '$root';
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    //AmazonS3.Timeout := fTimeout;
+    repeat
+      amParams := TStringList.Create;
+      try
+        amParams.Values['prefix'] := amObjectsStartWith;
+        if cNextMarker <> '' then amParams.Values['marker'] := cNextMarker;
+        amBucketResult := AmazonS3.GetBucket(amBucket,amParams,CloudResponseInfo,amRegion);
+        amResponseInfo := GetResponseInfo(CloudResponseInfo);
+        if Assigned(amBucketResult) then
+        begin
+          try
+            for i := 0 to amBucketResult.Objects.Count-1 do
+            begin
+              amObject := TAmazonObject.Create;
+              amObject.Name := amBucketResult.Objects[i].Name;
+              amObject.Modified := StrToDateTime(amBucketResult.Objects[i].LastModified);
+              amObject.Size := amBucketResult.Objects[i].Size;
+              amObject.IsDeleted := amBucketResult.Objects[i].IsDeleted;
+              Result.Add(amObject);
+            end;
+          finally
+            amBucketResult.Free;
+          end;
+          cNextMarker := amBucketResult.Marker;
+        end;
+      finally
+        amParams.Free;
+      end;
+    until (cNextMarker = '') or (amResponseInfo.StatusCode <> 200);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.ListObjectsNames(amBucket : string; amObjectsStartWith : string; amRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : TStrings;
+var
+  AmazonS3 : TAmazonStorage;
+  i : Integer;
+  amBucketResult : TAmazonBucketResult;
+  CloudResponseInfo : TCloudResponseInfo;
+  cNextMarker : string;
+  amParams : TStrings;
+  a : TAmazonBucketResult;
+begin
+  Result := TStringList.Create;
+  cNextMarker := '';
+  if amBucket = '' then amBucket := '$root';
+  AmazonS3 := TAmazonStorage.Create(fconAmazon);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    //AmazonS3.Timeout := fTimeout;
+    repeat
+      amParams := TStringList.Create;
+      try
+        if amObjectsStartWith <> '' then amParams.Values['prefix'] := amObjectsStartWith;
+        if cNextMarker <> '' then amParams.Values['marker'] := cNextMarker;
+        amBucketResult := AmazonS3.GetBucket(amBucket,amParams,CloudResponseInfo,amRegion);
+        amResponseInfo := GetResponseInfo(CloudResponseInfo);
+        if Assigned(amBucketResult) then
+        begin
+          try
+            for i := 0 to amBucketResult.Objects.Count-1 do Result.Add(amBucketResult.Objects[i].Name);
+          finally
+            amBucketResult.Free;
+          end;
+          cNextMarker := amBucketResult.Marker;
+        end;
+      finally
+        amParams.Free;
+      end;
+    until (cNextMarker = '') or (amResponseInfo.StatusCode <> 200);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.ExistsBucket(amBucketName : string) : Boolean;
+var
+  amBucket : string;
+  amBuckets : TStrings;
+  ResponseInfo : TAmazonResponseInfo;
+begin
+  Result := False;
+  amBuckets := ListBuckets(ResponseInfo);
+  try
+    if (ResponseInfo.StatusCode = 200) and (Assigned(amBuckets)) then
+    begin
+      for amBucket in amBuckets do
+      begin
+        if amBucket = amBucketName then
+        begin
+          Result := True;
+          Break;
+        end;
+      end;
+    end;
+  finally
+    amBuckets.Free;
+  end;
+end;
+
+function TQuickAmazon.ListBuckets(var amResponseInfo : TAmazonResponseInfo) : TStrings;
+var
+  AmazonS3 : TAmazonStorageService;
+  CloudResponseInfo : TCloudResponseInfo;
+  Buckets : TStrings;
+  i : Integer;
+begin
+  AmazonS3 := TAmazonStorageService.Create(fconAmazon);
+  Result := TStringList.Create;
+  try
+    //AmazonS3.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Buckets := AmazonS3.ListBuckets(CloudResponseInfo);
+    try
+      for i := 0 to Buckets.Count -1 do
+      begin
+        Result.Add(Buckets.Names[i]);
+      end;
+      amResponseInfo := GetResponseInfo(CloudResponseInfo);
+    finally
+      Buckets.Free;
+    end;
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.CreateBucket(amBucket : string; amBucketRegion : TAmazonRegion; amACLType : TAmazonACLAccess; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorageService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if amBucket = '' then Exit;
+
+  AmazonS3 := TAmazonStorageService.Create(fconAmazon);
+  try
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := AmazonS3.CreateBucket(amBucket,amACLType,amBucketRegion,CloudResponseInfo);
+    amResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAmazon.DeleteBucket(amBucket : string; amBucketRegion : TAmazonRegion; var amResponseInfo : TAmazonResponseInfo) : Boolean;
+var
+  AmazonS3 : TAmazonStorageService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if amBucket = '' then Exit;
+
+  AmazonS3 := TAmazonStorageService.Create(fconAmazon);
+  try
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := AmazonS3.DeleteBucket(amBucket,CloudResponseInfo,amBucketRegion);
+    amResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    AmazonS3.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+end.

+ 381 - 0
Quick.AppService.pas

@@ -0,0 +1,381 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2017 Kike Pérez
+
+  Unit        : Quick.AppService
+  Description : Allow run app as console or service
+  Author      : Kike Pérez
+  Version     : 1.0
+  Created     : 14/09/2017
+  Modified    : 05/10/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.AppService;
+
+interface
+
+{$IF CompilerVersion > 20}
+  {$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
+  {$WEAKLINKRTTI ON}
+{$ENDIF}
+
+uses
+  Windows,
+  System.SysUtils,
+  WinSvc,
+  Quick.Commons;
+
+const
+  DEF_SERVICENAME = 'QuickAppService';
+  DEF_DISPLAYNAME = 'QuickAppService';
+  NUM_OF_SERVICES = 2;
+
+type
+
+  TSvcStatus = (ssStopped = SERVICE_STOPPED,
+                ssStopping = SERVICE_STOP_PENDING,
+                ssStartPending = SERVICE_START_PENDING,
+                ssRunning = SERVICE_RUNNING,
+                ssPaused = SERVICE_PAUSED);
+
+  TSvcInitializeEvent = procedure of object;
+  TSvcAnonMethod = reference to procedure;
+  TSvcRemoveEvent = procedure of object;
+
+  TAppService = class
+  private
+    fSCMHandle : SC_HANDLE;
+    fSvHandle : SC_HANDLE;
+    fServiceName : string;
+    fDisplayName : string;
+    fFileName : string;
+    fSilent : Boolean;
+    fStatus : TSvcStatus;
+    fCanInstallWithOtherName : Boolean;
+    fOnInitialize : TSvcInitializeEvent;
+    fOnStart : TSvcAnonMethod;
+    fOnStop : TSvcAnonMethod;
+    fOnExecute : TSvcAnonMethod;
+    fAfterRemove : TSvcRemoveEvent;
+    procedure ReportSvcStatus(dwCurrentState, dwWin32ExitCode, dwWaitHint: DWORD);
+    procedure Execute;
+    procedure Help;
+    procedure DoStop;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property ServiceName : string read fServiceName write fServiceName;
+    property DisplayName : string read fDisplayName write fDisplayName;
+    property FileName : string read fFileName write fFileName;
+    property Silent : Boolean read fSilent write fSilent;
+    property CanInstallWithOtherName : Boolean read fCanInstallWithOtherName write fCanInstallWithOtherName;
+    property Status : TSvcStatus read fStatus write fStatus;
+    property OnStart : TSvcAnonMethod read fOnStart write fOnStart;
+    property OnStop : TSvcAnonMethod read fOnStop write fOnStop;
+    property OnExecute : TSvcAnonMethod read fOnExecute write fOnExecute;
+    property OnInitialize : TSvcInitializeEvent read fOnInitialize write fOnInitialize;
+    property AfterRemove : TSvcRemoveEvent read fAfterRemove write fAfterRemove;
+    procedure Install;
+    procedure Remove;
+    procedure CheckParams;
+    class function InstallParamsPresent : Boolean;
+    class function ConsoleParamPresent : Boolean;
+    class function IsRunningAsService : Boolean;
+    class function IsRunningAsConsole : Boolean;
+  end;
+
+var
+  ServiceStatus : TServiceStatus;
+  StatusHandle  : SERVICE_STATUS_HANDLE;
+  ServiceTable  : array [0..NUM_OF_SERVICES] of TServiceTableEntry;
+  ghSvcStopEvent: Cardinal;
+  AppService : TAppService;
+
+implementation
+
+procedure ServiceCtrlHandler(Control: DWORD); stdcall;
+begin
+  case Control of
+    SERVICE_CONTROL_STOP:
+      begin
+        AppService.Status := TSvcStatus.ssStopping;
+        SetEvent(ghSvcStopEvent);
+        ServiceStatus.dwCurrentState := SERVICE_STOP_PENDING;
+        SetServiceStatus(StatusHandle, ServiceStatus);
+      end;
+    SERVICE_CONTROL_PAUSE:
+      begin
+        AppService.Status := TSvcStatus.ssPaused;
+        ServiceStatus.dwcurrentstate := SERVICE_PAUSED;
+        SetServiceStatus(StatusHandle, ServiceStatus);
+      end;
+    SERVICE_CONTROL_CONTINUE:
+      begin
+        AppService.Status := TSvcStatus.ssRunning;
+        ServiceStatus.dwCurrentState := SERVICE_RUNNING;
+        SetServiceStatus(StatusHandle, ServiceStatus);
+      end;
+    SERVICE_CONTROL_INTERROGATE: SetServiceStatus(StatusHandle, ServiceStatus);
+    SERVICE_CONTROL_SHUTDOWN:
+      begin
+        AppService.Status := TSvcStatus.ssStopped;
+        AppService.DoStop;
+      end;
+  end;
+end;
+
+procedure RegisterService(dwArgc: DWORD; var lpszArgv: PChar); stdcall;
+begin
+  ServiceStatus.dwServiceType := SERVICE_WIN32_OWN_PROCESS;
+  ServiceStatus.dwCurrentState := SERVICE_START_PENDING;
+  ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_PAUSE_CONTINUE;
+  ServiceStatus.dwServiceSpecificExitCode := 0;
+  ServiceStatus.dwWin32ExitCode := 0;
+  ServiceStatus.dwCheckPoint := 0;
+  ServiceStatus.dwWaitHint := 0;
+
+  StatusHandle := RegisterServiceCtrlHandler(PChar(AppService.ServiceName), @ServiceCtrlHandler);
+
+  if StatusHandle <> 0 then
+  begin
+    AppService.ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
+    try
+      AppService.Status := TSvcStatus.ssRunning;
+      AppService.Execute;
+    finally
+      AppService.ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
+    end;
+  end;
+end;
+
+
+constructor TAppService.Create;
+begin
+  inherited;
+  fServiceName := DEF_SERVICENAME;
+  fDisplayName := DEF_DISPLAYNAME;
+  fFileName := ParamStr(0);
+  fSilent := True;
+  fStatus := TSvcStatus.ssStopped;
+  fCanInstallWithOtherName := False;
+  fOnExecute := nil;
+end;
+
+destructor TAppService.Destroy;
+begin
+  fOnStart := nil;
+  fOnStop := nil;
+  fOnExecute := nil;
+  if fSCMHandle <> 0 then CloseServiceHandle(fSCMHandle);
+  if fSvHandle <> 0 then CloseServiceHandle(fSvHandle);
+  inherited;
+end;
+
+procedure TAppService.ReportSvcStatus(dwCurrentState, dwWin32ExitCode, dwWaitHint: DWORD);
+begin
+  //fill in the SERVICE_STATUS structure
+  ServiceStatus.dwCurrentState := dwCurrentState;
+  ServiceStatus.dwWin32ExitCode := dwWin32ExitCode;
+  ServiceStatus.dwWaitHint := dwWaitHint;
+
+  if dwCurrentState = SERVICE_START_PENDING then ServiceStatus.dwControlsAccepted := 0
+    else ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP;
+
+  case (dwCurrentState = SERVICE_RUNNING) or (dwCurrentState = SERVICE_STOPPED) of
+    True: ServiceStatus.dwCheckPoint := 0;
+    False: ServiceStatus.dwCheckPoint := 1;
+  end;
+
+  //report service status to SCM
+  SetServiceStatus(StatusHandle,ServiceStatus);
+end;
+
+procedure TAppService.Execute;
+begin
+  //we have to do something or service will stop
+  ghSvcStopEvent := CreateEvent(nil,True,False,nil);
+
+  if ghSvcStopEvent = 0 then
+  begin
+    ReportSvcStatus(SERVICE_STOPPED,NO_ERROR,0);
+    Exit;
+  end;
+
+  if Assigned(fOnStart) then fOnStart;
+
+  //report running status when initialization is complete
+  ReportSvcStatus(SERVICE_RUNNING,NO_ERROR,0);
+
+  //perform work until service stops
+  while True do
+  begin
+    //external callback process
+    if Assigned(fOnExecute) then fOnExecute;
+    //check whether to stop the service.
+    WaitForSingleObject(ghSvcStopEvent,INFINITE);
+    ReportSvcStatus(SERVICE_STOPPED,NO_ERROR,0);
+    Exit;
+  end;
+end;
+
+procedure TAppService.DoStop;
+begin
+   if Assigned(fOnStop) then fOnStop;
+end;
+
+procedure TAppService.Remove;
+const
+  cRemoveMsg = 'Service "%s" removed successfully!';
+var
+  SCManager: SC_HANDLE;
+  Service: SC_HANDLE;
+begin
+  SCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
+  if SCManager = 0 then
+    Exit;
+  try
+    Service := OpenService(SCManager,PChar(fServiceName),SERVICE_ALL_ACCESS);
+    ControlService(Service,SERVICE_CONTROL_STOP,ServiceStatus);
+    DeleteService(Service);
+    CloseServiceHandle(Service);
+    if fSilent then Writeln(Format(cRemoveMsg,[fServiceName]))
+      else MessageBox(0,cRemoveMsg,PChar(fServiceName),MB_ICONINFORMATION or MB_OK or MB_TASKMODAL or MB_TOPMOST);
+  finally
+    CloseServiceHandle(SCManager);
+    if Assigned(fAfterRemove) then fAfterRemove;
+  end;
+end;
+
+procedure TAppService.Install;
+const
+  cInstallMsg = 'Service "%s" installed successfully!';
+  cSCMError = 'Error trying to open SC Manager (you need admin permissions)';
+begin
+  fSCMHandle := OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
+
+  if fSCMHandle = 0 then
+  begin
+    if fSilent then Writeln(cSCMError)
+      else MessageBox(0,cSCMError,PChar(fServiceName),MB_ICONERROR or MB_OK or MB_TASKMODAL or MB_TOPMOST);
+    Exit;
+  end;
+  fSvHandle := CreateService(fSCMHandle,
+                              PChar(fServiceName),
+                              PChar(fDisplayName),
+                              SERVICE_ALL_ACCESS,
+                              SERVICE_WIN32_OWN_PROCESS,
+                              SERVICE_AUTO_START,
+                              SERVICE_ERROR_IGNORE,
+                              PChar(fFileName),
+                              'System Reserved',
+                              nil,
+                              nil,
+                              nil, //user
+                              nil); //password
+
+  if fSvHandle <> 0 then
+  begin
+    if fSilent then Writeln(Format(cInstallMsg,[fServiceName]))
+      else MessageBox(0,cInstallMsg,PChar(fServiceName),MB_ICONINFORMATION or MB_OK or MB_TASKMODAL or MB_TOPMOST);
+  end;
+end;
+
+procedure TAppService.Help;
+begin
+  Writeln('HELP:');
+  if fCanInstallWithOtherName then
+  begin
+    Writeln(Format('%s [/instance:<Service name>] [/console] [/install] [/remove] [/h] [/help]',[ExtractFileName(ParamStr(0))]));
+    WriteLn(' [/instance:<service name>]'+#9+'Install service with a custom name');
+  end
+  else Writeln(Format('%s [/console] [/install] [/remove] [/h] [/help]',[ExtractFileName(ParamStr(0))]));
+  WriteLn(' [/console]'+#9#9#9+'Run as a console application');
+  WriteLn(' [/install]'+#9#9#9+'Install as a service');
+  WriteLn(' [/remove]'+#9#9#9+'Remove service');
+  WriteLn(' [/h /help]'+#9#9#9+'This help');
+end;
+
+procedure TAppService.CheckParams;
+var
+  svcname : string;
+begin
+
+  if ParamCount > 0 then
+  begin
+    if (ParamFindSwitch('h')) or (ParamFindSwitch('help')) then Self.Help
+      else if ParamFindSwitch('install') then
+      begin
+        if (fCanInstallWithOtherName) and (ParamGetSwitch('instance',svcname)) then
+        begin
+          fServiceName := svcname;
+          fDisplayName := svcname;
+        end;
+        Self.Install;
+      end
+      else if ParamFindSwitch('remove') then
+      begin
+        if (fCanInstallWithOtherName) and (ParamGetSwitch('instance',svcname)) then
+        begin
+          fServiceName := svcname;
+          fDisplayName := svcname;
+        end;
+        Self.Remove;
+      end
+      else Writeln('Unknow parameter specified!');
+  end
+  else
+  begin
+    //initialize as a service
+    if Assigned(fOnInitialize) then fOnInitialize;
+    ServiceTable[0].lpServiceName := PChar(fServiceName);
+    ServiceTable[0].lpServiceProc := @RegisterService;
+    ServiceTable[1].lpServiceName := nil;
+    ServiceTable[1].lpServiceProc := nil;
+    StartServiceCtrlDispatcher(ServiceTable[0]);
+  end;
+end;
+
+class function TAppService.ConsoleParamPresent : Boolean;
+begin
+  Result := ParamFindSwitch('console');
+end;
+
+class function TAppService.InstallParamsPresent : Boolean;
+begin
+  Result := (ParamFindSwitch('install') or ParamFindSwitch('remove') or ParamFindSwitch('help') or ParamFindSwitch('h'));  
+end;
+
+class function TAppService.IsRunningAsService : Boolean;
+begin
+  Result := (IsService and not ConsoleParamPresent) or InstallParamsPresent;
+end;
+
+class function TAppService.IsRunningAsConsole : Boolean;
+begin
+  Result := (not IsService) or (ConsoleParamPresent);
+end;
+
+initialization
+AppService := TAppService.Create;
+
+finalization
+AppService.Free;
+
+end.

+ 721 - 0
Quick.Azure.pas

@@ -0,0 +1,721 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.Azure
+  Description : Azure blobs operations
+  Author      : Kike Pérez
+  Version     : 1.2
+  Created     : 27/08/2015
+  Modified    : 09/05/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Azure;
+
+interface
+
+uses
+  Classes,
+  System.SysUtils,
+  System.Generics.Collections,
+  IPPeerClient,
+  Data.Cloud.CloudAPI,
+  Data.Cloud.AzureAPI;
+
+type
+
+  TAzureProtocol = (azHTTP,azHTTPS);
+  //TAzureBlob = Data.Cloud.AzureAPI.TAzureBlob;
+  TBlobPublicAccess = Data.Cloud.AzureAPI.TBlobPublicAccess;
+
+  TAzureResponseInfo = record
+    StatusCode : Integer;
+    StatusMsg : string;
+  end;
+
+  TAzureBlobObject = class
+    Name : string;
+    Size : Int64;
+    LastModified : TDateTime;
+  end;
+
+  TBlobList = class (TObjectList<TAzureBlobObject>);
+
+
+  TQuickAzure = class
+    private
+      fconAzure : TAzureConnectionInfo;
+      fAccountName : string;
+      fAccountKey : string;
+      fAzureProtocol : TAzureProtocol;
+      fTimeOut : Integer;
+      procedure SetAccountName(azAccountName : string);
+      procedure SetAccountKey(azAccountKey : string);
+      procedure SetAzureProtocol(azProtocol : TAzureProtocol);
+      function FileToArray(cFilename : string) : TArray<Byte>;
+      function StreamToArray(cStream : TStream) : TArray<Byte>;
+      function GMT2DateTime(const gmtdate : string):TDateTime;
+    public
+      constructor Create; overload;
+      constructor Create(azAccountName, azAccountKey : string); overload;
+      destructor Destroy; override;
+      property AccountName : string read fAccountName write SetAccountName;
+      property AccountKey : string read fAccountKey write SetAccountKey;
+      property AzureProtocol : TAzureProtocol read fAzureProtocol write SetAzureProtocol;
+      property TimeOut : Integer read fTimeOut write fTimeOut;
+      function PutBlob(azContainer, cFilename, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean; overload;
+      function PutBlob(azContainer : string; cStream : TStream; azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean; overload;
+      function GetBlob(azContainer, azBlobName, cFilenameTo : string; out azResponseInfo : TAzureResponseInfo) : Boolean; overload;
+      function GetBlob(azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo; out Stream : TMemoryStream) : Boolean; overload;
+      function GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : TMemoryStream; overload;
+      function CopyBlob(azSourceContainer, azSourceBlobName : string; azTargetContainer, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+      function RenameBlob(const azContainer, azSourceBlobName, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+      function ExistsObject(const azContainer, azBlobName : string) : Boolean;
+      function ExistsFolder(azContainer : string; azFolderName : string) : Boolean;
+      function DeleteBlob(azContainer,azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+      function ListBlobs(azContainer : string; azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TBlobList;
+      function ListBlobsNames(azContainer : string; azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TStrings;
+      function ExistsContainer(azContainer : string) : Boolean;
+      function ListContainers(azContainersStartWith : string; azResponseInfo : TAzureResponseInfo) : TStrings;
+      function CreateContainer(azContainer : string; azPublicAccess : TBlobPublicAccess; out azResponseInfo : TAzureResponseInfo) : Boolean;
+      function DeleteContainer(azContainer : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+  end;
+
+implementation
+
+
+constructor TQuickAzure.Create;
+begin
+  inherited;
+  fconAzure := TAzureConnectionInfo.Create(nil);
+  fAzureProtocol := azHTTP;
+  fTimeOut := 30;
+end;
+
+constructor TQuickAzure.Create(azAccountName, azAccountKey : string);
+begin
+  Create;
+  SetAccountName(azAccountName);
+  SetAccountKey(azAccountKey);
+end;
+
+destructor TQuickAzure.Destroy;
+begin
+  if Assigned(fconAzure) then fconAzure.Free;
+  inherited;
+end;
+
+procedure TQuickAzure.SetAccountName(azAccountName : string);
+begin
+  if fAccountName <> azAccountName  then
+  begin
+    fAccountName := azAccountName;
+    fconAzure.AccountName := azAccountName;
+  end;
+end;
+
+procedure TQuickAzure.SetAccountKey(azAccountKey : string);
+begin
+  if fAccountKey  <> azAccountKey   then
+  begin
+    fAccountKey  := azAccountKey ;
+    fconAzure.AccountKey  := azAccountKey;
+  end;
+end;
+
+procedure TQuickAzure.SetAzureProtocol(azProtocol: TAzureProtocol);
+begin
+  if fAzureProtocol <> azProtocol then
+  begin
+    fAzureProtocol := azProtocol;
+    if azProtocol = azHTTP then fconAzure.Protocol := 'HTTP'
+      else fconAzure.Protocol := 'HTTPS';
+  end;
+end;
+
+function TQuickAzure.FileToArray(cFilename : string) : TArray<Byte>;
+var
+  fs : TFileStream;
+  bs : TBytesStream;
+begin
+  fs := TFileStream.Create(cFilename, fmOpenRead);
+  try
+    bs := TBytesStream.Create(Result);
+    try
+      bs.LoadFromStream(fs);
+      Result := bs.Bytes;
+    finally
+      bs.Free
+    end;
+  finally
+    fs.Free;
+  end;
+end;
+
+function TQuickAzure.StreamToArray(cStream : TStream) : TArray<Byte>;
+var
+  bs : TBytesStream;
+begin
+  bs := TBytesStream.Create(Result);
+  try
+    bs.LoadFromStream(cStream);
+    Result := bs.Bytes;
+  finally
+    bs.Free
+  end;
+end;
+
+function TQuickAzure.GMT2DateTime(const gmtdate : string):TDateTime;
+  function GetMonthDig(Value : string):Integer;
+  const
+    aMonth : array[1..12] of string = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+  var
+    idx : Integer;
+  begin
+    Result := 0;
+    for idx := 1 to 12 do
+    begin
+      if CompareText(Value,aMonth[idx]) = 0 then
+      begin
+        Result := idx;
+        Break;
+      end;
+     end;
+  end;
+var
+  i : Integer;
+  Len : Integer;
+  wDay, wMonth, wYear,
+  wHour, wMinute, wSec : Word;
+begin
+  //GMT Format: 'Mon, 12 Jan 2014 16:20:35 GMT'
+  Result := 0;
+  Len := 0;
+  if gmtdate = '' then Exit;
+
+  try
+    for i := 0 to Length(gmtdate) do
+    begin
+      if gmtdate[i] in ['0'..'9'] then
+      begin
+        Len := i;
+        Break;
+      end;
+    end;
+
+    //Day
+    wDay := StrToIntDef(Copy(gmtdate,Len,2),0);
+    if wDay = 0 then Exit;
+
+    Inc(Len,3);
+
+    //Month
+    wMonth := GetMonthDig(Copy(gmtdate,Len,3));
+    if wMonth = 0 then Exit;
+    Inc(Len,4);
+
+    //Year
+    wYear := StrToIntDef(Copy(gmtdate,Len,4),0);
+    if wYear = 0 then Exit;
+    Inc(Len,5);
+
+    //Hour
+    wHour := StrToIntDef(Copy(gmtdate,Len,2),99);
+    if wHour = 99 then Exit;
+    Inc(Len,3);
+
+    //Min
+    wMinute := StrToIntDef(Copy(gmtdate,Len,2),99);
+    if wMinute = 99 then Exit;
+    Inc(Len,3);
+
+    //Sec
+    wSec := StrToIntDef(Copy(gmtdate,Len,2),99);
+    if wSec = 99 then Exit;
+
+    Result := EncodeDate(wYear,wMonth,wDay) + EncodeTime(wHour,wMinute,wSec,0);
+  except
+    Result := 0;
+  end;
+end;
+
+function GetResponseInfo(ResponseInfo : TCloudResponseInfo) : TAzureResponseInfo;
+begin
+  Result.StatusCode := ResponseInfo.StatusCode;
+  Result.StatusMsg := ResponseInfo.StatusMessage;
+end;
+
+function TQuickAzure.PutBlob(azContainer, cFilename, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  Content : TArray<Byte>;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  BlobService := TAzureBlobService.Create(fconAzure);
+  if azContainer = '' then azContainer := '$root';
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    BlobService.Timeout := fTimeout;
+    Content := FileToArray(cFilename);
+    if azBlobName = '' then azBlobName := cFilename;
+    if azBlobName.StartsWith('/') then azBlobName := Copy(azBlobName,2,Length(azBlobName));
+    Result := BlobService.PutBlockBlob(azContainer,azBlobName,Content,EmptyStr,nil,nil,CloudResponseInfo);
+    azResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.PutBlob(azContainer : string; cStream : TStream; azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  Content : TArray<Byte>;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  azResponseInfo.StatusCode := 500;
+  if cStream.Size = 0 then
+  begin
+    azResponseInfo.StatusMsg := 'Stream is empty';
+    Exit;
+  end;
+
+  if azContainer = '' then azContainer := '$root';
+  if azBlobName.StartsWith('/') then azBlobName := Copy(azBlobName,2,Length(azBlobName));
+  try
+    BlobService := TAzureBlobService.Create(fconAzure);
+    try
+      BlobService.Timeout := fTimeout;
+      CloudResponseInfo := TCloudResponseInfo.Create;
+      Content := StreamToArray(cStream);
+      Result := BlobService.PutBlockBlob(azContainer,azBlobName,Content,EmptyStr,nil,nil,CloudResponseInfo);
+      azResponseInfo := GetResponseInfo(CloudResponseInfo);
+    finally
+      BlobService.Free;
+      CloudResponseInfo.Free;
+    end;
+  except
+    on E : Exception do
+    begin
+      azResponseInfo.StatusCode := 500;
+      azResponseInfo.StatusMsg := e.message;
+      Result := False;
+    end;
+  end;
+end;
+
+function TQuickAzure.GetBlob(azContainer, azBlobName, cFilenameTo : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  fs : TFileStream;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if azContainer = '' then azContainer := '$root';
+  if azBlobName.StartsWith('/') then azBlobName := Copy(azBlobName,2,Length(azBlobName));
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    fs := TFileStream.Create(cFilenameTo,fmCreate);
+    try
+      try
+        Result := BlobService.GetBlob(azContainer,azBlobName,fs,EmptyStr,CloudResponseInfo);
+        azResponseInfo := GetResponseInfo(CloudResponseInfo);
+      except
+        Result := False;
+      end;
+    finally
+      fs.Free;
+    end;
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.GetBlob(azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo; out Stream : TMemoryStream) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  Stream := TMemoryStream.Create;
+  if azContainer = '' then azContainer := '$root';
+  if azBlobName.StartsWith('/') then azBlobName := Copy(azBlobName,2,Length(azBlobName));
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    try
+      Result := BlobService.GetBlob(azContainer,azBlobName,Stream,EmptyStr,CloudResponseInfo);
+      azResponseInfo := GetResponseInfo(CloudResponseInfo);
+    except
+      Stream := nil;
+    end;
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.GetBlob(const azContainer, azBlobName : string; out azResponseInfo : TAzureResponseInfo) : TMemoryStream;
+begin
+  GetBlob(azContainer,azBlobName,azResponseInfo,Result);
+end;
+
+function TQuickAzure.CopyBlob(azSourceContainer, azSourceBlobName : string; azTargetContainer, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if azSourceContainer = '' then azSourceContainer := '$root';
+  if azTargetContainer = '' then azTargetContainer := '$root';
+  if azSourceBlobName.StartsWith('/') then azSourceBlobName := Copy(azSourceBlobName,2,Length(azSourceBlobName));
+  if azTargetBlobName.StartsWith('/') then azTargetBlobName := Copy(azTargetBlobName,2,Length(azTargetBlobName));
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    try
+      Result := BlobService.CopyBlob(azTargetContainer,azTargetBlobName,azSourceContainer,azSourceBlobName,'',nil,CloudResponseInfo);
+      azResponseInfo := GetResponseInfo(CloudResponseInfo);
+    except
+      on E : Exception do
+      begin
+        Result := False;
+        azResponseInfo.StatusCode := 500;
+        azResponseInfo.StatusMsg := e.message;
+      end;
+    end;
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.RenameBlob(const azContainer, azSourceBlobName, azTargetBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  ResponseInfo : TAzureResponseInfo;
+begin
+  Result := False;
+  if CopyBlob(azContainer,azSourceBlobName,azContainer,azTargetBlobName,ResponseInfo) then
+  begin
+    Result := DeleteBlob(azContainer,azSourceBlobName,ResponseInfo);
+  end;
+end;
+
+function TQuickAzure.ExistsObject(const azContainer, azBlobName : string) : Boolean;
+var
+  azBlob : string;
+  azBlobs : TStrings;
+  ResponseInfo : TAzureResponseInfo;
+begin
+  Result := False;
+  azBlobs := ListBlobsNames(azContainer,azBlobName,True,ResponseInfo);
+  try
+    if (ResponseInfo.StatusCode = 200) and (Assigned(azBlobs)) then
+    begin
+      for azBlob in azBlobs do
+      begin
+        if azBlob = azBlobName then
+        begin
+          Result := True;
+          Break;
+        end;
+      end;
+    end;
+  finally
+    azBlobs.Free;
+  end;
+end;
+
+function TQuickAzure.ExistsFolder(azContainer : string; azFolderName : string) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  azBlob : TAzureBlob;
+  azBlobList : TList<TAzureBlob>;
+  CloudResponseInfo : TCloudResponseInfo;
+  AzParams : TStrings;
+  cNextMarker : string;
+begin
+  Result := False;
+  if azContainer = '' then azContainer := '$root';
+  BlobService := TAzureBlobService.Create(fconAzure);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    BlobService.Timeout := fTimeout;
+    AzParams := TStringList.Create;
+    try
+      if not azFolderName.EndsWith('/') then azFolderName := azFolderName + '/';      
+      AzParams.Values['prefix'] := azFolderName;
+      AzParams.Values['delimiter'] := '/';
+      AzParams.Values['maxresults'] := '1';
+      cNextMarker := '';
+      azBlobList := BlobService.ListBlobs(azContainer,cNextMarker,AzParams,CloudResponseInfo);
+      try
+        if (Assigned(azBlobList)) and (azBlobList.Count > 0) and (CloudResponseInfo.StatusCode = 200) then Result := True;
+      finally
+        azBlobList.Free;
+      end;
+    finally
+      AzParams.Free;
+    end;
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.DeleteBlob(azContainer,azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if azContainer = '' then azContainer := '$root';
+  if azBlobName.StartsWith('/') then azBlobName := Copy(azBlobName,2,Length(azBlobName));
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := BlobService.DeleteBlob(azContainer,azBlobName,False,EmptyStr,CloudResponseInfo);
+    azResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.ListBlobs(azContainer : string; azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TBlobList;
+var
+  BlobService : TAzureBlobService;
+  azBlob : TAzureBlob;
+  azBlobList : TList<TAzureBlob>;
+  Blob : TAzureBlobObject;
+  CloudResponseInfo : TCloudResponseInfo;
+  cNextMarker : string;
+  AzParams : TStrings;
+begin
+  Result := TBlobList.Create(True);
+  cNextMarker := '';
+  if azContainer = '' then azContainer := '$root';
+  BlobService := TAzureBlobService.Create(fconAzure);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    BlobService.Timeout := fTimeout;
+    repeat
+      AzParams := TStringList.Create;
+      try
+        AzParams.Values['prefix'] := azBlobsStartWith;
+        if not Recursive then AzParams.Values['delimiter'] := '/';
+        if cNextMarker <> '' then AzParams.Values['marker'] := cNextMarker;
+        azBlobList := BlobService.ListBlobs(azContainer,cNextMarker,AzParams,CloudResponseInfo);
+        azResponseInfo := GetResponseInfo(CloudResponseInfo);
+        if Assigned(azBlobList) then
+        begin
+          try
+            for azBlob in azBlobList do
+            begin
+              Blob := TAzureBlobObject.Create;
+              Blob.Name := azBlob.Name;
+              Blob.Size := StrToInt64Def(azBlob.Properties.Values['Content-Length'],0);
+              Blob.LastModified := GMT2DateTime(azBlob.Properties.Values['Last-Modified']);
+              Result.Add(Blob);
+            end;
+          finally
+            //frees azbloblist objects
+            for azBlob in azBlobList do azBlob.Free;
+            azBlobList.Free;
+          end;
+        end;
+      finally
+        AzParams.Free;
+      end;
+    until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.ListBlobsNames(azContainer : string; azBlobsStartWith : string; Recursive : Boolean; out azResponseInfo : TAzureResponseInfo) : TStrings;
+var
+  BlobService : TAzureBlobService;
+  azBlob : TAzureBlob;
+  azBlobList : TList<TAzureBlob>;
+  CloudResponseInfo : TCloudResponseInfo;
+  cNextMarker : string;
+  AzParams : TStrings;
+begin
+  Result := TStringList.Create;
+  cNextMarker := '';
+  if azContainer = '' then azContainer := '$root';
+  BlobService := TAzureBlobService.Create(fconAzure);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    BlobService.Timeout := fTimeout;
+    repeat
+      AzParams := TStringList.Create;
+      try
+        AzParams.Values['prefix'] := azBlobsStartWith;
+        if not Recursive then AzParams.Values['delimiter'] := '/';
+        if cNextMarker <> '' then AzParams.Values['marker'] := cNextMarker;
+
+        azBlobList := BlobService.ListBlobs(azContainer,cNextMarker,AzParams,CloudResponseInfo);
+        azResponseInfo := GetResponseInfo(CloudResponseInfo);
+        if Assigned(azBlobList) then
+        begin
+          Result.BeginUpdate;
+          try
+            for azBlob in azBlobList do Result.Add(azBlob.Name);
+          finally
+            Result.EndUpdate;
+            //frees bloblist objects
+            for azBlob in azBlobList do azBlob.Free;
+            azBlobList.Free;
+          end;
+        end;
+      finally
+        AzParams.Free;
+      end;
+    until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.ExistsContainer(azContainer : string) : Boolean;
+var
+  Container : string;
+  Containers : TStrings;
+  ResponseInfo : TAzureResponseInfo;
+begin
+  Result := False;
+  Containers := ListContainers(azContainer,ResponseInfo);
+  try
+    if (ResponseInfo.StatusCode = 200) and (Assigned(Containers)) then
+    begin
+      for Container in Containers do
+      begin
+        if Container = azContainer then
+        begin
+          Result := True;
+          Break;
+        end;
+      end;
+    end;
+  finally
+    Containers.Free;
+  end;
+end;
+
+function TQuickAzure.ListContainers(azContainersStartWith : string; azResponseInfo : TAzureResponseInfo) : TStrings;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+  cNextMarker : string;
+  AzParams : TStrings;
+  AzContainer : TAzureContainer;
+  AzContainers : TList<TAzureContainer>;
+begin
+  Result := TStringList.Create;
+  cNextMarker := '';
+  BlobService := TAzureBlobService.Create(fconAzure);
+  CloudResponseInfo := TCloudResponseInfo.Create;
+  try
+    BlobService.Timeout := fTimeout;
+    repeat
+      AzParams := TStringList.Create;
+      try
+        if azContainersStartWith <> '' then AzParams.Values['prefix'] := azContainersStartWith;
+        if cNextMarker <> '' then AzParams.Values['marker'] := cNextMarker;
+        AzContainers := BlobService.ListContainers(cNextMarker,AzParams,CloudResponseInfo);
+        try
+          azResponseInfo := GetResponseInfo(CloudResponseInfo);
+          if (azResponseInfo.StatusCode = 200) and (Assigned(AzContainer)) then
+          begin
+            for AzContainer in AzContainers do
+            begin
+              Result.Add(AzContainer.Name);
+            end;
+          end;
+        finally
+          if Assigned(AzContainer) then
+          begin
+            //frees ContainerList objects
+            for AzContainer in AzContainers do AzContainer.Free;
+            AzContainers.Free;
+          end;
+        end;
+      finally
+        AzParams.Free;
+      end;
+    until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.CreateContainer(azContainer : string; azPublicAccess : TBlobPublicAccess; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if azContainer = '' then Exit;
+
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := BlobService.CreateContainer(azContainer,nil,azPublicAccess,CloudResponseInfo);
+    azResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+function TQuickAzure.DeleteContainer(azContainer : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
+var
+  BlobService : TAzureBlobService;
+  CloudResponseInfo : TCloudResponseInfo;
+begin
+  Result := False;
+  if azContainer = '' then Exit;
+
+  BlobService := TAzureBlobService.Create(fconAzure);
+  try
+    BlobService.Timeout := fTimeout;
+    CloudResponseInfo := TCloudResponseInfo.Create;
+    Result := BlobService.DeleteContainer(azContainer,CloudResponseInfo);
+    azResponseInfo := GetResponseInfo(CloudResponseInfo);
+  finally
+    BlobService.Free;
+    CloudResponseInfo.Free;
+  end;
+end;
+
+end.

+ 391 - 0
Quick.Chrono.pas

@@ -0,0 +1,391 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.Chrono
+  Description : Chronometers time elapsed and estimated time to do a task
+  Author      : Kike Pérez
+  Version     : 1.2
+  Created     : 27/08/2015
+  Modified    : 29/09/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Chrono;
+
+interface
+
+uses
+  Windows,
+  SysUtils,
+  DateUtils;
+
+
+resourcestring
+  strDAY = 'day';
+  strHOUR = 'hour';
+  strMINUTE = 'minute';
+  strSECOND = 'second';
+  strMILLISECOND = 'millisecond';
+  strFMTSHORT_HOURS_MINUTES = 'hh:nn:ss';
+  strFMTSHORT_MINUTES_SECONDS = 'hh:nn:ss';
+  strFMTLONG_HOURS_MINUTES = 'h "hour(s) and" n "minute(s)"';
+  strFMTLONG_MINUTES_SECONDS = 'n "minute(s) and" s "second(s)"';
+
+type
+
+  TTimeValue = (utDay, utHour, utMinute, utSecond, utMillisecond);
+  TTimeFmt = (tfHoursAndMinutes, tfMinutesAndSeconds);
+
+const
+  UnitShortTime : array[utDay..utMillisecond] of string = ('d','h','m','s','ms');
+  UnitLongTime : array[utDay..utMillisecond] of string = (strDAY,strHOUR,strMINUTE,strSECOND,strMILLISECOND);
+  FmtShortTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTSHORT_HOURS_MINUTES,strFMTSHORT_MINUTES_SECONDS);
+  FmtLongTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTLONG_HOURS_MINUTES,strFMTLONG_MINUTES_SECONDS);
+
+type
+
+  TChronometer = class
+  private
+    fFrequency: TLargeInteger;
+    fIsRunning: Boolean;
+    fIsHighResolution: Boolean;
+    fStartCount, fStopCount: TLargeInteger;
+    fStartBreakPoint, fStopBreakPoint : TLargeInteger;
+    fReportFormatPrecission : Boolean;
+    procedure SetTickStamp(var lInt: TLargeInteger);
+    function GetElapsedTicks: TLargeInteger;
+    function GetElapsedMilliseconds: TLargeInteger;
+    function GetElapsedMillisecondsWithPrecission: Extended;
+    function GetElapsedMilliseconds_BreakPoint: TLargeInteger;
+    function GetElapsedMillisecondsWithPrecission_BreakPoint: Extended;
+    function GetElapsedSeconds : TLargeInteger;
+    class function GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
+    class function GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
+  public
+    constructor Create(const StartOnCreate: Boolean = false);
+    procedure Start;
+    procedure Stop;
+    procedure Reset;
+    procedure Check;
+    procedure BreakPoint;
+    property IsHighResolution: Boolean read fIsHighResolution;
+    property IsRunning: Boolean read fIsRunning;
+    property ReportFormatPrecission: boolean read fReportFormatPrecission write fReportFormatPrecission;
+    property ElapsedTicks: TLargeInteger read GetElapsedTicks;
+    property ElapsedMilliseconds: TLargeInteger read GetElapsedMilliseconds;
+    property ElapsedMilliseconds_Breakpoint: TLargeInteger read GetElapsedMilliseconds_BreakPoint;
+    property ElapsedMillisecondsWithPrecission: Extended read GetElapsedMillisecondsWithPrecission;
+    property ElapsedMillisecondsWithPrecission_BreakPoint: Extended read GetElapsedMillisecondsWithPrecission_BreakPoint;
+    property ElapsedSeconds: TLargeInteger read GetElapsedSeconds;
+    function ElapsedTime(LongFormat : Boolean = False) : string;
+    function ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
+    class function MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string; overload;
+    class function MillisecondsToString(aMillisecondsWithPrecission : Extended; LongFormat : Boolean = False) : string; overload;
+
+  end;
+
+  TChronoBenchmark = class
+  private
+    fTotalProcess : Int64;
+    fLastUpdateTime : TDateTime;
+    fCurrentProcess : Int64;
+    fFirstUpdateTime : TDateTime;
+    fEstimatedMilliseconds : TLargeInteger;
+    fSpeed : Single;
+    procedure SetCurrentProcess(NewCurrentProcess : Int64);
+    function GetElapsedMilliseconds : TLargeInteger;
+  public
+    constructor Create;
+    property TotalProcess : Int64 read fTotalProcess write fTotalProcess;
+    property CurrentProcess : Int64 read fCurrentProcess write SetCurrentProcess;
+    property Speed : Single read fSpeed write fSpeed;
+    property ElapsedMilliseconds : TLargeInteger read GetElapsedMilliseconds;
+    property EstimatedMilliseconds : TLargeInteger read fEstimatedMilliseconds write fEstimatedMilliseconds;
+    function ElapsedTime(LongFormat : Boolean) : string;
+    function EstimatedTime(LongFormat : Boolean) : string;
+    procedure Reset;
+  end;
+
+
+implementation
+
+{ TChronometer Class }
+
+constructor TChronometer.Create(const StartOnCreate: Boolean = false);
+begin
+  inherited Create;
+  fIsRunning := False;
+  fIsHighResolution := QueryPerformanceFrequency(fFrequency);
+  fReportFormatPrecission := True;
+  fStartCount := 0;
+  fStopCount := 0;
+  fStartBreakPoint := 0;
+  fStopBreakPoint := 0;
+  if not fIsHighResolution then fFrequency := MSecsPerSec;
+  if StartOnCreate then Start;
+end;
+
+function TChronometer.GetElapsedTicks: TLargeInteger;
+begin
+  Result := fStopCount - fStartCount;
+end;
+
+procedure TChronometer.SetTickStamp(var lInt: TLargeInteger);
+begin
+  if fIsHighResolution then QueryPerformanceCounter(lInt)
+    else lInt := MilliSecondOf(Now);
+end;
+
+function TChronometer.ElapsedTime(LongFormat : Boolean = False) : string;
+begin
+  if LongFormat then
+  begin
+    if fReportFormatPrecission then Result := MillisecondsToString(ElapsedMillisecondsWithPrecission,True)
+      else Result := MillisecondsToString(ElapsedMilliseconds,True);
+  end
+  else
+  begin
+    if fReportFormatPrecission then Result := MillisecondsToString(ElapsedMillisecondsWithPrecission)
+      else Result := MillisecondsToString(ElapsedMilliseconds);
+  end;
+end;
+
+function TChronometer.ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
+begin
+  if LongFormat then
+  begin
+    if fReportFormatPrecission then Result := MillisecondsToString(ElapsedMillisecondsWithPrecission_BreakPoint,True)
+      else Result := MillisecondsToString(ElapsedMilliseconds_BreakPoint,True);
+  end
+  else
+  begin
+    if fReportFormatPrecission then Result := MillisecondsToString(ElapsedMillisecondsWithPrecission_BreakPoint)
+      else Result := MillisecondsToString(ElapsedMilliseconds_BreakPoint);
+  end;
+end;
+
+class function TChronometer.GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
+begin
+  if LongFormat then Result := UnitLongTime[TimeValue] + '(s)'
+    else Result := UnitShortTime[TimeValue];
+end;
+
+class function TChronometer.GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
+begin
+  if LongFormat then Result := FmtLongTime[TimeFmt]
+    else Result := FmtShortTime[TimeFmt];
+end;
+
+class function TChronometer.MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string;
+var
+  dt : TDateTime;
+  sp : string;
+begin
+  if LongFormat then sp := ' ' else sp := '';
+
+  if aMilliseconds < MSecsPerSec then //milliseconds
+  begin
+    Result := Format('%d%s%s',[aMilliseconds,sp,GetUnitTime(utMillisecond,LongFormat)]);
+  end
+  else if (aMilliseconds / MSecsPerSec) < SecsPerMin then //seconds
+  begin
+    Result := Format('%d%s%s',[(aMilliseconds div MSecsPerSec),sp,GetUnitTime(utSecond,LongFormat)]);
+  end
+  else if ((aMilliseconds / MSecsPerSec) < SecsPerHour) and ((aMilliseconds mod (SecsPerMin * MSecsPerSec)) = 0) then //minutes
+  begin
+    Result := Format('%d%s%s',[(aMilliseconds div (SecsPerMin * MSecsPerSec)),sp,GetUnitTime(utMinute,LongFormat)]);
+  end
+  else if (aMilliseconds / MSecsPerSec) < SecsPerDay then //hours
+  begin
+    dt := aMilliseconds / MSecsPerSec / SecsPerDay;
+    if LongFormat then
+    begin
+      if (aMilliseconds / MSecsPerSec) > SecsPerHour then Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))
+        else Result := FormatDateTime(GetFmtTime(tfMinutesAndSeconds,LongFormat),Frac(dt))
+    end
+    else
+    begin
+      Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt));
+    end;
+  end
+  else //days
+  begin
+    dt := aMilliseconds / MSecsPerSec / SecsPerDay;
+    Result := Format('%d%s%s, %s', [trunc(dt),sp,GetUnitTime(utDay,LongFormat),FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))]);
+  end;
+end;
+
+class function TChronometer.MillisecondsToString(aMillisecondsWithPrecission : Extended; LongFormat : Boolean = False) : string;
+var
+  dt : TDateTime;
+  sp : string;
+begin
+  if LongFormat then sp := '' else sp := ' ';
+  if aMillisecondsWithPrecission < MSecsPerSec then //milliseconds
+  begin
+    Result := Format('%f%s%s',[aMillisecondsWithPrecission,sp,GetUnitTime(utMillisecond,LongFormat)]);
+  end
+  else if (aMillisecondsWithPrecission / MSecsPerSec) < 60 then //seconds
+  begin
+    Result := Format('%f%s%s',[(aMillisecondsWithPrecission / MSecsPerSec),sp,GetUnitTime(utSecond,LongFormat)]);
+  end
+  else if (aMillisecondsWithPrecission / MSecsPerSec) < SecsPerHour then //minutes
+  begin
+    Result := Format('%f%s%s',[(aMillisecondsWithPrecission / (SecsPerMin * MSecsPerSec)),sp,GetUnitTime(utMinute,LongFormat)]);
+  end
+  else if (aMillisecondsWithPrecission / MSecsPerSec) < SecsPerDay then //hours
+  begin
+    dt := aMillisecondsWithPrecission / MSecsPerSec / SecsPerDay;
+    if LongFormat then
+    begin
+      if (aMillisecondsWithPrecission / MSecsPerSec) > SecsPerHour then Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))
+        else Result := FormatDateTime(GetFmtTime(tfMinutesAndSeconds,LongFormat),Frac(dt));
+    end
+    else
+    begin
+      Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt));
+    end;
+  end
+  else //días
+  begin
+    dt := aMillisecondsWithPrecission / MSecsPerSec / SecsPerDay;
+    Result := Format('%d%s%s, %s', [trunc(dt),sp,GetUnitTime(utDay,LongFormat),FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))]);
+  end;
+end;
+
+function TChronometer.GetElapsedMilliseconds : TLargeInteger;
+begin
+  result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
+end;
+
+function TChronometer.GetElapsedMilliseconds_BreakPoint : TLargeInteger;
+begin
+  result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) div fFrequency;
+end;
+
+function TChronometer.GetElapsedMillisecondsWithPrecission : Extended;
+begin
+  result := (MSecsPerSec * (fStopCount - fStartCount)) / fFrequency;
+end;
+
+function TChronometer.GetElapsedMillisecondsWithPrecission_BreakPoint : Extended;
+begin
+  result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) / fFrequency;
+end;
+
+function TChronometer.GetElapsedSeconds : TLargeInteger;
+begin
+  result := ((MSecsPerSec * (fStopCount - fStartCount)) div fFrequency) div MSecsPerSec;
+end;
+
+procedure TChronometer.Start;
+begin
+  SetTickStamp(fStartCount);
+  fIsRunning := true;
+end;
+
+procedure TChronometer.Stop;
+begin
+  SetTickStamp(fStopCount);
+  fIsRunning := false;
+end;
+
+procedure TChronometer.Reset;
+begin
+  SetTickStamp(fStartCount);
+end;
+
+procedure TChronometer.Check;
+begin
+  if fIsRunning then SetTickStamp(fStopCount);
+end;
+
+procedure TChronometer.BreakPoint;
+begin
+  if fIsRunning then
+  begin
+    if fStartBreakPoint = 0 then
+    begin
+      SetTickStamp(fStopBreakPoint);
+      fStartBreakPoint := fStartCount;
+    end
+    else
+    begin
+      fStartBreakPoint := fStopBreakPoint;
+      SetTickStamp(fStopBreakPoint);
+    end;
+  end
+  else fStopBreakPoint := fStopCount;
+end;
+
+{ TChronoBenchmark Class }
+
+constructor TChronoBenchmark.Create;
+begin
+  inherited;
+  fTotalProcess := 0;
+  fSpeed := 0;
+  fLastUpdateTime := Now();
+  fCurrentProcess := 0;
+  fFirstUpdateTime := 0;
+  fEstimatedMilliseconds := 0;
+end;
+
+procedure TChronoBenchmark.SetCurrentProcess(NewCurrentProcess : Int64);
+begin
+  //corrects first time run
+  if fLastUpdateTime = 0 then fLastUpdateTime := Now();
+  if fFirstUpdateTime = 0 then fFirstUpdateTime := Now();
+
+  //calculates operation speed
+  fSpeed := (NewCurrentProcess - fCurrentProcess) / ((Now() - fLastUpdateTime) * 86400);
+  if fSpeed = 0 then fSpeed := 0.1;
+  //returns estimated time string
+  fEstimatedMilliseconds := Round(((TotalProcess - NewCurrentProcess) / fSpeed) * 1000);
+  //save current values
+  fLastUpdateTime := Now();
+  fCurrentProcess := NewCurrentProcess;
+end;
+
+function TChronoBenchmark.GetElapsedMilliseconds : TLargeInteger;
+begin
+  Result := Round(((Now() - fFirstUpdateTime) * 86400 * 1000));
+end;
+
+function TChronoBenchmark.ElapsedTime(LongFormat : Boolean) : string;
+begin
+  Result := TChronometer.MillisecondsToString(GetElapsedMilliseconds,LongFormat);
+end;
+
+function TChronoBenchmark.EstimatedTime(LongFormat : Boolean) : string;
+begin
+  Result := TChronometer.MillisecondsToString(fEstimatedMilliseconds,LongFormat);
+end;
+
+procedure TChronoBenchmark.Reset;
+begin
+  fLastUpdateTime := Now();
+  fSpeed := 0;
+  fCurrentProcess := 0;
+  fFirstUpdateTime := 0;
+  fEstimatedMilliseconds := 0;
+end;
+
+end.

+ 710 - 0
Quick.Commons.pas

@@ -0,0 +1,710 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2017 Kike Pérez
+
+  Unit        : Quick.Commons
+  Description : Common functions
+  Author      : Kike Pérez
+  Version     : 1.2
+  Created     : 14/07/2017
+  Modified    : 05/10/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Commons;
+
+interface
+  uses
+    Classes,
+    System.SysUtils,
+    {$IFDEF MSWINDOWS}
+      Windows,
+      Winapi.ShlObj,
+      System.Win.Registry,
+    {$ENDIF MSWINDOWS}
+    System.IOUtils,
+    System.DateUtils;
+
+type
+
+ TEventType = (etInfo, etSuccess, etWarning, etError, etDebug, etTrace);
+ TLogVerbose = set of TEventType;
+
+const
+  LOG_ONLYERRORS = [etInfo,etError];
+  LOG_ERRORSANDWARNINGS = [etInfo,etWarning,etError];
+  LOG_TRACE = [etInfo,etError,etWarning,etTrace];
+  LOG_ALL = [etInfo,etSuccess,etWarning,etError,etTrace];
+  LOG_DEBUG = [etInfo,etSuccess,etWarning,etError,etDebug];
+  EventStr : array of string = ['INFO','SUCC','WARN','ERROR','DEBUG','TRACE'];
+
+type
+  TPasswordComplexity = set of (pfIncludeNumbers,pfIncludeSigns);
+
+  {$IFDEF MSWINDOWS}
+  TEnvironmentPath = record
+    EXEPATH : string;
+    WINDOWS : string;
+    SYSTEM : string;
+    PROGRAMFILES : string;
+    COMMONFILES : string;
+    HOMEDRIVE : string;
+    TEMP : string;
+    USERPROFILE : string;
+    INSTDRIVE : string;
+    DESKTOP : string;
+    STARTMENU : string;
+    DESKTOP_ALLUSERS : string;
+    STARTMENU_ALLUSERS : string;
+    STARTUP : string;
+    APPDATA : String;
+    PROGRAMDATA : string;
+    ALLUSERSPROFILE : string;
+  end;
+  {$ENDIF MSWINDOWS}
+
+  TFileHelper = record helper for TFile
+    class function IsInUse(const FileName : string) : Boolean; static;
+    class function GetSize(const FileName: String): Int64; static;
+  end;
+
+  TDirectoryHelper = record helper for TDirectory
+    class function GetSize(const Path: String): Int64; static;
+  end;
+
+  TTextFileOperation = (tfOpenRead,tfOpenOverwrite,tfOpenAppend);
+
+	TTextStreamFile = class
+    private
+      fReadStream : TStreamReader;
+      fWriteStream : TStreamWriter;
+      function GetEOF : Boolean;
+    public
+      constructor Create(const FileName : string; OpenMode : TTextFileOperation);
+      destructor Destroy; override;
+      function ReadLn: string; overload;
+      function ReadLn(out Data: string): Boolean; overload;
+      procedure WriteLn (const Data : string);
+      procedure Close;
+      property EOF: Boolean read GetEOF;
+  end;
+
+  //generates a random password with complexity options
+  function RandomPassword(const PasswordLength : Integer; Complexity : TPasswordComplexity = [pfIncludeNumbers,pfIncludeSigns]) : string;
+  //extracts file extension from a filename
+  function ExtractFileNameWithoutExt(const FileName: String): String;
+  //converts a Unix path to Windows path
+  function UnixToWindowsPath(const UnixPath: string): string;
+  //converts a Windows path to Unix path
+  function WindowsToUnixPath(const WindowsPath: string): string;
+  {$IFDEF MSWINDOWS}
+  //get typical environment paths as temp, desktop, etc
+  procedure GetEnvironmentPaths;
+  function GetSpecialFolderPath(folderID : Integer) : string;
+  //checks if running on a 64bit OS
+  function Is64bitOS : Boolean;
+  //checks if is a console app
+  function IsConsole : Boolean;
+  //checks if compiled in debug mode
+  function IsDebug : Boolean;
+  //checks if running as a service
+  function IsService : Boolean;
+  //gets number of seconds without user interaction (mouse, keyboard)
+  function SecondsIdle: DWord;
+  //frees process memory not needed
+  procedure FreeUnusedMem;
+  //changes screen resolution
+  function SetScreenResolution(Width, Height: integer): Longint;
+  {$ENDIF MSWINDOWS}
+  //returns last day of current month
+  function LastDayCurrentMonth: TDateTime;
+  //returns n times a char
+  function FillStr(const C : Char; const Count : Byte) : string;
+  //returns a number leading zero
+  function Zeroes(const Number, Len : Int64) : string;
+  //converts a number to thousand delimeter string
+  function NumberToStr(const Number : Int64) : string;
+  //returns n spaces
+  function Spaces(const Count : Integer) : string;
+  //returns current date as a string
+  function NowStr : string;
+  //returns a new GUID as string
+  function NewGuidStr : string;
+  //compare a string with a wildcard pattern (? or *)
+  function IsLike(cText, Pattern: string) : Boolean;
+  //Upper case for first letter
+  function Capitalize(s: string): string;
+  function CapitalizeWords(s: string): string;
+  //returns current logged user
+  function GetLoggedUserName : string;
+  //returns computer name
+  function GetComputerName : string;
+  //Changes incorrect delims in path
+  function NormalizePathDelim(const cPath : string; const Delim : Char) : string;
+  //Removes last segment of a path
+  function RemoveLastPathSegment(cDir : string) : string;
+  //finds swith in commandline params
+  function ParamFindSwitch(const Switch : string) : Boolean;
+  //gets value for a switch if exists
+  function ParamGetSwitch(const Switch : string; var cvalue : string) : Boolean;
+  //returns app version (major & minor)
+  function GetAppVersionStr: string;
+  //returns app version full (major, minor, release & compiled)
+  function GetAppVersionFullStr: string;
+  //UTC DateTime to Local DateTime
+  function UTCToLocalTime(GMTTime: TDateTime): TDateTime;
+  //Local DateTime to UTC DateTime
+  function LocalTimeToUTC(LocalTime : TDateTime): TDateTime;
+
+var
+  {$IFDEF MSWINDOWS}
+  path : TEnvironmentPath;
+  {$ENDIF MSWINDOWS}
+
+implementation
+
+{TFileHelper}
+
+class function TFileHelper.IsInUse(const FileName : string) : Boolean;
+var
+  HFileRes: HFILE;
+begin
+  Result := False;
+  if not FileExists(FileName) then Exit;
+  try
+   HFileRes := CreateFile(PChar(FileName)
+    ,GENERIC_READ or GENERIC_WRITE
+    ,0
+    ,nil
+    ,OPEN_EXISTING
+    ,FILE_ATTRIBUTE_NORMAL
+    ,0);
+
+   Result := (HFileRes = INVALID_HANDLE_VALUE);
+
+   if not(Result) then begin
+     CloseHandle(HFileRes);
+   end;
+  except
+    Result := True;
+  end;
+end;
+
+class function TFileHelper.GetSize(const FileName: String): Int64;
+  var
+    info: TWin32FileAttributeData;
+begin
+  Result := -1;
+  if not GetFileAttributesEx(PWideChar(FileName), GetFileExInfoStandard, @info) then Exit;
+  Result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
+end;
+
+{TDirectoryHelper}
+
+class function TDirectoryHelper.GetSize(const Path: String): Int64;
+var
+  filename : string;
+begin
+  Result := -1;
+  for filename in TDirectory.GetFiles(Path) do
+  begin
+    Result := Result + TFile.GetSize(filename);
+  end;
+end;
+
+{TTextStreamFile}
+
+constructor TTextStreamFile.Create(const FileName : string; OpenMode : TTextFileOperation);
+var
+  Append : Boolean;
+begin
+  if OpenMode = tfOpenRead then fReadStream := TStreamReader.Create(FileName,True)
+  else
+  begin
+    if OpenMode = tfOpenAppend then Append := True
+      else Append := False;
+    fWriteStream := TStreamWriter.Create(FileName,Append);
+  end;
+end;
+
+destructor TTextStreamFile.Destroy;
+begin
+   if Assigned(fReadStream) then fReadStream.Free;
+   if Assigned(fWriteStream) then fWriteStream.Free;
+   inherited Destroy;
+end;
+
+function TTextStreamFile.ReadLn(out Data: string): Boolean;
+var
+   Len, Start: Integer;
+   EOLChar: ansiChar;
+begin
+   Data := fReadStream.ReadLine;
+   if Data <> '' then Result := True;
+end;
+
+function TTextStreamFile.ReadLn: string;
+begin
+   Result := fReadStream.ReadLine;
+end;
+
+procedure TTextStreamFile.WriteLn (const Data : string);
+begin
+  fWriteStream.WriteLine(Data);
+end;
+
+function TTextStreamFile.GetEOF : Boolean;
+begin
+  Result := fReadStream.EndOfStream;
+end;
+
+procedure TTextStreamFile.Close;
+begin
+  if Assigned(fReadStream) then fReadStream.Close;
+  if Assigned(fWriteStream) then fWriteStream.Close;
+end;
+
+{other functions}
+
+function RandomPassword(const PasswordLength : Integer; Complexity : TPasswordComplexity = [pfIncludeNumbers,pfIncludeSigns]) : string;
+const
+  PassAlpha  = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+  PassSigns = '@!&$';
+  PassNumbers = '1234567890';
+var
+  MinNumbers,
+  MinSigns : Integer;
+  NumNumbers,
+  NumSigns : Integer;
+begin
+  Result := '';
+  Randomize;
+  //fill all alfa
+  repeat
+    Result := Result + PassAlpha[Random(Length(PassAlpha))+1];
+  until (Length(Result) = PasswordLength);
+  //checks if need include numbers
+  if pfIncludeNumbers in Complexity then
+  begin
+    MinNumbers := Round(PasswordLength / 10 * 2);
+    NumNumbers := 0;
+    if MinNumbers = 0 then MinNumbers := 1;
+    repeat
+      Result[Random(PasswordLength)+1] := PassNumbers[Random(Length(PassNumbers))+1];
+      Inc(NumNumbers);
+    until NumNumbers = MinNumbers;
+  end;
+  //checks if need include signs
+  if pfIncludeNumbers in Complexity then
+  begin
+    MinSigns := Round(PasswordLength / 10 * 1);
+    NumSigns := 0;
+    if MinSigns = 0 then MinSigns := 1;
+    repeat
+      Result[Random(PasswordLength)+1] := PassSigns[Random(Length(PassSigns))+1];
+      Inc(NumSigns);
+    until NumSigns = MinSigns;
+  end;
+end;
+
+function ExtractFileNameWithoutExt(const FileName: String): String;
+begin
+  Result := TPath.GetFileNameWithoutExtension(FileName);
+end;
+
+function UnixToWindowsPath(const UnixPath: string): string;
+begin
+  Result:=StringReplace(UnixPath, '/', '\',[rfReplaceAll, rfIgnoreCase]);
+end;
+
+function WindowsToUnixPath(const WindowsPath: string): string;
+begin
+  Result:=StringReplace(WindowsPath, '\', '/',[rfReplaceAll, rfIgnoreCase]);
+end;
+
+{$IFDEF MSWINDOWS}
+procedure GetEnvironmentPaths;
+begin
+  //gets path
+  path.EXEPATH := TPath.GetDirectoryName(ParamStr(0));
+  path.WINDOWS := GetEnvironmentVariable('windir');
+  path.PROGRAMFILES := GetEnvironmentVariable('ProgramFiles');
+  path.COMMONFILES := GetEnvironmentVariable('CommonProgramFiles(x86)');
+  path.HOMEDRIVE := GetEnvironmentVariable('SystemDrive');
+  path.USERPROFILE := GetEnvironmentVariable('USERPROFILE');
+  path.PROGRAMDATA := GetEnvironmentVariable('ProgramData');
+  path.ALLUSERSPROFILE := GetEnvironmentVariable('AllUsersProfile');
+  path.INSTDRIVE := path.HOMEDRIVE;
+  path.TEMP := GetEnvironmentVariable('TEMP');
+  path.SYSTEM := GetSpecialFolderPath(CSIDL_SYSTEM);
+  path.DESKTOP := GetSpecialFolderPath(CSIDL_DESKTOP);
+  try
+    path.DESKTOP_ALLUSERS := GetSpecialFolderPath(CSIDL_COMMON_DESKTOPDIRECTORY);
+  except
+    path.DESKTOP_ALLUSERS := path.DESKTOP;
+  end;
+  path.STARTMENU:=GetSpecialFolderPath(CSIDL_PROGRAMS);
+  try
+    path.STARTMENU_ALLUSERS:=GetSpecialFolderPath(CSIDL_COMMON_PROGRAMS);
+  except
+    path.STARTMENU_ALLUSERS := path.STARTMENU;
+  end;
+  path.STARTUP:=GetSpecialFolderPath(CSIDL_STARTUP);
+  path.APPDATA:=GetSpecialFolderPath(CSIDL_APPDATA);
+end;
+
+function GetSpecialFolderPath(folderID : Integer) : string;
+var
+  ppidl: PItemIdList;
+begin
+  SHGetSpecialFolderLocation(0, folderID, ppidl);
+  SetLength(Result, MAX_PATH);
+  if not SHGetPathFromIDList(ppidl, PChar(Result)) then
+  begin
+    raise exception.create(Format('GetSpecialFolderPath Error: Invalid PIPL (%d)',[folderID]));
+  end;
+  SetLength(Result, lStrLen(PChar(Result)));
+end;
+
+function Is64bitOS : Boolean;
+begin
+  {$IFDEF WIN64}
+    Result := True;
+  {$ELSE}
+    Result := False;
+  {$ENDIF WIN64}
+end;
+
+function IsConsole: Boolean;
+begin
+  {$IFDEF CONSOLE}
+    Result := True;
+  {$ELSE}
+    Result := False;
+  {$ENDIF CONSOLE}
+end;
+
+function IsDebug: Boolean;
+begin
+  {$IFDEF DEBUG}
+    Result := True;
+  {$ELSE}
+    Result := False;
+  {$ENDIF DEBUG}
+end;
+
+function IsService : Boolean;
+begin
+  //only working with my Quick.AppService unit
+  try
+    Result := (IsConsole) and (GetStdHandle(STD_OUTPUT_HANDLE) = 0);
+  except
+    Result := False;
+  end;
+end;
+
+function SecondsIdle: DWord;
+var
+   liInfo: TLastInputInfo;
+begin
+   liInfo.cbSize := SizeOf(TLastInputInfo) ;
+   GetLastInputInfo(liInfo) ;
+   Result := (GetTickCount - liInfo.dwTime) DIV 1000;
+end;
+
+procedure FreeUnusedMem;
+begin
+  if Win32Platform = VER_PLATFORM_WIN32_NT then SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF);
+end;
+
+function SetScreenResolution(Width, Height: integer): Longint;
+var
+  DeviceMode: TDeviceMode;
+begin
+  with DeviceMode do
+  begin
+    dmSize := SizeOf(TDeviceMode);
+    dmPelsWidth := Width;
+    dmPelsHeight := Height;
+    dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
+  end;
+  Result := ChangeDisplaySettings(DeviceMode, CDS_UPDATEREGISTRY);
+end;
+{$ENDIF MSWINDOWS}
+
+function LastDayCurrentMonth: TDateTime;
+begin
+  Result := EncodeDate(YearOf(Now),MonthOf(Now), DaysInMonth(Now));
+end;
+
+function FillStr(const C : Char; const Count : Byte) : string;
+var
+    i   : Byte;
+begin
+  Result := '';
+  for i := 1 to Count do Result := Result + C;
+end;
+
+function Zeroes(const Number, Len : Int64) : string;
+begin
+  if Len > Length(IntToStr(Number)) then Result := FillStr('0',Len - Length(IntToStr(Number))) + IntToStr(Number)
+    else Result := IntToStr(Number);
+end;
+
+function NumberToStr(const Number : Int64) : string;
+begin
+  try
+    Result := FormatFloat('0,',Number);
+  except
+    Result := '#Error';
+  end;
+end;
+
+function Spaces(const Count : Integer) : string;
+begin
+  Result := FillStr(' ',Count);
+end;
+
+function NowStr : string;
+begin
+  Result := DateTimeToStr(Now());
+end;
+
+function NewGuidStr : string;
+var
+  guid : TGUID;
+begin
+  guid.NewGuid;
+  Result := guid.ToString
+  //GUIDToString(guid);
+end;
+
+function IsLike(cText, Pattern: string) : Boolean;
+var
+  i, n : Integer;
+  match : Boolean;
+  wildcard : Boolean;
+  CurrentPattern : Char;
+  aux : string;
+begin
+  Result := False;
+  wildcard := False;
+  cText := LowerCase(cText);
+  Pattern := LowerCase(Pattern);
+  match := False;
+
+  if (Pattern.Length > cText.Length) or (Pattern = '') then Exit;
+  if Pattern = '*' then
+  begin
+    Result := True;
+    Exit;
+  end;
+
+  for i := 1 to cText.Length do
+  begin
+    CurrentPattern := Pattern[i];
+    if CurrentPattern = '*' then wildcard := True;
+
+    if wildcard then
+    begin
+      aux := Copy(Pattern,i+1,Pattern.Length);
+      n := Pos(Copy(Pattern,i+1,Pattern.Length),cText);
+      if (n > i) or (Pattern.Length = i) then
+      begin
+        Result := True;
+        Exit;
+      end;
+    end
+    else
+    begin
+      if (cText[i] = CurrentPattern) or (CurrentPattern = '?') then match := True
+        else match := False;
+    end;
+  end;
+  Result := match;
+end;
+
+function Capitalize(s: string): string;
+begin
+  Result := '';
+  if s.Length = 0 then Exit;
+  s := LowerCase(s,loUserLocale);
+  Result := UpperCase(s[1],loUserLocale) + Trim(Copy(s, 2, s.Length));
+end;
+
+function CapitalizeWords(s: string): string;
+var
+  cword : string;
+begin
+  Result := '';
+  if s.Length = 0 then Exit;
+  s := LowerCase(s,loUserLocale);
+  for cword in s.Split([' ']) do
+  begin
+    if Result = '' then Result := Capitalize(cword)
+      else Result := Result + ' ' + Capitalize(cword);
+  end;
+end;
+
+function GetLoggedUserName : string;
+const
+  cnMaxUserNameLen = 254;
+var
+  sUserName     : string;
+  dwUserNameLen : DWord;
+begin
+  dwUserNameLen := cnMaxUserNameLen-1;
+  SetLength( sUserName, cnMaxUserNameLen );
+  GetUserName(PChar( sUserName ),dwUserNameLen );
+  SetLength( sUserName, dwUserNameLen );
+  Result := sUserName;
+end;
+
+function GetComputerName : string;
+var
+  dwLength: dword;
+begin
+  dwLength := 253;
+  SetLength(Result, dwLength+1);
+  if not Windows.GetComputerName(pchar(result), dwLength) then Result := 'Not detected!';
+  Result := pchar(result);
+end;
+
+function NormalizePathDelim(const cPath : string; const Delim : Char) : string;
+begin
+  if Delim = '\' then Result := StringReplace(cPath,'/',Delim,[rfReplaceAll])
+    else Result := StringReplace(cPath,'\',Delim,[rfReplaceAll]);
+end;
+
+function RemoveLastPathSegment(cDir : string) : string;
+var
+  posi : Integer;
+  delim : Char;
+  EndsWithDelim : Boolean;
+begin
+  if cDir.Contains('\') then delim := '\'
+    else if cDir.Contains('/') then delim := '/'
+      else
+      begin
+        Result := '';
+        Exit;
+      end;
+  NormalizePathDelim(cDir,delim);
+
+  if cDir.EndsWith(delim) then
+  begin
+    cDir := Copy(cDir,1,cDir.Length-1);
+    EndsWithDelim := True;
+  end
+  else EndsWithDelim := False;
+
+  if cDir.CountChar(delim) > 1 then posi := cDir.LastDelimiter(delim)
+    else posi := Pos(delim,cDir)-1;
+  if posi = cDir.Length then posi := 0;
+  Result := Copy(cDir,1,posi);
+  if (Result <> '') and (EndsWithDelim) then Result := Result + delim;
+end;
+
+function ParamFindSwitch(const Switch : string) : Boolean;
+begin
+  Result := FindCmdLineSwitch(Switch,['-', '/'],True);
+end;
+
+function ParamGetSwitch(const Switch : string; var cvalue : string) : Boolean;
+begin
+  Result := FindCmdLineSwitch(Switch,cvalue,True,[clstValueAppended]);
+end;
+
+function GetAppVersionStr: string;
+var
+  Rec: LongRec;
+  ver : Cardinal;
+begin
+  ver := GetFileVersion(ParamStr(0));
+  if ver <> Cardinal(-1) then
+  begin
+    Rec := LongRec(ver);
+    Result := Format('%d.%d', [Rec.Hi, Rec.Lo]);
+  end
+  else Result := '';
+end;
+
+function GetAppVersionFullStr: string;
+var
+  Exe: string;
+  Size, Handle: DWORD;
+  Buffer: TBytes;
+  FixedPtr: PVSFixedFileInfo;
+begin
+  Result := '';
+  Exe := ParamStr(0);
+  Size := GetFileVersionInfoSize(PChar(Exe), Handle);
+  if Size = 0 then
+  begin
+    //RaiseLastOSError;
+    //no version info in file
+    Exit;
+  end;
+  SetLength(Buffer, Size);
+  if not GetFileVersionInfo(PChar(Exe), Handle, Size, Buffer) then
+    RaiseLastOSError;
+  if not VerQueryValue(Buffer, '\', Pointer(FixedPtr), Size) then
+    RaiseLastOSError;
+  if (LongRec(FixedPtr.dwFileVersionLS).Hi = 0) and (LongRec(FixedPtr.dwFileVersionLS).Lo = 0) then
+  begin
+    Result := Format('%d.%d',
+    [LongRec(FixedPtr.dwFileVersionMS).Hi,   //major
+     LongRec(FixedPtr.dwFileVersionMS).Lo]); //minor
+  end
+  else if (LongRec(FixedPtr.dwFileVersionLS).Lo = 0) then
+  begin
+    Result := Format('%d.%d.%d',
+    [LongRec(FixedPtr.dwFileVersionMS).Hi,   //major
+     LongRec(FixedPtr.dwFileVersionMS).Lo,   //minor
+     LongRec(FixedPtr.dwFileVersionLS).Hi]); //release
+  end
+  else
+  begin
+    Result := Format('%d.%d.%d.%d',
+    [LongRec(FixedPtr.dwFileVersionMS).Hi,   //major
+     LongRec(FixedPtr.dwFileVersionMS).Lo,   //minor
+     LongRec(FixedPtr.dwFileVersionLS).Hi,   //release
+     LongRec(FixedPtr.dwFileVersionLS).Lo]); //build
+  end;
+end;
+
+function UTCToLocalTime(GMTTime: TDateTime): TDateTime;
+begin
+  Result :=  TTimeZone.Local.ToLocalTime(GMTTime);
+end;
+
+function LocalTimeToUTC(LocalTime : TDateTime): TDateTime;
+begin
+  Result := TTimeZone.Local.ToUniversalTime(LocalTime);
+end;
+
+
+initialization
+  try
+    GetEnvironmentPaths;
+  except
+    on E : Exception do if not IsService then raise Exception.Create(E.Message);
+  end;
+
+end.

+ 188 - 0
Quick.Config.pas

@@ -0,0 +1,188 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.Config
+  Description : Load/Save config from/to JSON file
+  Author      : Kike Pérez
+  Version     : 1.0
+  Created     : 26/01/2017
+  Modified    : 29/09/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Config;
+
+interface
+
+uses
+  System.Classes,
+  System.SysUtils,
+  {$IF CompilerVersion >= 32.0}
+  System.JSON.Types,
+  System.JSON.Serializers;
+  {$ELSE}
+  Rest.Json.Types,
+  Rest.Json;
+  {$ENDIF}
+
+type
+
+  [JsonSerialize(TJsonMemberSerialization.&Public)]
+  TAppConfig = class
+  private
+    [JSONMarshalledAttribute(False)]
+    fConfigFile : string;
+    [JSONMarshalledAttribute(False)]
+    fConfigEncrypted : Boolean;
+    [JSONMarshalledAttribute(False)]
+    fConfigPassword : string;
+  public
+    constructor Create(const ConfigFileName : string);
+    [JsonIgnoreAttribute]
+    property ConfigFile : string read fConfigFile;
+    [JsonIgnoreAttribute]
+    property ConfigEncrypted : Boolean read fConfigEncrypted write fConfigEncrypted;
+    [JsonIgnoreAttribute]
+    property ConfigPassword : string read fConfigPassword write fConfigPassword;
+    function Load(CreateIfNotExists : Boolean = False) : Boolean;
+    function Save : Boolean;
+    function AsJsonString : string;
+  end;
+
+  {Usage: create a descend class from TAppConfig and add public properties to be loaded/saved
+
+  TMyConfig = class(TAppConfig)
+  private
+    fName : string;
+    fSurname : string;
+    fStatus : Integer;
+  public
+    property Name : string read fName write fName;
+    property SurName : string read fSurname write fSurname;
+    property Status : Integer read fStatus write fStatus;
+  end;
+  }
+
+implementation
+
+
+{ TAppConfig }
+
+constructor TAppConfig.Create(const ConfigFileName : string);
+begin
+  fConfigFile := ConfigFileName;
+end;
+
+function TAppConfig.Load(CreateIfNotExists : Boolean = False) : Boolean;
+var
+  json : TStrings;
+  {$IF CompilerVersion >= 32.0}
+    Serializer : TJsonSerializer;
+  {$ENDIF}
+begin
+  if (CreateIfNotExists) and (not FileExists(fConfigFile)) then
+  begin
+    Self.Save;
+    Result := False;
+  end;
+
+  try
+    json := TStringList.Create;
+    try
+      json.LoadFromFile(fConfigFile);
+      if fConfigEncrypted then
+      begin
+        //decrypt json
+      end;
+      {$IF CompilerVersion >= 32.0}
+        Serializer := TJsonSerializer.Create;
+        try
+          Self := Serializer.Deserialize<TAppConfig>(json.Text);
+        finally
+          Serializer.Free;
+        end;
+      {$ELSE}
+        Self := TJson.JsonToObject<TAppConfig>(json.Text)
+      {$ENDIF}
+    finally
+      json.Free;
+    end;
+  except
+    on e : Exception do raise e;
+  end;
+end;
+
+function TAppConfig.Save : Boolean;
+var
+  json : TStrings;
+  {$IF CompilerVersion >= 32.0}
+    Serializer : TJsonSerializer;
+  {$ENDIF}
+begin
+  try
+    json := TStringList.Create;
+    try
+      if fConfigEncrypted then
+      begin
+        //encrypt json
+      end;
+      {$IF CompilerVersion >= 32.0}
+        Serializer := TJsonSerializer.Create;
+        try
+          Serializer.Formatting := TJsonFormatting.Indented;
+          json.Text := Serializer.Serialize(Self);
+        finally
+          Serializer.Free;
+        end;
+      {$ELSE}
+        json.Text := TJson.ObjectToJsonString(Self);
+      {$ENDIF}
+      json.SaveToFile(fConfigFile);
+    finally
+      json.Free;
+    end;
+  except
+    on e : Exception do raise e;
+  end;
+end;
+
+function TAppConfig.AsJsonString : string;
+{$IF CompilerVersion >= 32.0}
+  var
+    Serializer: TJsonSerializer;
+{$ENDIF}
+begin
+  Result := '';
+  {$IF CompilerVersion >= 32.0}
+    Serializer := TJsonSerializer.Create;
+    try
+      Serializer.Formatting := TJsonFormatting.Indented;
+      Result := Serializer.Serialize(Self);
+    finally
+      Serializer.Free;
+    end;
+  {$ELSE}
+    Result := TJson.ObjectToJsonString(Self);
+  {$ENDIF}
+end;
+
+
+
+end.

+ 362 - 0
Quick.Console.pas

@@ -0,0 +1,362 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2017 Kike Pérez
+
+  Unit        : Quick.Console
+  Description : Console output with colors and optional file log
+  Author      : Kike Pérez
+  Version     : 1.5
+  Created     : 10/05/2017
+  Modified    : 17/09/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Console;
+
+{$IFDEF CONDITIONALEXPRESSIONS}
+  {$ifndef VER140}
+    {$ifndef LINUX}
+      {$define WITHUXTHEME}
+    {$endif}
+  {$endif}
+  {$IF CompilerVersion >= 17.0}
+    {$DEFINE INLINES}
+  {$ENDIF}
+  {$IF RTLVersion >= 14.0}
+    {$DEFINE HASERROUTPUT}
+  {$ENDIF}
+{$ENDIF}
+
+interface
+
+uses
+  Classes,
+  Windows,
+  Winapi.Messages,
+  System.SysUtils,
+  Quick.Commons,
+  Quick.Log;
+
+type
+
+  //text colors
+  TConsoleColor = (
+  ccBlack        = 0,
+  ccBlue         = 1,
+  ccGreen        = 2,
+  ccCyan         = 3,
+  ccRed          = 4,
+  ccMagenta      = 5,
+  ccBrown        = 6,
+  ccLightGray    = 7,
+  ccDarkGray     = 8,
+  ccLightBlue    = 9,
+  ccLightGreen   = 10,
+  ccLightCyan    = 11,
+  ccLightRed     = 12,
+  ccLightMagenta = 13,
+  ccYellow       = 14,
+  ccWhite        = 15);
+
+  TConsoleProperties = record
+    LogVerbose : TLogVerbose;
+    Log : TQuickLog;
+  end;
+
+  procedure cout(const cMsg : Integer; cEventType : TEventType); overload;
+  procedure cout(const cMsg : Double; cEventType : TEventType); overload;
+  procedure cout(const cMsg : string; cEventType : TEventType); overload;
+  procedure coutXY(x,y : Integer; const s : string);
+  procedure coutFmt(const cMsg : string; params : array of const; cEventType : TEventType);
+  procedure TextColor(Color: TConsoleColor); overload;
+  procedure TextColor(Color: Byte); overload;
+  procedure TextBackground(Color: TConsoleColor); overload;
+  procedure TextBackground(Color: Byte); overload;
+  function ClearScreen : Boolean;
+  procedure ClearLine;
+  procedure ConsoleWaitForEnterKey;
+  procedure InitConsole;
+
+
+var
+  Console : TConsoleProperties;
+  CSConsole : TRTLCriticalSection;
+  LastMode : Word;
+  TextAttr : Byte;
+  hStdOut: THandle;
+  hStdErr: THandle;
+  ConsoleRect: TSmallRect;
+  ScreenBufInfo : TConsoleScreenBufferInfo;
+
+implementation
+
+
+procedure cout(const cMsg : Integer; cEventType : TEventType);
+var
+  FmtSets : TFormatSettings;
+begin
+  try
+    FmtSets := TFormatSettings.Create;
+    FmtSets.ThousandSeparator := '.';
+    FmtSets.DecimalSeparator := ',';
+    cout(FormatFloat('0,',cMsg,FmtSets),cEventType);
+  except
+    cout(cMsg.ToString,cEventType);
+  end;
+end;
+
+procedure cout(const cMsg : Double; cEventType : TEventType);
+var
+  FmtSets : TFormatSettings;
+begin
+  try
+    FmtSets := TFormatSettings.Create;
+    FmtSets.ThousandSeparator := '.';
+    FmtSets.DecimalSeparator := ',';
+    cout(FormatFloat('.0###,',cMsg,FmtSets),cEventType);
+  except
+    cout(cMsg.ToString,cEventType);
+  end;
+end;
+
+procedure cout(const cMsg : string; cEventType : TEventType);
+begin
+  if cEventType in Console.LogVerbose then
+  begin
+    EnterCriticalSection(CSConsole);
+    try
+      if hStdOut <> 0 then
+      begin
+        case cEventType of
+          etError : TextColor(ccLightRed);
+          etInfo : TextColor(ccWhite);
+          etSuccess : TextColor(ccLightGreen);
+          etWarning : TextColor(ccYellow);
+          etDebug : TextColor(ccLightCyan);
+          etTrace : TextColor(ccLightMagenta);
+          else TextColor(ccWhite);
+        end;
+        Writeln(cMsg);
+        TextColor(LastMode);
+      end;
+    finally
+      LeaveCriticalSection(CSConsole);
+    end;
+    if Assigned(Log) then Log.Add(cMsg,cEventType);
+  end;
+end;
+
+function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
+var
+  BufferInfo: TConsoleScreenBufferInfo;
+begin
+  GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
+  Result := BufferInfo.dwCursorPosition.X;
+end;
+
+function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
+var
+  BufferInfo: TConsoleScreenBufferInfo;
+begin
+  GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
+  Result := BufferInfo.dwCursorPosition.Y;
+end;
+
+procedure SetCursorPos(NewCoord : TCoord);
+begin
+  SetConsoleCursorPosition(hStdOut, NewCoord);
+end;
+
+procedure coutXY(x,y : Integer; const s : string);
+var
+ NewCoord : TCoord;
+ LastCoord : TCoord;
+ dwCount : DWORD;
+begin
+  if hStdOut = 0 then Exit;
+
+  EnterCriticalSection(CSConsole);
+  try
+    LastCoord.X := GetCursorX;
+    LastCoord.Y := GetCursorY;
+    NewCoord.X := x;
+    NewCoord.Y := GetCursorY;
+    ClearLine;
+    SetCursorPos(NewCoord);
+    if s <> '' then Write(s)
+      else ClearLine;
+    SetCursorPos(LastCoord);
+  finally
+    LeaveCriticalSection(CSConsole);
+  end;
+end;
+
+procedure coutFmt(const cMsg : string; params : array of const; cEventType : TEventType);
+begin
+  cout(Format(cMsg,params),cEventType);
+end;
+
+procedure TextColor(Color: TConsoleColor);
+begin
+  TextColor(Integer(Color));
+end;
+
+procedure TextColor(Color: Byte);
+begin
+  if hStdOut = 0 then Exit;
+  LastMode := TextAttr;
+  TextAttr := (TextAttr and $F0) or (Color and $0F);
+  if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
+end;
+
+procedure TextBackground(Color: TConsoleColor);
+begin
+  TextBackground(Integer(Color));
+end;
+
+procedure TextBackground(Color: Byte);
+begin
+  if hStdOut = 0 then Exit;
+  LastMode := TextAttr;
+  TextAttr := (TextAttr and $0F) or ((Color shl 4) and $F0);
+  if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
+end;
+
+function ClearScreen : Boolean;
+const
+  BUFSIZE = 80*25;
+var
+  Han, Dummy: LongWord;
+  buf: string;
+  coord: TCoord;
+begin
+  Result := false;
+  Han := GetStdHandle(STD_OUTPUT_HANDLE);
+  if Han <> INVALID_HANDLE_VALUE then
+  begin
+    if SetConsoleTextAttribute(han, FOREGROUND_RED or FOREGROUND_GREEN or FOREGROUND_BLUE) then
+    begin
+      SetLength(buf,BUFSIZE);
+      FillChar(buf[1],Length(buf),' ');
+      if WriteConsole(han,PChar(buf),BUFSIZE,Dummy,nil) then
+      begin
+        coord.X := 0;
+        coord.Y := 0;
+        if SetConsoleCursorPosition(han,coord) then Result := true;
+      end;
+    end;
+  end;
+end;
+
+procedure ClearLine;
+var
+ dwWriteCoord: TCoord;
+ dwCount, dwSize: DWord;
+begin
+ if hStdOut = 0 then Exit;
+ dwWriteCoord.X := ConsoleRect.Left;
+ dwWriteCoord.Y := GetCursorY;
+ dwSize := ConsoleRect.Right - ConsoleRect.Left + 1;
+ FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
+ FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
+end;
+
+function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
+var
+  lpNumberOfEvents: DWORD;
+  lpBuffer: TInputRecord;
+  lpNumberOfEventsRead : DWORD;
+  nStdHandle: THandle;
+begin
+  Result := False;
+  nStdHandle := GetStdHandle(STD_INPUT_HANDLE);
+  lpNumberOfEvents := 0;
+  GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents);
+  if lpNumberOfEvents <> 0 then
+  begin
+    PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead);
+    if lpNumberOfEventsRead <> 0 then
+    begin
+      if lpBuffer.EventType = KEY_EVENT then
+      begin
+        if lpBuffer.Event.KeyEvent.bKeyDown and ((ExpectedKey = 0) or (lpBuffer.Event.KeyEvent.wVirtualKeyCode = ExpectedKey)) then Result := true
+          else FlushConsoleInputBuffer(nStdHandle);
+      end
+      else FlushConsoleInputBuffer(nStdHandle);
+    end;
+  end;
+end;
+
+procedure ConsoleWaitForEnterKey;
+var
+  msg: TMsg;
+begin
+  while not ConsoleKeyPressed(VK_RETURN) do
+  begin
+    {$ifndef LVCL}
+    if GetCurrentThreadID=MainThreadID then CheckSynchronize{$ifdef WITHUXTHEME}(1000){$endif}  else
+    {$endif}
+    WaitMessage;
+    while PeekMessage(msg,0,0,0,PM_REMOVE) do
+    begin
+      if Msg.Message = WM_QUIT then Exit
+      else
+      begin
+        TranslateMessage(Msg);
+        DispatchMessage(Msg);
+      end;
+    end;
+  end;
+end;
+
+procedure InitConsole;
+var
+  BufferInfo: TConsoleScreenBufferInfo;
+begin
+  Console.LogVerbose := LOG_ALL;
+  Rewrite(Output);
+  hStdOut := TTextRec(Output).Handle;
+  {$IFDEF HASERROUTPUT}
+    Rewrite(ErrOutput);
+    hStdErr := TTextRec(ErrOutput).Handle;
+  {$ELSE}
+    hStdErr := GetStdHandle(STD_ERROR_HANDLE);
+  {$ENDIF}
+  if not GetConsoleScreenBufferInfo(hStdOut, BufferInfo) then
+  begin
+    SetInOutRes(GetLastError);
+    Exit;
+  end;
+  ConsoleRect.Left := 0;
+  ConsoleRect.Top := 0;
+  ConsoleRect.Right := BufferInfo.dwSize.X - 1;
+  ConsoleRect.Bottom := BufferInfo.dwSize.Y - 1;
+  TextAttr := BufferInfo.wAttributes and $FF;
+  LastMode := 3; //CO80;
+end;
+
+initialization
+InitializeCriticalSection(CSConsole);
+//init stdout if not a service
+if GetStdHandle(STD_OUTPUT_HANDLE) <> 0 then InitConsole;
+
+finalization
+DeleteCriticalSection(CSConsole);
+
+end.

+ 168 - 0
Quick.FileMonitor.pas

@@ -0,0 +1,168 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.FileMonitor
+  Description : Watch for single file changes
+  Author      : Kike Pérez
+  Version     : 1.0
+  Created     : 11/09/2017
+  Modified    : 16/09/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.FileMonitor;
+
+interface
+
+uses
+  Classes,
+  Windows,
+  System.SysUtils,
+  System.IOUtils;
+
+type
+
+  TMonitorNotify = (mnNone, mnFileCreated, mnFileModified, mnFileDeleted);
+  TMonitorWatch = set of TMonitorNotify;
+  TFileChangeNotify = procedure(MonitorNofify : TMonitorNotify) of object;
+
+  TQuickFileMonitor = class(TThread)
+  private
+    fFileName : string;
+    fTickEvent : THandle;
+    fInterval : Integer;
+    fNotifies : TMonitorWatch;
+    fEnabled : Boolean;
+    fExists : Boolean;
+    fModifedDate : TDateTime;
+    fCurrentMonitorNotify : TMonitorNotify;
+    fOnChangeNotify : TFileChangeNotify;
+    procedure Execute; override;
+    procedure SetStatus(Status : Boolean);
+    procedure NotifyEvent;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property FileName : string read fFileName write fFileName;
+    property Interval : Integer read fInterval write fInterval;
+    property Notifies : TMonitorWatch read fNotifies write fNotifies;
+    property OnFileChange : TFileChangeNotify read fOnChangeNotify write fOnChangeNotify;
+    property Enabled : Boolean read fEnabled write SetStatus;
+  end;
+
+implementation
+
+constructor TQuickFileMonitor.Create;
+begin
+  inherited Create(True);
+  Self.FreeOnTerminate := False;
+  fInterval := 1000;
+  fExists := False;
+  fModifedDate := 0;
+  fCurrentMonitorNotify := mnNone;
+  fNotifies := [mnFileCreated,mnFileModified,mnFileDeleted];
+  fTickEvent := CreateEvent(nil, True, False, nil);
+  Self.Resume;
+end;
+
+destructor TQuickFileMonitor.Destroy;
+begin
+  if not Terminated then Terminate;
+  SetEvent(fTickEvent);
+  CloseHandle(fTickEvent);
+  inherited;
+end;
+
+procedure TQuickFileMonitor.Execute;
+var
+  LastModifiedDate : TDateTime;
+begin
+  inherited;
+  while not Terminated do
+  begin
+    fCurrentMonitorNotify := mnNone;
+    if WaitForSingleObject(fTickEvent,fInterval) = WAIT_TIMEOUT then
+    begin
+      if fEnabled then
+      begin
+        if TFile.Exists(fFileName) then
+        begin
+          if fExists then
+          begin
+            if mnFileModified in fNotifies then
+            begin
+              LastModifiedDate := TFile.GetLastWriteTime(fFileName);
+              if LastModifiedDate > fModifedDate then
+              begin
+                fCurrentMonitorNotify := mnFileModified;
+                fModifedDate := LastModifiedDate;
+              end;
+            end;
+          end
+          else
+          begin
+            if mnFileCreated in fNotifies then fCurrentMonitorNotify := mnFileCreated;
+            LastModifiedDate := TFile.GetLastWriteTime(fFileName);
+            fModifedDate := LastModifiedDate;
+          end;
+          fExists := True;
+        end
+        else
+        begin
+          //check if file deleted
+          if mnFileDeleted in fNotifies then
+          begin
+            if fExists then
+            begin
+              fExists := False;
+              fCurrentMonitorNotify := mnFileDeleted;
+            end;
+          end;
+        end;
+        if fCurrentMonitorNotify <> mnNone then
+        begin
+          if IsConsole then NotifyEvent
+            else Synchronize(NotifyEvent);
+        end;
+      end;
+    end;
+  end;
+end;
+
+procedure TQuickFileMonitor.SetStatus(Status : Boolean);
+begin
+  if fEnabled <> Status then
+  begin
+    fEnabled := Status;
+    //gets current values
+    if TFile.Exists(fFileName) then
+    begin
+      fExists := True;
+      fModifedDate := TFile.GetLastWriteTime(fFileName);
+    end;
+  end;
+end;
+
+procedure TQuickFileMonitor.NotifyEvent;
+begin
+  if Assigned(fOnChangeNotify) then fOnChangeNotify(fCurrentMonitorNotify);
+end;
+
+end.

+ 272 - 0
Quick.Log.pas

@@ -0,0 +1,272 @@
+{ ***************************************************************************
+
+  Copyright (c) 2016-2017 Kike Pérez
+
+  Unit        : Quick.Log
+  Description : Threadsafe Log
+  Author      : Kike Pérez
+  Version     : 1.17
+  Created     : 10/04/2016
+  Modified    : 17/09/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Log;
+
+interface
+
+uses
+  Windows,
+  Classes,
+  Quick.Commons,
+  System.SysUtils,
+  System.IOUtils;
+
+type
+
+  TMemoryLog = class
+  private
+    fLines : TStrings;
+    fText : string;
+    fEnabled : Boolean;
+    function GetLogText : string;
+  public
+    constructor Create;
+    destructor Destroy; override;
+    property Lines : TStrings read fLines write fLines;
+    property Text : string read GetLogText;
+    property Enabled : Boolean read fEnabled write fEnabled;
+  end;
+
+  TQuickLog = class
+  private
+    fLogFileName : string;
+    fCurrentDateToFileName : Boolean;
+    fHideHour : Boolean;
+    fFMTName : string;
+    fVerbose : TLogVerbose;
+    fLimitLogSize : Int64;
+    fCurrentLogSize : Int64;
+    fShowEventTypes : Boolean;
+    fShowHeaderInfo : Boolean;
+    fCheckFileInUse : Boolean;
+    fMemoryLog : TMemoryLog;
+    function GetLogFileName : string;
+    procedure WriteLog(cMsg : string);
+  public
+    property Verbose : TLogVerbose read fVerbose write fVerbose;
+    property LogFileName : string read GetLogFileName;
+    property HideHour : Boolean read fHideHour write fHideHour;
+    property ShowEventType : Boolean read fShowEventTypes write fShowEventTypes;
+    property ShowHeaderInfo : Boolean read fShowHeaderInfo write fShowHeaderInfo;
+    property CheckFileInUse : Boolean read fCheckFileInUse write fCheckFileInUse;
+    property MemoryLog : TMemoryLog read fMemoryLog write fMemoryLog;
+    constructor Create;
+    destructor Destroy; override;
+    function SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
+    procedure Add(const cMsg : string; cEventType : TEventType = etInfo); overload;
+    procedure Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TEventType = etInfo); overload;
+  end;
+
+var
+  CS : TRTLCriticalSection;
+  Log : TQuickLog;
+
+implementation
+
+
+constructor TMemoryLog.Create;
+begin
+  inherited;
+  fLines := TStringList.Create;
+  fEnabled := False;
+end;
+
+destructor TMemoryLog.Destroy;
+begin
+  if Assigned(fLines) then fLines.Free;
+  inherited;
+end;
+
+function TMemoryLog.GetLogText : string;
+begin
+  Result := fLines.Text;
+end;
+
+
+constructor TQuickLog.Create;
+begin
+  inherited;
+  fVerbose := LOG_ALL;
+  fLimitLogSize := 0;
+  fShowEventTypes := True;
+  fShowHeaderInfo := True;
+  fCheckFileInUse := False;
+  fMemoryLog := TMemoryLog.Create;
+end;
+
+destructor TQuickLog.Destroy;
+begin
+  if Assigned(fMemoryLog) then fMemoryLog.Free;
+  inherited;
+end;
+
+
+{Returns date + time in english format (to add to filename}
+function TQuickLog.GetLogFileName : string;
+begin
+  Result := '';
+  if fCurrentDateToFileName then
+  begin
+    try
+      //Checks if needs to show time or not
+      if HideHour then Result := FormatDateTime('yyyymmdd', Now())
+        else Result := FormatDateTime('yyyymmdd_hhmm', Now());
+      Result := Format(fFMTName,[Result]);
+    except
+      Result := 'Error';
+    end;
+  end
+  else Result := fLogFileName;
+end;
+
+function TQuickLog.SetLog(logname : string; AddCurrentDateToFileName : Boolean; LimitSizeInMB : Integer = 0) : Boolean;
+begin
+  if logname = '' then logname := TPath.GetDirectoryName(ParamStr(0)) + '\' + TPath.GetFileNameWithoutExtension(ParamStr(0)) + '.log';
+  fFMTName := ExtractFilePath(logname) + ExtractFileNameWithoutExt(logname) + '_%s' + ExtractFileExt(logname);
+  fHideHour := True;
+  fCurrentDateToFileName := AddCurrentDateToFileName;
+  fLogFileName := logname;
+  //Checks if logfile is too big and deletes
+  fLimitLogSize := LimitSizeInMB * 1024 * 1024;
+  if (fLimitLogSize > 0) and (TFile.Exists(logname,True)) then
+  begin
+    try
+      fCurrentLogSize := TFile.GetSize(logname);
+      if fCurrentLogSize > fLimitLogSize Then if DeleteFile(logname) then fCurrentLogSize := 0;
+    except
+      raise Exception.Create('can''t access to log file!');
+    end;
+  end;
+  //Checks if it's in use
+  if (fCheckFileInUse) and (TFile.IsInUse(logname)) Then fLogFileName := Format('%s_(1).%s',[ExtractFileNameWithoutExt(logname),ExtractFileExt(logname)]);
+
+  //writes header info
+  if fShowHeaderInfo then
+  begin
+    Self.WriteLog(FillStr('-',70));
+    Self.WriteLog(Format('Application : %s %s',[ExtractFilenameWithoutExt(ParamStr(0)),GetAppVersionFullStr]));
+    Self.WriteLog(Format('Path        : %s',[ExtractFilePath(ParamStr(0))]));
+    Self.WriteLog(Format('CPU cores   : %d',[CPUCount]));
+    Self.WriteLog(Format('OS version  : %s',[TOSVersion.ToString]));
+    Self.WriteLog(Format('Host        : %s',[GetComputerName]));
+    Self.WriteLog(Format('Username    : %s',[Trim(GetLoggedUserName)]));
+    Self.WriteLog(Format('Started     : %s',[NowStr]));
+    if IsService then Self.WriteLog('AppType     : Service')
+      else if System.IsConsole then Self.WriteLog('AppType     : Console');
+
+    if IsDebug then Self.WriteLog('Debug mode  : On');
+    Self.WriteLog(FillStr('-',70));
+  end;
+  Result := True;
+end;
+
+procedure TQuickLog.Add(const cMsg : string; cEventType : TEventType = etInfo);
+begin
+  //Check Log Verbose level
+  if cEventType in fVerbose then
+  begin
+  if fShowEventTypes then Self.WriteLog(Format('%s [%s] %s',[DateTimeToStr(Now()),EventStr[Integer(cEventType)],cMsg]))
+    else Self.WriteLog(Format('%s %s',[DateTimeToStr(Now()),cMsg]));
+  end;
+end;
+
+procedure TQuickLog.WriteLog(cMsg : string);
+var
+  stream: TFileStream;
+  logname : string;
+  LBuffer      : TBytes;
+  LByteOrderMark: TBytes;
+  LOffset      : Integer;
+  LEncoding, DestEncoding: TEncoding;
+begin
+  //Checks if need to add date to filename
+  logname := GetLogFileName;
+  EnterCriticalSection(CS);
+  try
+    cMsg := cMsg + #13#10;
+    LEncoding:= TEncoding.Unicode;
+    DestEncoding := TEncoding.Unicode;
+    SetLength(LBuffer, length(cMsg) * sizeof(char));
+    if cMsg <> '' then Move(cMsg[1], lbuffer[0], Length(lbuffer));
+    LOffset := TEncoding.GetBufferEncoding(LBuffer, LEncoding);
+    LBuffer := LEncoding.Convert(LEncoding, DestEncoding, LBuffer,
+                                  LOffset, Length(LBuffer) - LOffset);
+
+    if TFile.Exists(logname,True) then
+    begin
+      stream := TFileStream.Create(logname, fmOpenReadWrite or fmShareDenyWrite);
+      stream.Position := stream.Size;
+    end
+    else
+    begin
+      stream := TFileStream.Create(logname, fmCreate or fmShareDenyWrite);
+      LByteOrderMark := DestEncoding.GetPreamble;
+      stream.Write(LByteOrderMark[0], Length(LByteOrderMark));
+    end;
+
+    with stream do
+    begin
+      try
+        Write(LBuffer[0], Length(LBuffer));
+        fCurrentLogSize := fCurrentLogSize + Length(LBuffer);
+      finally
+        Free;
+      end;
+    end;
+
+    //writes internal log
+    if fMemoryLog.Enabled then
+    begin
+      fMemoryLog.Lines.Add(cMsg);
+    end;
+
+    //check if limits max size
+    try
+      if (fLimitLogSize > 0) and (fCurrentLogSize > fLimitLogSize) then if DeleteFile(logname) then fCurrentLogSize := 0;
+    except
+      //
+    end;
+  finally
+    LeaveCriticalSection(CS);
+  end;
+end;
+
+procedure TQuickLog.Add(const cFormat : string; cParams : array of TVarRec ; cEventType : TEventType = etInfo);
+begin
+  Self.Add(Format(cFormat,cParams),cEventType);
+end;
+
+initialization
+InitializeCriticalSection(CS);
+
+finalization
+DeleteCriticalSection(CS);
+
+end.

+ 116 - 0
Quick.Network.pas

@@ -0,0 +1,116 @@
+{ ***************************************************************************
+
+  Copyright (c) 2015-2017 Kike Pérez
+
+  Unit        : Quick.Network
+  Description : Network related functions
+  Author      : Kike Pérez
+  Version     : 1.1
+  Created     : 11/07/2017
+  Modified    : 05/10/2017
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.Network;
+
+
+interface
+
+uses
+  Classes,
+  SysUtils,
+  Math;
+
+  function IntToIPv4(IPv4: LongWord): string;
+  function IPv4ToInt(const IPv4: string) : LongWord;
+  procedure CIDRToRange(CIDR : string; out MinIP,MaxIP : string); overload;
+  procedure CIDRToRange(CIDR : string; out MinIP,MaxIP : LongWord); overload;
+  procedure GetIPRange(const cIP, cMask : string; out LowIP, HighIP : string);
+
+implementation
+
+function IntToIPv4(IPv4: LongWord): string;
+var
+  Retvar : string;
+  iSeg,iShift,
+  i, iMask : LongWord;
+begin
+  Retvar := '';
+  iShift := 24;
+  iMask := $FF000000;
+  for i := 1 to 4 do
+  begin
+    iSeg := (IPv4 and iMask) shr iShift;
+    Retvar := Retvar + IntToStr(iSeg);
+    if i < 4 then Retvar := Retvar + '.';
+    iMask := iMask shr 8;
+    dec(iShift,8);
+  end;
+  Result := Retvar;
+end;
+
+function IPv4ToInt(const IPv4: string) : LongWord;
+var
+  S : TStrings;
+begin
+  S := TStringList.Create;
+  try
+    S.Delimiter := '.';
+    S.DelimitedText := IPv4;
+    if S.Count <> 4 then raise Exception.Create('Invalid IP4 Address string');
+    Result := (StrToInt(S[0]) shl 24) + (StrToInt(S[1]) shl 16) + (StrToInt(S[2]) shl 8) + StrToInt(S[3]);
+  finally
+    S.Free;
+  end;
+end;
+
+procedure CIDRToRange(CIDR : string; out MinIP,MaxIP : string);
+var
+  Mask : Integer;
+begin
+  Mask := StrToInt(Copy(CIDR,Pos('/',CIDR)+1,CIDR.Length));
+  CIDR := Copy(CIDR,0,Pos('/',CIDR)-1);
+  MinIP := IntToIPv4((IPv4ToInt(CIDR)) and ((-1 shl (32 - Mask))));
+  MaxIP := IntToIPv4((IPv4ToInt(MinIP)) + Round(Power(2,(32 - Mask))) - 1);
+end;
+
+procedure CIDRToRange(CIDR : string; out MinIP,MaxIP : LongWord);
+var
+  Mask : Integer;
+  aux : string;
+begin
+  Mask := StrToInt(Copy(CIDR,Pos('/',CIDR)+1,CIDR.Length));
+  CIDR := Copy(CIDR,0,Pos('/',CIDR)-1);
+  aux := IntToIPv4((IPv4ToInt(CIDR)) and ((-1 shl (32 - Mask))));
+  MinIP := IPv4ToInt(aux);
+  aux := IntToIPv4((IPv4ToInt(aux)) + Round(Power(2,(32 - Mask))) - 1);
+  MaxIP := IPv4ToInt(aux);
+end;
+
+procedure GetIPRange(const cIP, cMask : string; out LowIP, HighIP : string);
+begin
+  try                      
+    LowIP := IntToIPv4((IPv4ToInt(cIP) and (IPv4ToInt(cMask)) + 1));
+    HighIP := IntToIPv4(IPv4ToInt(cIP) or not(IPv4ToInt(cMask))-1);
+  except
+    on E : Exception do raise Exception.Create(Format('GetIPRange error: %s',[e.Message]));
+  end;
+end;
+
+end.

+ 373 - 0
Quick.WebBrowser.pas

@@ -0,0 +1,373 @@
+{ ***************************************************************************
+
+  Copyright (c) 2014-2017 Kike Pérez
+
+  Unit        : Quick.WebBrowser
+  Description : Web browser functions
+  Author      : Kike Pérez
+  Version     : 1.0
+  Created     : 10/02/2014
+  Modified    : 03/11/2016
+
+  This file is part of QuickLib: https://github.com/exilon/QuickLib
+
+  Uses code parts of: Thomas Stutz
+
+ ***************************************************************************
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+ *************************************************************************** }
+
+unit Quick.WebBrowser;
+
+interface
+
+  uses
+    Classes,
+    Forms,
+    System.SysUtils,
+    SHDocVw,
+    MSHTML,
+    ActiveX,
+    Vcl.Graphics,
+    System.Variants,
+    Winapi.WinInet;
+
+  procedure WB_SetBorderColor(Sender: TObject; BorderColor: String);
+  procedure WB_SetBorderStyle(Sender: TObject; BorderStyle: String);
+  procedure WB_Set3DBorderStyle(Sender: TObject; bValue: Boolean);
+  procedure WB_SetDessignMode(Sender : TObject; bEnabled : Boolean);
+  procedure WB_SetFontColor(Sender : TObject; aColor : TColor);
+  procedure WB_SetFontBold(Sender : TObject; bEnabled : Boolean);
+  procedure WB_SetFontItalic(Sender : TObject; bEnabled : Boolean);
+  procedure WB_SetFontUnderline(Sender : TObject; bEnabled : Boolean);
+  procedure WB_SetFontFace(Sender : TObject; cFontName : string);
+  procedure WB_SetFontSize(Sender : TObject; nFontSize : Integer);
+  procedure WB_InsertImage(Sender : TObject);
+  procedure WBLoadHTML(const WebBrowser: TWebBrowser; HTMLCode: string) ;
+  function GetHTML(const wbBrowser : TWebBrowser) : string;
+  function GetHTML2(const wbBrowser : TWebBrowser) : string;
+  function GetPlainText(const Html: string): string;
+  function GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
+  procedure DeleteIECacheAll;
+  procedure DeleteIECache(filenameWildcard : string);
+
+implementation
+
+procedure WB_SetBorderColor(Sender: TObject; BorderColor: String);
+{
+  BorderColor: Can be specified in HTML pages in two ways.
+               1) by using a color name (red, green, gold, firebrick, ...)
+               2) or by using numbers to denote an RGB color value. (#9400D3, #00CED1,...)
+
+  See: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/properties/borderstyle.asp
+}
+
+var
+  Document : IHTMLDocument2;
+  Element : IHTMLElement;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  if Assigned(Document) then
+  begin
+    Element := Document.Body;
+    if Element <> nil then
+    begin
+      Element.Style.BorderColor := BorderColor;
+    end;
+  end;
+end;
+
+procedure WB_SetBorderStyle(Sender: TObject; BorderStyle: String);
+{
+  BorderStyle values:
+
+  'none'         No border is drawn
+  'dotted'       Border is a dotted line. (as of IE 5.5)
+  'dashed'       Border is a dashed line. (as of IE 5.5)
+  'solid'        Border is a solid line.
+  'double'       Border is a double line
+  'groove'       3-D groove is drawn //Está se ve perfecto en Windows 7 y Windows 8
+  'ridge'        3-D ridge is drawn
+  'inset'        3-D inset is drawn
+  'window-inset' Border is the same as inset, but is surrounded by an additional single line
+  'outset'       3-D outset is drawn
+
+  See: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/reference/properties/borderstyle.asp
+}
+
+var
+  Document : IHTMLDocument2;
+  Element : IHTMLElement;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  if Assigned(Document) then
+  begin
+    Element := Document.Body;
+    if Element <> nil then
+    begin
+      Element.Style.BorderStyle := BorderStyle;
+    end;
+  end;
+end;
+
+procedure WB_Set3DBorderStyle(Sender: TObject; bValue: Boolean);
+{
+  bValue: True: Show a 3D border style
+          False: Show no border
+}
+var
+  Document : IHTMLDocument2;
+  Element : IHTMLElement;
+  StrBorderStyle: string;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  if Assigned(Document) then
+  begin
+    Element := Document.Body;
+    if Element <> nil then
+    begin
+      case BValue of
+        False: StrBorderStyle := 'none';
+        True: StrBorderStyle := '';
+      end;
+      Element.Style.BorderStyle := StrBorderStyle;
+    end;
+  end;
+end;
+
+procedure WB_SetDessignMode(Sender : TObject; bEnabled : Boolean);
+begin
+  ((Sender as TWebBrowser).Document as IHTMLDocument2).designMode := 'On';
+end;
+
+procedure WB_SetFontColor(Sender : TObject; aColor : TColor);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('ForeColor',True,AColor);
+end;
+
+procedure WB_SetFontBold(Sender : TObject; bEnabled : Boolean);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('Bold',False,bEnabled);
+end;
+
+procedure WB_SetFontItalic(Sender : TObject; bEnabled : Boolean);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('Italic',False,bEnabled);
+end;
+
+procedure WB_SetFontUnderline(Sender : TObject; bEnabled : Boolean);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('Underline',False,bEnabled);
+end;
+
+procedure WB_SetFontFace(Sender : TObject; cFontName : string);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('FontName',False,cFontName);
+end;
+
+procedure WB_SetFontSize(Sender : TObject; nFontSize : Integer);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('FontSize',False,nFontSize);
+end;
+
+procedure WB_InsertImage(Sender : TObject);
+var
+  Document : IHTMLDocument2;
+begin
+  Document := TWebBrowser(Sender).Document as IHTMLDocument2;
+  Document.execCommand('InsertImage',True,0);
+end;
+
+procedure WBLoadHTML(const WebBrowser: TWebBrowser; HTMLCode: string) ;
+var
+   sl: TStringList;
+   ms: TMemoryStream;
+begin
+   WebBrowser.Navigate('about:blank') ;
+   while WebBrowser.ReadyState < READYSTATE_INTERACTIVE do
+    Application.ProcessMessages;
+
+   if Assigned(WebBrowser.Document) then
+   begin
+     sl := TStringList.Create;
+     try
+       ms := TMemoryStream.Create;
+       try
+         sl.Text := HTMLCode;
+         sl.SaveToStream(ms) ;
+         ms.Seek(0, 0) ;
+         (WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms)) ;
+       finally
+         ms.Free;
+       end;
+     finally
+       sl.Free;
+     end;
+   end;
+end;
+
+function GetHTML(const wbBrowser : TWebBrowser) : string;
+var
+    iall : IHTMLElement;
+begin
+  (wbBrowser.Document as IHTMLDocument2).designMode := 'Off';
+  Result := (wbBrowser.Document as IHTMLDocument2).body.toString;
+  exit;
+  if Assigned(wbBrowser.Document) then
+   begin
+     iall := (wbBrowser.Document as IHTMLDocument2).body;
+
+     while iall.parentElement <> nil do
+     begin
+       iall := iall.parentElement;
+     end;
+     Result := iall.outerHTML;
+   end;
+end;
+
+function GetHTML2(const wbBrowser : TWebBrowser) : string;
+var
+    Doc: IHTMLDocument2;
+    BodyElement: IHTMLElement;
+begin
+    Assert(Assigned(wbBrowser.Document));
+
+    if wbBrowser.Document.QueryInterface(IHTMLDocument2, Doc) = S_OK then begin
+        BodyElement := Doc.body;
+        if Assigned(BodyElement) then
+        begin
+            result := '<html>' + BodyElement.outerHTML + '</html>';
+        end;
+    end;
+end;
+
+function GetWebBrowserHTML(const WebBrowser: TWebBrowser): String;
+var
+  LStream: TStringStream;
+  Stream : IStream;
+  LPersistStreamInit : IPersistStreamInit;
+begin
+  if not Assigned(WebBrowser.Document) then exit;
+  LStream := TStringStream.Create('');
+  try
+    LPersistStreamInit := WebBrowser.Document as IPersistStreamInit;
+    Stream := TStreamAdapter.Create(LStream,soReference);
+    LPersistStreamInit.Save(Stream,true);
+    result := LStream.DataString;
+  finally
+    LStream.Free();
+  end;
+end;
+
+function GetPlainText(const Html: string): string;
+var
+DummyWebBrowser: TWebBrowser;
+Document       : IHtmlDocument2;
+DummyVar       : Variant;
+begin
+   Result := '';
+   DummyWebBrowser := TWebBrowser.Create(nil);
+   try
+     //open an blank page to create a IHtmlDocument2 instance
+     DummyWebBrowser.Navigate('about:blank');
+     Document := DummyWebBrowser.Document as IHtmlDocument2;
+     if (Assigned(Document)) then //Check the Document
+     begin
+       DummyVar      := VarArrayCreate([0, 0], varVariant); //Create a variant array to write the html code to the  IHtmlDocument2
+       DummyVar[0]   := Html; //assign the html code to the variant array
+       Document.Write(PSafeArray(TVarData(DummyVar).VArray)); //set the html in the document
+       Document.Close;
+       Result :=(Document.body as IHTMLBodyElement).createTextRange.text;//get the plain text
+     end;
+   finally
+     DummyWebBrowser.Free;
+   end;
+end;
+
+procedure DeleteIECacheAll;
+var
+  lpEntryInfo: PInternetCacheEntryInfo;
+  hCacheDir: LongWord;
+  dwEntrySize: LongWord;
+begin
+  dwEntrySize := 0;
+  FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), dwEntrySize);
+  GetMem(lpEntryInfo, dwEntrySize);
+  if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
+  hCacheDir := FindFirstUrlCacheEntry(nil, lpEntryInfo^, dwEntrySize);
+  if hCacheDir <> 0 then
+  begin
+    repeat
+      DeleteUrlCacheEntry(lpEntryInfo^.lpszSourceUrlName);
+      FreeMem(lpEntryInfo, dwEntrySize);
+      dwEntrySize := 0;
+      FindNextUrlCacheEntry(hCacheDir, TInternetCacheEntryInfo(nil^), dwEntrySize);
+      GetMem(lpEntryInfo, dwEntrySize);
+      if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
+    until not FindNextUrlCacheEntry(hCacheDir, lpEntryInfo^, dwEntrySize);
+  end;
+  FreeMem(lpEntryInfo, dwEntrySize);
+  FindCloseUrlCache(hCacheDir);
+end;
+
+//DeleteIECache('?M=P');
+procedure DeleteIECache(filenameWildcard : string);
+var
+   lpEntryInfo: PInternetCacheEntryInfo;
+   hCacheDir: LongWord;
+   dwEntrySize: LongWord;
+begin
+   dwEntrySize := 0;
+   FindFirstUrlCacheEntry(nil, TInternetCacheEntryInfo(nil^), dwEntrySize) ;
+   GetMem(lpEntryInfo, dwEntrySize) ;
+   if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
+   hCacheDir := FindFirstUrlCacheEntry(nil, lpEntryInfo^, dwEntrySize) ;
+   if hCacheDir <> 0 then
+   begin
+     repeat
+       if Pos(filenameWildcard, lpEntryInfo^.lpszSourceUrlName) > 0 then begin
+         DeleteUrlCacheEntry(lpEntryInfo^.lpszSourceUrlName) ;
+       end;
+       FreeMem(lpEntryInfo, dwEntrySize) ;
+       dwEntrySize := 0;
+       FindNextUrlCacheEntry(hCacheDir, TInternetCacheEntryInfo(nil^), dwEntrySize) ;
+       GetMem(lpEntryInfo, dwEntrySize) ;
+       if dwEntrySize > 0 then lpEntryInfo^.dwStructSize := dwEntrySize;
+     until not FindNextUrlCacheEntry(hCacheDir, lpEntryInfo^, dwEntrySize) ;
+   end;
+   FreeMem(lpEntryInfo, dwEntrySize) ;
+   FindCloseUrlCache(hCacheDir) ;
+end;
+
+
+end.

+ 142 - 1
README.md

@@ -1 +1,142 @@
-QuickLib
+**QuickLib**
+--------
+----------
+
+
+Small delphi library containing interesting and quick to implement functions, created to simplify application development.
+
+
+----------
+**Quick.AppService:** Allow a console app to run as console mode or service mode with same code simplifying debug tasks.
+
+    if not AppService.IsRunningAsService then
+    begin
+      ...your code running as console
+    end
+    else
+    begin
+      AppService.ServiceName := 'MyService';
+      AppService.DisplayName := 'MyServicesvc';
+      //you can pass an anonymous method to events
+      AppService.OnStart := procedure
+	                        begin
+                              ...your start code
+	                        end;
+	  AppService.OnExecute := YourExecuteFunction;
+	  AppService.OnStop := YourStopFunction;
+      AppService.CheckParams;
+    end;
+
+**Quick.Azure/Amazon:** Simplifies blob iteraction with Azure and Amazon Cloud Storage.
+
+    //connect to a Azure blobstorage
+    QuickAzure := TQuickAzure.Create(AzureAccountName,AzureAccountKey);
+    
+    //download a blob file to a stream
+    done := QuickAzure.GetBlob('MyContainer','MyFile.jpg',ResponseInfo,MyStream);
+    
+    //check if exists a folder
+    found := ExistFolder('MyContainer','/Public/Documents/Personal');
+    
+    //list blobs starting with a pattern (recursively or not)
+    for azBlob in ListBlobs('MyContainer','/Public/Documents',Recursive,ResponseInfo) do
+    begin
+	  if azBlob.Size > 1000 then Showmessage(azBlob.Name);
+    end;
+
+**Quick.Network:** CIDR and IP Range functions.
+
+    //convert ip string to integer
+    IPv4ToInt('192.168.1.10');
+
+	//get first and last ip of a subnet scope
+    GetIpRange('192.168.100.0','255.255.255.0',LowIp,HighIP);
+
+**Quick.Commons:** Functions frequently needed in the everyday of a developer.
+
+    //coverts UTC time TDateTime to Local date time
+    UTCToLocalTime(MyUTCTime);
+    
+    //generate a 10 char length random password with alfanumeric and signs.
+    RandomPassword(10,[pfIncludeNumbers,pfIncludeSigns]);
+    
+    //Capitalize every word of a phrase
+    CapitalizeAll('the grey fox'); //returns "The Grey Fox"
+
+**Quick.Chrono:** Chronometer and Benchmark a piece of code is simple.
+
+    //get elapsed time execution of a code part
+    Chrono := TChronometer.Create(False);
+    Chrono.Start;
+    ...code you need benchmark
+    Chrono.Stop;
+    
+    //shows elapsed time in LongTime format (2 hour(s) and 10 minute(s))
+    Showmessage(Chrono.TimeElapsed(True));
+
+    //shows elapsed time in ShortTime format (02:10:00)
+    Showmessage(Chrono.TimeElapsed(False));
+    
+**Quick.Console:** Write log messages to console with colors and more...
+
+    //define which level of output needed
+    Console.Verbose := LOG_DEBUG;
+    
+    //writes line to console in red color
+    cout('Error x',etError); 
+	
+	//writes formatted line in green color
+    coutFmt('Proccess %s finished',[ProccesName],etSuccess);
+    
+    //writes integer
+    cout(12348);
+    
+    //Connect a QuickLog and write to disk and screen with one line of code (with independent verbose levels)
+    MyQuickLog := TQuickLog.Create;
+    MyQuickLog.Verbose := LOG_ALL;
+    Console.Verbose := LOG_ONLYERRORS;
+    Console.Log := MyQuickLog;
+
+**Quick.Log:** Log to disk or memory with verbose levels and daily or max space rotation.
+
+    //write a header on start with info as running path, appname, debugmode, user, etc...
+    Log.ShowHeader := True;
+    
+    //sets log with rotation at 20MB
+    Log.SetLog('.\mylog.log',False,20);
+    
+    //write an error message
+    Log.Add('Error x',etError);
+    
+    //write formatted error message
+    Log.Add('Error is %s',[ErrorStr],etError);
+
+**Quick.Config:** Load/Save a config as json. Create a descend class from TAppConfig and add private variables will be loaded/saved.
+
+     //create a class heritage
+     TMyConfig = class(TAppConfig)
+      private
+        fName : string;
+        fSurname : string;
+        fStatus : Integer;
+      public
+        property Name : string read fName write fName;
+        property SurName : string read fSurname write fSurname;
+        property Status : Integer read fStatus write fStatus;
+      end;
+      
+      //save your config to json file
+      MyConfig.ConfigFile := '.\config.json';
+      MyConfig.Name := 'John';
+      MyConfig.Surname := 'Smith';
+      MyConfig.Save;
+
+**Quick.FileMonitor:** Monitorizes a file for changes and throws events.
+
+    FileMonitor.Filename := '.\myfile.txt';
+    //check file changes every 2 seconds
+    FileMonitor.Interval := 2000;
+    //watch for deleted or modified file events
+    FileMonitor.Notifies := [mnFileModified, mnFileDeleted)];
+    FileMonitor.OnFileChange := MyFileChangeFunction;
+    FileMonitor.Enabled := True;

+ 42 - 0
samples/QuickConfig/Main.dfm

@@ -0,0 +1,42 @@
+object MainForm: TMainForm
+  Left = 0
+  Top = 0
+  Caption = 'Quick Config Demo'
+  ClientHeight = 274
+  ClientWidth = 337
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  OnCreate = FormCreate
+  PixelsPerInch = 96
+  TextHeight = 13
+  object meInfo: TMemo
+    Left = 16
+    Top = 16
+    Width = 305
+    Height = 209
+    TabOrder = 0
+  end
+  object btnLoad: TButton
+    Left = 168
+    Top = 241
+    Width = 75
+    Height = 25
+    Caption = 'Load'
+    TabOrder = 1
+    OnClick = btnLoadClick
+  end
+  object btnSave: TButton
+    Left = 249
+    Top = 241
+    Width = 75
+    Height = 25
+    Caption = 'Save'
+    TabOrder = 2
+    OnClick = btnSaveClick
+  end
+end

+ 64 - 0
samples/QuickConfig/Main.pas

@@ -0,0 +1,64 @@
+unit Main;
+
+interface
+
+uses
+  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
+  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
+  Quick.Config, Vcl.StdCtrls;
+
+type
+
+  TWinPos = record
+  public
+    PosX : Integer;
+    PosY : Integer;
+  end;
+
+  TMyConfig = class(TAppConfig)
+  public
+    LastFilename : string;
+    WindowPos : TWinPos;
+  end;
+
+  TMainForm = class(TForm)
+    meInfo: TMemo;
+    btnLoad: TButton;
+    btnSave: TButton;
+    procedure FormCreate(Sender: TObject);
+    procedure btnLoadClick(Sender: TObject);
+    procedure btnSaveClick(Sender: TObject);
+  private
+    { Private declarations }
+  public
+    { Public declarations }
+  end;
+
+var
+  MainForm: TMainForm;
+  MyConfig : TMyConfig;
+
+implementation
+
+{$R *.dfm}
+
+procedure TMainForm.btnLoadClick(Sender: TObject);
+begin
+  MyConfig.Load(True);
+  meInfo.Lines.Text := MyConfig.AsJsonString;
+end;
+
+procedure TMainForm.btnSaveClick(Sender: TObject);
+begin
+  MyConfig.LastFilename := 'notes.txt';
+  MyConfig.WindowPos.PosX := 200;
+  MyConfig.WindowPos.PosX := 100;
+  MyConfig.Save;
+end;
+
+procedure TMainForm.FormCreate(Sender: TObject);
+begin
+  MyConfig := TMyConfig.Create('prueba.json');
+end;
+
+end.

+ 14 - 0
samples/QuickConfig/QuickConfigDemo.dpr

@@ -0,0 +1,14 @@
+program QuickConfigDemo;
+
+uses
+  Vcl.Forms,
+  Main in 'Main.pas' {MainForm};
+
+{$R *.res}
+
+begin
+  Application.Initialize;
+  Application.MainFormOnTaskbar := True;
+  Application.CreateForm(TMainForm, MainForm);
+  Application.Run;
+end.

+ 560 - 0
samples/QuickConfig/QuickConfigDemo.dproj

@@ -0,0 +1,560 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+    <PropertyGroup>
+        <ProjectGuid>{CDEBAD41-A241-457F-96FD-E367D8E3B0AE}</ProjectGuid>
+        <ProjectVersion>18.2</ProjectVersion>
+        <FrameworkType>VCL</FrameworkType>
+        <MainSource>QuickConfigDemo.dpr</MainSource>
+        <Base>True</Base>
+        <Config Condition="'$(Config)'==''">Debug</Config>
+        <Platform Condition="'$(Platform)'==''">Win32</Platform>
+        <TargetedPlatforms>1</TargetedPlatforms>
+        <AppType>Application</AppType>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
+        <Base_Win32>true</Base_Win32>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
+        <Base_Win64>true</Base_Win64>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
+        <Cfg_1>true</Cfg_1>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
+        <Cfg_1_Win32>true</Cfg_1_Win32>
+        <CfgParent>Cfg_1</CfgParent>
+        <Cfg_1>true</Cfg_1>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
+        <Cfg_2>true</Cfg_2>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''">
+        <Cfg_2_Win32>true</Cfg_2_Win32>
+        <CfgParent>Cfg_2</CfgParent>
+        <Cfg_2>true</Cfg_2>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base)'!=''">
+        <DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
+        <DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
+        <DCC_E>false</DCC_E>
+        <DCC_N>false</DCC_N>
+        <DCC_S>false</DCC_S>
+        <DCC_F>false</DCC_F>
+        <DCC_K>false</DCC_K>
+        <DCC_UsePackage>RESTComponents;FireDACIBDriver;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace>
+        <Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon>
+        <UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
+        <UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
+        <SanitizedProjectName>QuickConfigDemo</SanitizedProjectName>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Win32)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;UbuntuProgressPackage;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;JvGlobus;FireDACADSDriver;JvPluginSystem;JvMM;vacommpkgdXE11;tmsxlsdXE11;vcltouch;JvBands;vcldb;bindcompfmx;svn;Intraweb;JvJans;JvNet;inetdb;JvAppFrm;EssentialsDR;vcwdedXE11;vcwdXE11;FmxTeeUI;JvDotNetCtrls;AbbreviaVCLD;fmx;fmxdae;tmsdXE11;vclib;JvWizards;tmsexdXE11;dbexpress;IndyCore;vclx;JvPageComps;dsnap;JvDB;VCLRESTComponents;JclDeveloperTools;vclie;bindengine;DBXMySQLDriver;JvCmp;FireDACMySQLDriver;JvHMI;FireDACCommonODBC;LockBoxDR;bindcompdbx;IndyIPCommon;JvCustom;advchartdedxe11;vcl;IndyIPServer;GR32_D;JvXPCtrls;PngComponents;IndySystem;advchartdxe11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;vclimg;vacommpkgdedXE11;TeeDB;Jcl;madBasic_;JvCore;JvCrypt;FireDACPgDriver;ibmonitor;FMXTee;SevenZippro;DbxCommonDriver;JvDlgs;JvRuntimeDesign;ibxpress;Tee;JvManagedThreads;xmlrtl;ibxbindings;fmxobj;vclwinx;JvTimeFramework;rtl;GR32_R;DbxClientDriver;QuickVCL;CustomIPTransport;vcldsnap;JvSystem;JvStdCtrls;DCEF_DX10;bindcomp;appanalytics;tmswizdXE11;CoolTrayIcon_D210_XE7;IndyIPClient;bindcompvcl;TeeUI;TMSFMXPackPkgDXE11;JvDocking;dbxcds;VclSmp;JvPascalInterpreter;adortl;JclVcl;Gauge3D;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;JvControls;JvPrintPreview;Analog_XE7;JclContainers;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
+        <BT_BuildType>Debug</BT_BuildType>
+        <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
+        <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
+        <VerInfo_Locale>1033</VerInfo_Locale>
+        <Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Win64)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;vcltouch;vcldb;bindcompfmx;Intraweb;inetdb;EssentialsDR;vcwdXE11;FmxTeeUI;AbbreviaVCLD;fmx;fmxdae;tmsdXE11;vclib;tmsexdXE11;dbexpress;IndyCore;vclx;dsnap;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACMySQLDriver;FireDACCommonODBC;bindcompdbx;IndyIPCommon;vcl;IndyIPServer;IndySystem;advchartdxe11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;vclimg;TeeDB;FireDACPgDriver;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;Tee;xmlrtl;ibxbindings;fmxobj;vclwinx;rtl;DbxClientDriver;QuickVCL;CustomIPTransport;vcldsnap;DCEF_DX10;bindcomp;appanalytics;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_1)'!=''">
+        <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
+        <DCC_DebugDCUs>true</DCC_DebugDCUs>
+        <DCC_Optimize>false</DCC_Optimize>
+        <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
+        <DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
+        <DCC_RemoteDebug>true</DCC_RemoteDebug>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
+        <DCC_RemoteDebug>false</DCC_RemoteDebug>
+        <AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
+        <AppEnableHighDPI>true</AppEnableHighDPI>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_2)'!=''">
+        <DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
+        <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
+        <DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
+        <DCC_DebugInformation>0</DCC_DebugInformation>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_2_Win32)'!=''">
+        <AppEnableRuntimeThemes>true</AppEnableRuntimeThemes>
+        <AppEnableHighDPI>true</AppEnableHighDPI>
+    </PropertyGroup>
+    <ItemGroup>
+        <DelphiCompile Include="$(MainSource)">
+            <MainSource>MainSource</MainSource>
+        </DelphiCompile>
+        <DCCReference Include="Main.pas">
+            <Form>MainForm</Form>
+            <FormType>dfm</FormType>
+        </DCCReference>
+        <BuildConfiguration Include="Release">
+            <Key>Cfg_2</Key>
+            <CfgParent>Base</CfgParent>
+        </BuildConfiguration>
+        <BuildConfiguration Include="Base">
+            <Key>Base</Key>
+        </BuildConfiguration>
+        <BuildConfiguration Include="Debug">
+            <Key>Cfg_1</Key>
+            <CfgParent>Base</CfgParent>
+        </BuildConfiguration>
+    </ItemGroup>
+    <ProjectExtensions>
+        <Borland.Personality>Delphi.Personality.12</Borland.Personality>
+        <Borland.ProjectType>Application</Borland.ProjectType>
+        <BorlandProject>
+            <Delphi.Personality>
+                <Source>
+                    <Source Name="MainSource">QuickConfigDemo.dpr</Source>
+                </Source>
+            </Delphi.Personality>
+            <Deployment Version="3">
+                <DeployFile LocalName="Win32\Debug\QuickConfigDemo.exe" Configuration="Debug" Class="ProjectOutput">
+                    <Platform Name="Win32">
+                        <RemoteName>QuickConfigDemo.exe</RemoteName>
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployClass Name="AdditionalDebugSymbols">
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidClassesDexFile">
+                    <Platform Name="Android">
+                        <RemoteDir>classes</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidGDBServer">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidLibnativeArmeabiFile">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidLibnativeMipsFile">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\mips</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidServiceOutput">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidSplashImageDef">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidSplashStyles">
+                    <Platform Name="Android">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_DefaultAppIcon">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon144">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon36">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-ldpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon48">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-mdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon72">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-hdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon96">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage426">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-small</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage470">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-normal</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage640">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-large</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage960">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xlarge</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DebugSymbols">
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DependencyFramework">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.framework</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DependencyModule">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                        <Extensions>.dll;.bpl</Extensions>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Required="true" Name="DependencyPackage">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                        <Extensions>.bpl</Extensions>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="File">
+                    <Platform Name="Android">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice32">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\Resources\StartUp\</RemoteDir>
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1024">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1536">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2048">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch768">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch320">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch640">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch640x1136">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectAndroidManifest">
+                    <Platform Name="Android">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSDeviceDebug">
+                    <Platform Name="iOSDevice32">
+                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSDeviceResourceRules">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSEntitlements">
+                    <Platform Name="iOSDevice32">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSInfoPList">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSResource">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXEntitlements">
+                    <Platform Name="OSX32">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXInfoPList">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXResource">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\Resources</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Required="true" Name="ProjectOutput">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Linux64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectUWPManifest">
+                    <Platform Name="Win32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="UWP_DelphiLogo150">
+                    <Platform Name="Win32">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="UWP_DelphiLogo44">
+                    <Platform Name="Win32">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
+            </Deployment>
+            <Platforms>
+                <Platform value="Win32">True</Platform>
+                <Platform value="Win64">False</Platform>
+            </Platforms>
+        </BorlandProject>
+        <ProjectFileVersion>12</ProjectFileVersion>
+    </ProjectExtensions>
+    <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
+    <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
+    <Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
+</Project>

BIN
samples/QuickConfig/QuickConfigDemo.res


+ 1 - 0
samples/QuickConfig/Win32/Debug/prueba.json

@@ -0,0 +1 @@
+{"lastFilename":"notes.txt","windowPos":[100,0]}

+ 31 - 0
samples/QuickNetwork/GetIPRangeSample.dpr

@@ -0,0 +1,31 @@
+program GetIPRangeSample;
+
+{$APPTYPE CONSOLE}
+
+{$R *.res}
+
+uses
+  System.SysUtils,
+  Quick.Commons,
+  Quick.Network,
+  Quick.Console;
+
+var
+  Ip,
+  Mask : string;
+  LowIP,
+  HighIp : string;
+
+begin
+  try
+    Console.LogVerbose := LOG_DEBUG;
+    ip := '192.168.1.15';
+    mask := '255.255.255.0';
+    GetIpRange(Ip,Mask,LowIP,HighIp);
+    coutFmt('IP: %s Mask: %s / Range: %s to %s',[Ip,Mask,LowIP,HighIp],etInfo);
+    ConsoleWaitForEnterKey;
+  except
+    on E: Exception do
+      Writeln(E.ClassName, ': ', E.Message);
+  end;
+end.

+ 622 - 0
samples/QuickNetwork/GetIPRangeSample.dproj

@@ -0,0 +1,622 @@
+<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+    <PropertyGroup>
+        <ProjectGuid>{36FE2FD1-B3D8-464C-BE25-24E59EADE7E3}</ProjectGuid>
+        <ProjectVersion>18.2</ProjectVersion>
+        <FrameworkType>None</FrameworkType>
+        <MainSource>GetIPRangeSample.dpr</MainSource>
+        <Base>True</Base>
+        <Config Condition="'$(Config)'==''">Debug</Config>
+        <Platform Condition="'$(Platform)'==''">Win32</Platform>
+        <TargetedPlatforms>1</TargetedPlatforms>
+        <AppType>Console</AppType>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Android' and '$(Base)'=='true') or '$(Base_Android)'!=''">
+        <Base_Android>true</Base_Android>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='iOSDevice32' and '$(Base)'=='true') or '$(Base_iOSDevice32)'!=''">
+        <Base_iOSDevice32>true</Base_iOSDevice32>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='iOSDevice64' and '$(Base)'=='true') or '$(Base_iOSDevice64)'!=''">
+        <Base_iOSDevice64>true</Base_iOSDevice64>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='iOSSimulator' and '$(Base)'=='true') or '$(Base_iOSSimulator)'!=''">
+        <Base_iOSSimulator>true</Base_iOSSimulator>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='OSX32' and '$(Base)'=='true') or '$(Base_OSX32)'!=''">
+        <Base_OSX32>true</Base_OSX32>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''">
+        <Base_Win32>true</Base_Win32>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''">
+        <Base_Win64>true</Base_Win64>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''">
+        <Cfg_1>true</Cfg_1>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''">
+        <Cfg_1_Win32>true</Cfg_1_Win32>
+        <CfgParent>Cfg_1</CfgParent>
+        <Cfg_1>true</Cfg_1>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''">
+        <Cfg_2>true</Cfg_2>
+        <CfgParent>Base</CfgParent>
+        <Base>true</Base>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base)'!=''">
+        <DCC_DcuOutput>.\$(Platform)\$(Config)</DCC_DcuOutput>
+        <DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
+        <DCC_E>false</DCC_E>
+        <DCC_N>false</DCC_N>
+        <DCC_S>false</DCC_S>
+        <DCC_F>false</DCC_F>
+        <DCC_K>false</DCC_K>
+        <DCC_UsePackage>RESTComponents;FireDACIBDriver;FireDACCommon;RESTBackendComponents;soapserver;CloudService;FireDACCommonDriver;inet;FireDAC;FireDACSqliteDriver;soaprtl;soapmidas;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace>
+        <SanitizedProjectName>GetIPRangeSample</SanitizedProjectName>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Android)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;CoolTrayIcon_D210_XE7;IndyIPClient;dbxcds;Gauge3D;dsnapxml;dbrtl;IndyProtocols;$(DCC_UsePackage)</DCC_UsePackage>
+        <Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
+        <Android_LauncherIcon48>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_48x48.png</Android_LauncherIcon48>
+        <Android_LauncherIcon72>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_72x72.png</Android_LauncherIcon72>
+        <Android_LauncherIcon96>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_96x96.png</Android_LauncherIcon96>
+        <Android_LauncherIcon144>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_144x144.png</Android_LauncherIcon144>
+        <Android_SplashImage426>$(BDS)\bin\Artwork\Android\FM_SplashImage_426x320.png</Android_SplashImage426>
+        <Android_SplashImage470>$(BDS)\bin\Artwork\Android\FM_SplashImage_470x320.png</Android_SplashImage470>
+        <Android_SplashImage640>$(BDS)\bin\Artwork\Android\FM_SplashImage_640x480.png</Android_SplashImage640>
+        <Android_SplashImage960>$(BDS)\bin\Artwork\Android\FM_SplashImage_960x720.png</Android_SplashImage960>
+        <EnabledSysJars>android-support-v4.dex.jar;cloud-messaging.dex.jar;fmx.dex.jar;google-analytics-v2.dex.jar;google-play-billing.dex.jar;google-play-licensing.dex.jar;google-play-services.dex.jar</EnabledSysJars>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_iOSDevice32)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_iOSDevice64)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;vacommpkgdXE11;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;TMSFMXPackPkgDXE11;dbxcds;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_iOSSimulator)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;FmxTeeUI;fmx;dbexpress;IndyCore;dsnap;bindengine;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;dbrtl;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_OSX32)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;tethering;bindcompfmx;inetdb;FmxTeeUI;fmx;fmxdae;dbexpress;IndyCore;dsnap;bindengine;DBXMySQLDriver;FireDACMySQLDriver;FireDACCommonODBC;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDACPgDriver;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;xmlrtl;ibxbindings;fmxobj;rtl;DbxClientDriver;CustomIPTransport;bindcomp;IndyIPClient;dbxcds;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_ConsoleTarget>true</DCC_ConsoleTarget>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Win32)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;UbuntuProgressPackage;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;svnui;JvGlobus;FireDACADSDriver;JvPluginSystem;JvMM;vacommpkgdXE11;tmsxlsdXE11;vcltouch;JvBands;vcldb;bindcompfmx;svn;Intraweb;JvJans;JvNet;inetdb;JvAppFrm;EssentialsDR;vcwdedXE11;vcwdXE11;FmxTeeUI;JvDotNetCtrls;AbbreviaVCLD;fmx;fmxdae;tmsdXE11;vclib;JvWizards;tmsexdXE11;dbexpress;IndyCore;vclx;JvPageComps;dsnap;JvDB;VCLRESTComponents;JclDeveloperTools;vclie;bindengine;DBXMySQLDriver;JvCmp;FireDACMySQLDriver;JvHMI;FireDACCommonODBC;LockBoxDR;bindcompdbx;IndyIPCommon;JvCustom;advchartdedxe11;vcl;IndyIPServer;GR32_D;JvXPCtrls;PngComponents;IndySystem;advchartdxe11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;vclimg;vacommpkgdedXE11;TeeDB;Jcl;madBasic_;JvCore;JvCrypt;FireDACPgDriver;ibmonitor;FMXTee;SevenZippro;DbxCommonDriver;JvDlgs;JvRuntimeDesign;ibxpress;Tee;JvManagedThreads;xmlrtl;ibxbindings;fmxobj;vclwinx;JvTimeFramework;rtl;GR32_R;DbxClientDriver;QuickVCL;CustomIPTransport;vcldsnap;JvSystem;JvStdCtrls;DCEF_DX10;bindcomp;appanalytics;tmswizdXE11;CoolTrayIcon_D210_XE7;IndyIPClient;bindcompvcl;TeeUI;TMSFMXPackPkgDXE11;JvDocking;dbxcds;VclSmp;JvPascalInterpreter;adortl;JclVcl;Gauge3D;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;JvControls;JvPrintPreview;Analog_XE7;JclContainers;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace>
+        <BT_BuildType>Debug</BT_BuildType>
+        <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
+        <VerInfo_Locale>1033</VerInfo_Locale>
+        <DCC_ConsoleTarget>true</DCC_ConsoleTarget>
+        <UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
+        <UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Base_Win64)'!=''">
+        <DCC_UsePackage>DBXSqliteDriver;DBXInterBaseDriver;vclactnband;vclFireDAC;tethering;FireDACADSDriver;vcltouch;vcldb;bindcompfmx;Intraweb;inetdb;EssentialsDR;vcwdXE11;FmxTeeUI;AbbreviaVCLD;fmx;fmxdae;tmsdXE11;vclib;tmsexdXE11;dbexpress;IndyCore;vclx;dsnap;VCLRESTComponents;vclie;bindengine;DBXMySQLDriver;FireDACMySQLDriver;FireDACCommonODBC;bindcompdbx;IndyIPCommon;vcl;IndyIPServer;IndySystem;advchartdxe11;dsnapcon;FireDACMSAccDriver;fmxFireDAC;vclimg;TeeDB;FireDACPgDriver;ibmonitor;FMXTee;DbxCommonDriver;ibxpress;Tee;xmlrtl;ibxbindings;fmxobj;vclwinx;rtl;DbxClientDriver;QuickVCL;CustomIPTransport;vcldsnap;DCEF_DX10;bindcomp;appanalytics;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;dsnapxml;dbrtl;inetdbxpress;IndyProtocols;fmxase;$(DCC_UsePackage)</DCC_UsePackage>
+        <DCC_ConsoleTarget>true</DCC_ConsoleTarget>
+        <UWP_DelphiLogo44>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png</UWP_DelphiLogo44>
+        <UWP_DelphiLogo150>$(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png</UWP_DelphiLogo150>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_1)'!=''">
+        <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
+        <DCC_DebugDCUs>true</DCC_DebugDCUs>
+        <DCC_Optimize>false</DCC_Optimize>
+        <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames>
+        <DCC_DebugInfoInExe>true</DCC_DebugInfoInExe>
+        <DCC_RemoteDebug>true</DCC_RemoteDebug>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_1_Win32)'!=''">
+        <DCC_RemoteDebug>false</DCC_RemoteDebug>
+    </PropertyGroup>
+    <PropertyGroup Condition="'$(Cfg_2)'!=''">
+        <DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
+        <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
+        <DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
+        <DCC_DebugInformation>0</DCC_DebugInformation>
+    </PropertyGroup>
+    <ItemGroup>
+        <DelphiCompile Include="$(MainSource)">
+            <MainSource>MainSource</MainSource>
+        </DelphiCompile>
+        <BuildConfiguration Include="Release">
+            <Key>Cfg_2</Key>
+            <CfgParent>Base</CfgParent>
+        </BuildConfiguration>
+        <BuildConfiguration Include="Base">
+            <Key>Base</Key>
+        </BuildConfiguration>
+        <BuildConfiguration Include="Debug">
+            <Key>Cfg_1</Key>
+            <CfgParent>Base</CfgParent>
+        </BuildConfiguration>
+    </ItemGroup>
+    <ProjectExtensions>
+        <Borland.Personality>Delphi.Personality.12</Borland.Personality>
+        <Borland.ProjectType>Application</Borland.ProjectType>
+        <BorlandProject>
+            <Delphi.Personality>
+                <Source>
+                    <Source Name="MainSource">GetIPRangeSample.dpr</Source>
+                </Source>
+            </Delphi.Personality>
+            <Deployment Version="3">
+                <DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule">
+                    <Platform Name="OSX32">
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule">
+                    <Platform Name="iOSSimulator">
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule">
+                    <Platform Name="iOSSimulator">
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule">
+                    <Platform Name="OSX32">
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployFile LocalName="Win32\Debug\GetIPRangeSample.exe" Configuration="Debug" Class="ProjectOutput">
+                    <Platform Name="Win32">
+                        <RemoteName>GetIPRangeSample.exe</RemoteName>
+                        <Overwrite>true</Overwrite>
+                    </Platform>
+                </DeployFile>
+                <DeployClass Name="AdditionalDebugSymbols">
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidClassesDexFile">
+                    <Platform Name="Android">
+                        <RemoteDir>classes</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidGDBServer">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidLibnativeArmeabiFile">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidLibnativeMipsFile">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\mips</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidServiceOutput">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidSplashImageDef">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="AndroidSplashStyles">
+                    <Platform Name="Android">
+                        <RemoteDir>res\values</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_DefaultAppIcon">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon144">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xxhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon36">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-ldpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon48">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-mdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon72">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-hdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_LauncherIcon96">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xhdpi</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage426">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-small</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage470">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-normal</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage640">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-large</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="Android_SplashImage960">
+                    <Platform Name="Android">
+                        <RemoteDir>res\drawable-xlarge</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DebugSymbols">
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DependencyFramework">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.framework</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="DependencyModule">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                        <Extensions>.dll;.bpl</Extensions>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Required="true" Name="DependencyPackage">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                        <Extensions>.dylib</Extensions>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                        <Extensions>.bpl</Extensions>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="File">
+                    <Platform Name="Android">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice32">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\Resources\StartUp\</RemoteDir>
+                        <Operation>0</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1024">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch1536">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch2048">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPad_Launch768">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch320">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch640">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="iPhone_Launch640x1136">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectAndroidManifest">
+                    <Platform Name="Android">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSDeviceDebug">
+                    <Platform Name="iOSDevice32">
+                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSDeviceResourceRules">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSEntitlements">
+                    <Platform Name="iOSDevice32">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSInfoPList">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectiOSResource">
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXEntitlements">
+                    <Platform Name="OSX32">
+                        <RemoteDir>..\</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXInfoPList">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectOSXResource">
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\Resources</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Required="true" Name="ProjectOutput">
+                    <Platform Name="Android">
+                        <RemoteDir>library\lib\armeabi-v7a</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSDevice64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="iOSSimulator">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Linux64">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="OSX32">
+                        <RemoteDir>Contents\MacOS</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win32">
+                        <Operation>0</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="ProjectUWPManifest">
+                    <Platform Name="Win32">
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="UWP_DelphiLogo150">
+                    <Platform Name="Win32">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <DeployClass Name="UWP_DelphiLogo44">
+                    <Platform Name="Win32">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                    <Platform Name="Win64">
+                        <RemoteDir>Assets</RemoteDir>
+                        <Operation>1</Operation>
+                    </Platform>
+                </DeployClass>
+                <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/>
+                <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
+                <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
+            </Deployment>
+            <Platforms>
+                <Platform value="Android">False</Platform>
+                <Platform value="iOSDevice32">False</Platform>
+                <Platform value="iOSDevice64">False</Platform>
+                <Platform value="iOSSimulator">False</Platform>
+                <Platform value="Linux64">False</Platform>
+                <Platform value="OSX32">False</Platform>
+                <Platform value="Win32">True</Platform>
+                <Platform value="Win64">False</Platform>
+            </Platforms>
+        </BorlandProject>
+        <ProjectFileVersion>12</ProjectFileVersion>
+    </ProjectExtensions>
+    <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
+    <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/>
+    <Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/>
+</Project>

BIN
samples/QuickNetwork/GetIPRangeSample.res