Procrastination Books

"Books. I like 'em." -- bumper sticker Contrary to the title, these are not books on the art of procrastination. I've just been dragging my feet making a post about new Delphi books. Delphi Programming Projects by William Duarte Delphi is a cross-platform programming language and software development kit that supports rapid application development for Microsoft Windows, Apple Mac OS X
Read More

Persistence with Spring4D

My blog tagline is certainly no accident.  I have been interested in persistence frameworks for a long time, and thought I would use Spring4D’s Marshmellow ORM for a project.  Spring4D has been around for quite some time and just had it’s first conference in Italy so I figured the framework was mature and warranted a closer look.  I had previously used the Spring4D DI container, and decided this time to use Spring4D collections as well to avoid as much code bloat as possible. The first thing I discovered is that the Spring persistence layer aka Marshmellow has a very unfortunate name.  Trying to google it with “delphi” leads to all sorts of hits related that Android version.  The next thing I learned is that development has been put on hold as of September 2018 due to a lack of resources.  This is unfortunate, as there are not that many open source ORMs that use the newer language features of Delphi when compared C# or Java. The third thing I quickly learned is that other than the Tests, there is not a whole lot of documentation available.  There is the reference help which is really not that helpful.  It doesn’t contain descriptions of the class interactions or architecture and has no examples of usage.  Really it’s not much more informative than drilling through the code.  The best source of “getting started” help that I could find is the previous bitbucket repo. There is of course also the google groups if you need to get clarification of something, and reading previous posts can help you from running into common issues. As a newbie, it’s not clear how the [InheritenceAttribute()] works so I will have to investigate it further. I assume if the last descendant in a class hierarchy is the only one marked as an entity, it will by default have all the fields marked with the Column() attribute in all ancestor classes. Another thing that is not particularly clear from any documentation is how the underlying datatype employed by the database is determined.  There are really only 3 pertinent parameters supplied to the Column() attribute, namely length, precision and scale.  Length only applies to string types and precision, scale to numeric.  It is unclear how precision and scale effectively change the numeric datatype and it’s corresponding precision or scale, other than for Integer datatypes you specify 0,0 for precision,scale. I’m sure all this will become clear as I use the framework more, but some more thorough ‘Getting Started’ documentation would have been really nice.
Read More

How to get Login Dialogs Appearing on the TaskBar

Most of the applications I have worked on in Delphi are database apps that may present a splash form quickly followed by a login dialog.  If the user fails to authenticate, the application needs to terminate gracefully.  The only way to do so cleanly is to modify the DPR code with some conditional logic. I’ve seen scenarios where after the main form was created the login dialog was invoked and if authentication failed everything was torn down. This complicates the shutdown logic, and often didn’t work well, encouraging a call to Halt() and sometimes leaving the process in memory. Any long time Delphi user knows that messing with the generated DPR code in Delphi can cause all sorts of grief later when Delphi tries to auto create forms and add units to the uses clause.  That is out of scope for this post, suffice to say that it is possible to write something like this: var User :TUser; begin Screen.Cursor := crAppStart; try Application.Initialize; Application.MainFormOnTaskbar := True; Application.Title := 'My Secure App'; Application.CreateForm(TMainDm, MainDm); finally Screen.Cursor := crDefault; end; User := TfrmLogin.Login ( function (const UserName,Password :string) :TObject begin Result := MainDm.Session.FindWhere<TUser>( Restrictions.&And( Restrictions.Eq('UserName',UserName), Restrictions.Eq('Password',Password) ) ).FirstOrDefault; end, { UserName can be passed as first parameter so don't have to type it in all the time } ParamStr(1), 3 {credential retries available } ) as TUser; if User <> nil then begin Application.CreateForm(TfrmMain, frmMain); frmMain.CurrentUser := User; Application.Run; end; end. The problem is that the Login Dialog does not appear on the Windows Taskbar. If it is hidden behind other windows, the user may think the application has not been launched and attempt to start another instance. There is no easy way for the user to bring the login dialog to the foreground short of closing other windows that may be in front of it. Putting the form on the taskbar solves this. As a quick solution I looked at the SetMainForm method in the Vcl.Forms unit, and decided to extract the ChangeAppWindow() procedure since it is not available outside of the Forms unit. Then I simply called it from this event, and voila! a taskbar button showing the Login form. procedure TfrmLogin.FormCreate(Sender: TObject); begin ChangeAppWindow(handle,True,True); end; I’m sure there are reasons why this method is not exposed as a public TApplication class procedure, but perhaps it could be with a usage caveat.
Read More

The Story of an intern at TMSSoftware

Hello, this is the intern speaking (or writing) about his experience at TMSSoftware.
I’m a second year graduate student in programming-informatics who has been given the opportunity to do my internship here at TMSSoftware.

Getting started

My first week starting here was interesting as I had no real background experience in Delphi or Object Pascal, as the primary languages I learned at school were Java and C#. Thankfully, I had a lot of resources available, which includes the Object Pascal Handbook by Marcu Cantù. So getting started went smooth.

My internship was split up into three parts, were the first part was to study and test different REST services. The second part was based on implementing the APIs such as Google Translate and Microsoft Vision. The third part involved using the implementation in an application called TMS Vision.

TMS Vision should allow visually impaired people to be informed about their environment by simply tapping on their mobile phone and getting a spoken response in the native language. That response can be a series of keywords, or a short description on the object that has been recognized by the camera.

Used technologies

Doing my internship at TMSSoftware, I got access to the newest tools TMS had to offer.
With the TMS FNC Cloud Pack I had the foundation to build a set of classes that implement the services needed to send and receive data via REST. For the GUI, I used FNC. For those not yet familiar with ‘FNC’ , these are what we call Framework Neutral Components. This means that these components can be simultaneously used with different frameworks, are fully cross platform, can be used from Delphi, C++Builder and Lazarus and most spectacularly can be used to create web applications with TMS WEB Core. Read more about FNC for the web here.

TMS FNC Controls can be simultaneously used on these frameworks:

TMS FNC Controls can be simultaneously used on these operating systems/browsers:

TMS FNC Controls can be simultaneously used on these IDE’s:

Because TMS FNC Cloud Pack contains a core layer to handle asynchronous REST request, it became really easy to send and receive data.

Example of translating text via the Google Translate API. In the code below, the TTMSFNCSimpleCloudBase class allows you to quickly send and receive data via REST without OAuth 2.0 authentication flow requirements.

// form class private variables
private
  c: TTMSFNCSimpleCloudBase;
  target : string;

procedure TGoogleTranslateForm.Button1Click(Sender: TObject);
begin
  c.Request.Clear;
  c.Request.Host := 'https://translation.googleapis.com';
  c.Request.Path := '/language/translate/v2';
  c.Request.Query := 'q=' + TTMSFNCUtils.URLEncode(Edit1.Text) +'&format=text'+
   '&target=' + target + '&key=' + c.Authentication.Key;
  c.Request.Method := rmGET;
  c.ExecuteRequest(DoTranslate);
end;

Also parsing the request result data was very easy with TMS FNC Core, which exposes a set of ready to use functions for parsing JSON.

procedure TGoogleTranslateForm.DoGetList(const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  j, jd, ja, jav: TJSONValue;
  I: Integer;
  siso, sname: string;
  l: TLanguage;
begin
  lst.Clear;
  if ARequestResult.ResultString  ''  then
  begin
    j := TTMSFNCUtils.ParseJSON(ARequestResult.ResultString);
    try
      jd := TTMSFNCUtils.GetJSONValue(j, 'data');
      if Assigned(jd) then
      begin
        ja := TTMSFNCUtils.GetJSONValue(jd, 'languages');
        if Assigned(ja) and (ja is TJSONArray) then
        begin
          for I := 0 to TTMSFNCUtils.GetJSONArraySize (ja as TJSONArray) - 1do
          begin
            jav := TTMSFNCUtils.GetJSONArrayItem(ja as TJSONArray, I);
            if Assigned(jav) then
            begin
              siso := TTMSFNCUtils.GetJSONProp(jav, 'language');
              sname := TTMSFNCUtils.GetJSONProp(jav, 'name');

(Code snippets were written for Google Translate in the first week of the internship)
Here is an example to test the REST API for Google Translate.

Starting on components

After getting used to the different platforms, investigating and testing the various cloud services to be implemented in TMS Vision, I got started on transferring the sample code into components that work cross-platform. Doing the internship here, made this easy as the TMS FNC Cloud Pack already provided a solid base to work on, and additionally my colleagues were eager to lend their advice when I needed help.

Here is an example of how the final code looks with the Google Translate component.

procedure TGoogleTranslateForm.FormCreate(Sender: TObject);
begin
  c := TTMSFNCCloudGoogleTranslate.Create;
  TLanguage := TStringList.Create;
  c.Authentication.Key := 'My-API-Key';
  c.OnIsoLanguageList  := doLanguageList;
  c.GetSupportedLanguageList;
  c.OnTranslate := DoTranslate;
end;

procedure TGoogleTranslateForm.Button1Click(Sender: TObject);
begin
  if FTarget  '' then 
    c.Translate(Edit1.Text, FTarget)
  else 
    c.Translate(Edit1.Text) ;
end;

This shows how compact your final code can be.

TMS Vision

I decided to use the knowledge I collected to make an application that works cross-framework and cross-platform to assist people who are visually impaired. The application allowed them to be informed about the environment around them. There were some critical parts in TMS Vision related to the Google Vision and Microsoft Vision APIs that involved uploading the picture taken from the camera. The data required by Google Vision, for example, needed to be in a base64 string. This was handled by the TTMSFNCUtils.FileToBase64 function included in TMS FNC Core. Below you can see a code snippet that uses this functionality to upload the picture data to Google Vision.

procedure TTMSFNCCustomCloudGoogleVision.AnalysePicture(const AFile: TTMSFNCUtilsFile);
var
 basestring :String;
begin
  try
    basestring := TTMSFNCUtils.FileToBase64(AFile);
    Request.Clear;
    Request.Host := Service.BaseURL;
    Request.Path := '/v1/images:annotate';
    Request.Method := rmPOST;
    Request.AddHeader('Content-Type','application/json; charset=utf-8');
    Request.Query := 'key='+Authentication.Key;
    Request.PostData := '{"requests": [{"image": {"content":"'+      
    basestring+'"}'+',"features": [{"type": "LABEL_DETECTION"},{"type":    
    "OBJECT_LOCALIZATION","maxResults": 5},{"type": "WEB_DETECTION"}]}]}' ;
    ExecuteRequest({$IFDEF LCLWEBLIB}@{$ENDIF}DoRequestAnalyse);
  finally
  end;
end;

Example of how the application looks now. The layout of the app has been created with the TMS FNC UI Pack, and runs on TMS WEB Core. The application is a PWA that is installable on the device.



CONCLUSION

Right now I’m in my second half of my internship and can say I enjoyed every moment of it,
gaining access to the tools of TMSSoftware and the wide variety of FNC components, made programming in Delphi or Pascal a lot easier than I thought.

I’m still expanding my knowledge of Delphi and Object Pascal and I plan to expand the TMS Vision application with other API services such as Google speech to text to remove the need to interact with the screen manually by implementing voice commands.

Author: Brutin Bjorn

Read More

Read More

TMS FNC Cloud Pack is here!

First release!

We are excited to announce the first release of the TMS FNC Cloud Pack!

What’s included

Included in the TMS FNC Cloud Pack is the ability to create/implement your own REST service client(s). There is a small accompanying guide available after installation, that explains a few of the basics to get started. Additionally, below is a list of some of the many features that are supported in the TMS FNC Cloud Pack.

  • Create your own REST service client(s) built on top of the TMS FNC Cloud Pack core
  • One source code base to use on multiple frameworks (FMX, VCL, WEB and LCL)
  • Make GET, POST, PUT, UPDATE, DELETE and PATCH requests
  • Support for multi-part form data via a separate form data builder class
  • Built-in OAuth 2.0 authentication flow
  • Built-in URL encoding, JSON parsing, file to Base64 encoding and many more

Included in the TMS FNC Cloud Pack is a set of ready-to-use REST service client implementations that are listed below.

  • Google Tasks
  • Google Calendar
  • Google Contacts
  • Google Firebase Database
  • Google Drive
  • Google GMail
  • Google Search terms
  • Google Maps address data
  • Google Photos
  • Microsoft Onedrive
  • Outlook Calendar
  • Outlook Contacts
  • Outlook Mail
  • Apple CloudKit
  • DropBox Cloud storage
  • PayPal
  • YouTube
  • Hubic
  • myCloudData.net

<!–

  • Imgur

–>

How to create your own REST service client?

To illustrate how to create your own REST service client, we take the source code from accessing a Google service, such as Google Drive. Google Drive has a common layer in where the authentication flow and access token generation are handled to allow other services from Google implement the API’s on top of this layer.

The first step is to generate the authentication URL.

function TTMSFNCCustomCloudGoogle.GetAuthenticationURL: string;
begin
  Result := InitializeAuthenticationURL(Self);
end;

function InitializeAuthenticationURL(const ACloudBase: TTMSFNCCloudBase): string;
var
  url, sc: string;
begin
  sc := TTMSFNCCloudBaseOpen(ACloudBase).GetScopes('+', True);
  url :=
      '?scope=' + sc
    + '&state=profile'
    + '&redirect_uri='+ TTMSFNCUtils.URLEncode(ACloudBase.Authentication.CallBackURL)
    + '&response_type=code'
    + '&client_id=' + ACloudBase.Authentication.ClientID
    + '&approval_prompt=force'
    + '&access_type=offline'
    + '&hl=en';

  url := 'https://accounts.google.com/o/oauth2/auth' + url;

  Result := url;
end;

The URL is a concatenation of The ClientID, CallBackURL, some default parameters and a set of scopes, which is crucial to allow the user to identify which services is accessing which information. For Google Drive, the scopes are added in the constructor:

  Scopes.Clear;
  Scopes.Add('https://www.googleapis.com/auth/drive');
  Scopes.Add('https://www.googleapis.com/auth/drive.file');
  Scopes.Add('https://www.googleapis.com/auth/userinfo.profile');

When calling the Connect method, and the Access Token is not yet retrieved, or no longer valid, a browser is shown which allows identifying and authorizing the application which is requesting access to your files/folders and account information. Below is a screenshot of the browser and the scopes that are requested via the authorization URL.

After Clicking on the “Allow” button, the Application is redirected back to your application which runs an HTTP Server listening to the Callback URL & Port set via Authentication.CallBackURL. As soon as the HTTP Server catches the OAuth 2.0 redirect callback URL, it parses the URL and generates an authentication token. The next step is to take the authentication token and convert it to an access token:

procedure TTMSFNCCustomCloudGoogle.RetrieveAccessToken;
begin
  InitializeRetrieveAccessTokenRequest(Self);
  ExecuteRequest({$IFDEF LCLWEBLIB}@{$ENDIF}DoRetrieveAccessToken);
end;

procedure InitializeRetrieveAccessTokenRequest(const ACloudBase: TTMSFNCCloudBase);
begin
  ACloudBase.Request.Clear;
  ACloudBase.Request.Name := 'RETRIEVE ACCESS TOKEN';
  ACloudBase.Request.Host := 'https://accounts.google.com';
  ACloudBase.Request.Path := '/o/oauth2/token';
  ACloudBase.Request.Query :=  'client_id=' + ACloudBase.Authentication.ClientID
  + '&client_secret=' + ACloudBase.Authentication.Secret
  + '&redirect_uri=' + ACloudBase.Authentication.CallBackURL
  + '&code=' + ACloudBase.Authentication.AuthenticationToken
  + '&grant_type=authorization_code';
  ACloudBase.Request.Method := rmPOST;
end;

In the access token request, you’ll notice that the secret, authentication token, and callback URL are required to identify your application request and make sure the service returns the correct access token. The request is executed and automatically handled by the core layer in TMS FNC Cloud Pack. There is no need to manually parse the access token, unless the service deviates from the default OAuth 2.0 authentication flow.

After retrieving the access token, the service core layer is automatically performing a test to validate the access token and grant access to service API’s. The test needs to be handled by your service implementation. For Google Drive, the test involves calling a simple tokeninfo API endpoint to validate the tokens, but for other services, it could be retrieving the account information, or testing a retrieval of files/folders.

procedure TTMSFNCCustomCloudGoogle.TestTokens(const ATestTokensRequestResultEvent: TTMSFNCCloudBaseRequestResultEvent = nil);
begin
  InitializeTestTokensRequest(Self);
  ExecuteRequest(ATestTokensRequestResultEvent);
end;

procedure InitializeTestTokensRequest(const ACloudBase: TTMSFNCCloudBase);
begin
  ACloudBase.Request.Clear;
  ACloudBase.Request.Name := 'TEST TOKENS';
  ACloudBase.Request.Host := 'https://www.googleapis.com';
  ACloudBase.Request.Path := '/oauth2/v1/tokeninfo';
  ACloudBase.Request.Query := 'access_token=' + ACloudBase.Authentication.AccessToken;
  ACloudBase.Request.Method := rmGET;
end;

After executing the test tokens request, the service returns a JSON response which is unique for each service. For Google Drive, this is checking if the returned JSON does not have an error tag.

function TTMSFNCCustomCloudGoogle.GetTestTokensResult(
  const ARequestResult: TTMSFNCCloudBaseRequestResult): Boolean;
begin
  Result := InitializeTestTokensResult(Self, ARequestResult)
end;

function InitializeTestTokensResult(const ACloudBase: TTMSFNCCloudBase; const ARequestResult: TTMSFNCCloudBaseRequestResult): Boolean;
var
  o: TJSONValue;
  s: string;
begin
  Result := False;
  s := ARequestResult.ResultString;
  if s  '' then
  begin
    o := TTMSFNCUtils.ParseJSON(s);
    if Assigned(o) then
    begin
      Result := not Assigned(TTMSFNCUtils.GetJSONValue(o, 'error'));
      o.Free;
    end;
  end;
end;

The result is a Boolean (true/false). When the result is a true, the service is successfully authenticated, and the application can start accessing various API’s. Below is a sample that shows how to use the access token to retrieve the account drive space info.

procedure TTMSFNCCustomCloudGoogleDrive.GetAccountInfo;
begin
  Request.Clear;
  Request.Host := Service.BaseURL;
  Request.Path := FBasePath + '/about';
  Request.Query := 'access_token=' + Authentication.AccessToken;
  Request.Method := rmGET;
  Request.Name := 'GET SPACE USAGE';
  ExecuteRequest({$IFDEF LCLWEBLIB}@{$ENDIF}DoRequestGetSpaceUsage);
end;

procedure TTMSFNCCustomCloudGoogleDrive.DoRequestGetSpaceUsage(const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  o: TJSONValue;
begin
  if ARequestResult.ResultString  '' then
  begin
    o := TTMSFNCUtils.ParseJSON(ARequestResult.ResultString);

    if Assigned(o) then
    begin
      try
        FInfo.UserName := TTMSFNCUtils.GetJSONProp(o, 'displayName');
        FInfo.Quota := TTMSFNCUtils.GetJSONDoubleValue(o, 'quotaBytesTotal');
        FInfo.QuotaUsed := TTMSFNCUtils.GetJSONDoubleValue(o, 'quotaBytesUsed');

      finally
        o.Free;
      end;
    end;
  end;

  DoGetSpaceUsage(FInfo, ARequestResult);
  DoGetCurrentAccount(FInfo, ARequestResult);
end;

Demo

Included in the release is a demo that allows you to test out each service individually in one easy and intuïtive overview. Click on the service of your choice and fill in your Application Client-ID, Secret and the appropriate Callback URL registered to your application. click on Authenticate and start exploring. The demo is available for FMX, VCL, WEB and LCL.

We want your feedback!

In this blog post, we already asked you for which service(s) you wish to see an implementation. We already received a lot of feedback, and want to thank you for this. This allows us to continue development in future updates of TMS FNC Cloud Pack. For those that didn’t yet fill in the survey, please go to https://www.survio.com/survey/d/N4J9X1C8L4V7K1U7V

We want your feedback, so we can improve the TMS FNC Cloud Pack even more, so we want to ask you for a thorough testing on TMS FNC Cloud Pack and provide us with any feedback, comments, suggestions and issues you experience. This can be done via comments on this blog, or an email to support@tmssoftware.com

Read More

Read More

The TMS WebGMaps v3.1 update includes map styles and SVG markers

We’re pleased to announce TMS WebGMaps v3.1 has been released today. TMS VCL WebGMaps is our component for VCL development that exposes a myriad of geographical functions for integration in Delphi or C++Builder Windows applications while TMS FMX WebGMaps offers the same functionality for cross-platform FireMonkey applications on Windows, Mac, iOS and Android.

In this new version we introduce the functionality to display the map in a custom style. You can use the pre-defined Night Mode style or any other custom style.

The code needed for this is just one line of code:

    WebGMaps1.MapOptions.MapStyle := mstNightMode; 

The code needed for this is just two lines of code:

  WebGMaps1.MapOptions.CustomStyle.Text := '[JSON Object]';
  WebGMaps1.MapOptions.MapStyle := mstCustom;

Use the Google Maps Platform Styling Wizard as a quick way to generate your own JSON styling object.

Another new feature are SVG Markers. This technique allows to display markers with a custom shape based on a SVG path. Several pre-defined shapes are available as well as the possibility to add your own custom SVG path.

Adding markers with a custom shape is very easy as this code snippet shows:

var
  m: TMarker;
begin
  m := WebGMaps1.Markers.Add(WebGMaps1.MapOptions.DefaultLatitude, WebGMaps1.MapOptions.DefaultLongitude);
  m.Shape := msStar;
  m.ShapeColor := clYellow;
  m.ShapeScale := 2;
  WebGMaps1.UpdateMapMarker(m);

  m := WebGMaps1.Markers.Add(WebGMaps1.MapOptions.DefaultLatitude + 0.1, WebGMaps1.MapOptions.DefaultLongitude - 0.1);
  m.Shape := msStar;
  m.ShapeColor := clYellow;
  WebGMaps1.UpdateMapMarker(m);

  m := WebGMaps1.Markers.Add(WebGMaps1.MapOptions.DefaultLatitude - 0.1, WebGMaps1.MapOptions.DefaultLongitude - 0.1);
  m.Shape := msFavorite;
  WebGMaps1.UpdateMapMarker(m);

  m := WebGMaps1.Markers.Add(WebGMaps1.MapOptions.DefaultLatitude + 0.1, WebGMaps1.MapOptions.DefaultLongitude + 0.1);
  m.Shape := msHome;
  m.ShapeColor := clSkyBlue;
  m.ShapeScale := 2;
  WebGMaps1.UpdateMapMarker(m);
end;

Today’s update also includes other smaller new features and improvements.
We look forward to learn about your interesting integrations of TMS WebGMaps in your applications!

Read More

Read More

When generics and VCL controls bite you: memory overwrites when you show the control usually ending up in access violations

Recently I got bitten by the 2013 reported http://qc.embarcadero.com/wc/qcmain.aspx?d=112101 (too bad the site is gone and the WayBack machine doesn’t have it archived) as a result of [WayBack] delphi – Why do I get access violations when a control’s class name is very, very long? – Stack Overflow. It got reentered as [RSP-18399] Buffer overflow in TWinControl.CreateParams() – Embarcadero Technologies but since that requires logon, it’s not search machine indexed so it’s very hard to find unless you know where to look. So I spent a quite some time to find out what was wrong: Since Delphi 1, the [WayBack] Controls.TCreateParams Record has a 64-byte WinClassName field that’s blindingly copied by the TWinControl.CreateParams without range checking. The structure is used by the [WayBack] TWinControl.CreateWnd Method to call the Windows API [WayBack] RegisterClass function that takes a [WayBack] WNDCLASS structure with a lpszClassName field that supports up to 256 characters and it fails when it’s longer. That overwrite cause spurious other errors depending on the memory that gets overwritten. It took almost a day to figure out the cause of the error was this field, then an hour or to track that down to the long class names created by generic code. To my surprise, I found back [WayBack] This issue caused coworkers and me quite a few hours wasted:Long story short – refactor some forms/frames to class names longer than 64 chars and boom… – Stefan Glienke – Google+. As of Delphi 8 (yes, that version that a lot of people want to forget, but did bring a few good things), the structure was defined as below, and the code intialising also got improved: Params.WinClassName := ClassName; ... Params.WinClassName := Format('%s.%d', [Params.WinClassName, AppDomain.CurrentDomain.GetHashCode]); So there it’s a string that – if it is too long – will get rejected by the Windows API anyway just like the native Delphi VCL implementation should have done 20+ years ago. The sad part for FMX users: that structure and code got blindingly copied to the FMX.Controls.Win unit. {$IF DEFINED(CLR)} TCreateParams = record Caption: string; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWND; Param: IntPtr; WindowClass: TWndClassInfo; WndProc: TFNWndProc; WinClassName: string; end; {$ELSE} TCreateParams = record Caption: PChar; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWnd; Param: Pointer; WindowClass: TWndClass; WinClassName: array[0..63] of Char; end; {$ENDIF} –jeroen
Read More

When generics and VCL controls bite you: memory overwrites when you show the control usually ending up in access violations

Recently I got bitten by the 2013 reported http://qc.embarcadero.com/wc/qcmain.aspx?d=112101 (too bad the site is gone and the WayBack machine doesn’t have it archived) as a result of [WayBack] delphi – Why do I get access violations when a control’s class name is very, very long? – Stack Overflow. It got reentered as [RSP-18399] Buffer overflow in TWinControl.CreateParams() – Embarcadero Technologies but since that requires logon, it’s not search machine indexed so it’s very hard to find unless you know where to look. So I spent a quite some time to find out what was wrong: Since Delphi 1, the [WayBack] Controls.TCreateParams Record has a 64-byte WinClassName field that’s blindingly copied by the TWinControl.CreateParams without range checking. The structure is used by the [WayBack] TWinControl.CreateWnd Method to call the Windows API [WayBack] RegisterClass function that takes a [WayBack] WNDCLASS structure with a lpszClassName field that supports up to 256 characters and it fails when it’s longer. That overwrite cause spurious other errors depending on the memory that gets overwritten. It took almost a day to figure out the cause of the error was this field, then an hour or to track that down to the long class names created by generic code. To my surprise, I found back [WayBack] This issue caused coworkers and me quite a few hours wasted:Long story short – refactor some forms/frames to class names longer than 64 chars and boom… – Stefan Glienke – Google+. As of Delphi 8 (yes, that version that a lot of people want to forget, but did bring a few good things), the structure was defined as below, and the code intialising also got improved: Params.WinClassName := ClassName; ... Params.WinClassName := Format('%s.%d', [Params.WinClassName, AppDomain.CurrentDomain.GetHashCode]); So there it’s a string that – if it is too long – will get rejected by the Windows API anyway just like the native Delphi VCL implementation should have done 20+ years ago. The sad part for FMX users: that structure and code got blindingly copied to the FMX.Controls.Win unit. {$IF DEFINED(CLR)} TCreateParams = record Caption: string; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWND; Param: IntPtr; WindowClass: TWndClassInfo; WndProc: TFNWndProc; WinClassName: string; end; {$ELSE} TCreateParams = record Caption: PChar; Style: DWORD; ExStyle: DWORD; X, Y: Integer; Width, Height: Integer; WndParent: HWnd; Param: Pointer; WindowClass: TWndClass; WinClassName: array[0..63] of Char; end; {$ENDIF} –jeroen
Read More

Firemonkey/Isometric at master · tothpaul/Firemonkey

This shows you how to do 2.5D isometric projection in Delphi using Firemonkey: [WayBack] Firemonkey/Isometric at master · tothpaul/Firemonkey. [WayBack] Isometric projection – Wikipedia. Via: [WayBack] I wonder what the best approach would be to use FireMonkey to develop an isometric 2.5D game in the “classic” way… – Fl Ko – Google+ –jeroen
Read More

Changes between GExperts 1.3.12 and 1.3.13

I had to look them up myself, so it took a while but here they are, the changes, bugfixes and improvements between the GExperts 1.3.12 (released 2018-12-22) and 1.3.13 (released yesterday, 2019-03-30): Bugfix (#105): Set Tab Order expert no longer worked with Delphi 6, 7 and 2005 (Remember what I wrote about testing these versions?)Improvements to the Uses Clause Manager:New configuration setting “GExperts caching directory”, used to store the cached identifiers per unitAdditional configuration options for the expert: Caching can now be disabled and the cache can be cleared. (Bug report #104)It is now possible to use the project’s map file rather than the dpr for getting a list of used units. This includes the VCL/RTL and possibly any 3rd party units and makes the Project tab much more useful.Bugfix (#110): Entries in the VCL/RTL list were clipped / overlapped.Bugfix (#109): When loading a form’s position only, Width and Height no longer change every time.Added additional Delphi 10.2 and 10.3 warnings to the Insert Warn Directive editor expertBugfix: The number of entries for the Favourite Files expert in the registry doubled every time the list was saved.Improvements to the Grep expert:Hint about separating multiple directories with semicolon.It can now use the project’s map file rather than the dpr for getting the list of used units. This includes all units compiled in the project not just those explicitly added to it. There is a configuration option for enabling that.Ensure that the RTL and all existing subsystem Paths (VLC, FMX, CLX) are in the drop down menu.Searching DFM files can now handle strings split into multiple lines and containing special characters stored as #<number>. (Note: This is not quite finished. The display in the Results window is broken.) (Bug report #112)Custom beep as a WAV file for the Proof Reader expert (Suggested by Philip Rayment, Patch by Achim Kalwa, Bug report #111).Bugfix (#113): Added another check for duplicate GExperts DLLs being loaded into the same IDE to prevent error message “PrivateGXMenuActionManager is not nil upon creation”Project Dependency expert:Dramatically improved performance for the indirect dependencies tab. The strings and string lists are no longer stored in and read from the UI. Also string lists are now sorted so look ups are much faster.Units in the project (root node of the tree view are now also listed)New configuration option to search for units in the library path and also in the browsing path.Bugfix (#114): Disabled editor experts still blocked the associated keyboard shortcut and could be called with this shortcut.Bugfix (#117): Editor experts could be executed even if not the code editor but the form editor was active. As you can see above, I am looking into bugs reported on Sourceforge and also feature suggestions posted there. That is my preferred way of users to report bugs and request features because it is easy for me to manage them in one place. The other channels (email via the GExperts “Send a Bug Report / Suggestion” button and the GExperts subforum in the international Delphi Praxis forum) work too, but they are much less convenient for me and some reports / requests may get lost. Also, please remember that I am working on this project in my spare time, so any additional work, e.g. creating bug reports on Sourceforge from emails I receive, takes time in which I am not working on the actual software. It’s also boring work which might discourage me from doing it and if it is getting out of hand might even make me stop working on GExperts at all. Let me close this post by thanking all those people who have worked on GExperts before me and on whose work I have been building over the years (and whose work I have also been using as a GExperts user for more than two decades as a professional software developer). And also those who continue to contribute their work and some money to this project. Discussion about this post in the international Delphi Praxis forum.
Read More

ICS V8.60 has been released

ICS V8.60 has been released at: http://wiki.overbyte.eu/wiki/index.php/ICS_Download ICS is a free internet component library for Delphi 7, 2006 to 2010, XE  to XE8, 10 Seattle, 10.1 Berlin, 10.2 Tokyo and 10.3 Rio, and C++Builder 2006 to XE3, 10.2 Tokyo and 10.3 Rio. ICS supports VCL and FMX, Win32, Win64 and MacOS targets. The distribution zip includes the latest OpenSSL 1.1.1 win32, with
Read More

Selling your Delphi apps via Microsoft Store is now Cheaper

For a few years now, Delphi has had support for the Windows Dektop Bridge. This is a Microsoft technology allowing to create UWP (Universal Windows Platform) applications based on a traditional Win32/Win64 application. Delphi IDE simplifies this process (making it smoother than with Microsoft own tools) as you ca read in the docwiki. While there are still some hurdles to enable desktop bridge apps in the store (there is an manual authorization process from Microsoft) the cost to be on the store is fairly low. Over the last year Microsoft has been increasingly pushing developers towards the Windows Store (actually, now called Microsoft Store) for applications distribution on Windows -- where it competes mostly with game distribution platforms. They even released a version of the operating system, Windows 10 S, which only allows installation of apps from the store. However, this has seen a limited traction. In terms of costs, the Microsoft Store has long offered a 85% revenue share, which is generous compared to the mobile counterparts (at 70% in general). They were offering some special rates from links to the developer site. Now they have rolled out a new and simplified revenue sharing plan. In short, for anyone who buys through an external link (that is, not browsing or searching in the store application itself) the revenue share will be at 95%. This is lower than the processing, invoicing, and payment commission costs of a direct sale -- plus you have to pay for the infrastructure and more, handle updates process, and so forth. If you sell Windows applications to the public (consumers, but also companies) I suggest you to double check the Microsoft Store distribution model, given how easy we have made turning your existing VCL and FMX apps into store apps and how generous the revenue share from Microsoft has become.
Read More