My first commercial application

A DOS program with a graphical UI for controlling timber drying kilns

It is funny how coincidences affect your whole career. I was in my first year of my microprocessor design education and I was learning myself Turbo Pascal 2.0 for CP/M on the side, when a friend of my uncle landed me a summer job at Norsk Data. I learned a lot about mainframe computers during those summer months, and I got to wet my feet in languages like Fortran, COBOL. and PLANC. PLANC had many similarities to Pascal, and I really enjoyed toying around with it.

In 1986, my uncle asked if I wanted to have a go at porting a timber drying kiln program on a DEC Rainbow 100 (CP/M) to an IBM PC (DOS). Naturally, I said yes. It was not very difficult to make the port from UCSD Pascal to Turbo Pascal 3.0, although it was still a very rudimentary program with very little UI and ReadLn inputs. In 1987, Turbo Pascal 4.0 arrived with a proper IDE!

I continued to make improvements to the DOS program, such as meticulously crafting an interrupt driven serial port unit, improved input, DOS specifics, etc.

Digression: Over the years I’ve told myself that it was the Black Monday stock market crash in October ’87 that ate all the HW jobs and made me switch from designing microprocessors to writing software,

However, at that point in time, I was already enrolled for the second year of the computer science education programme (having skipped the first year) – so I guess I was hooked on software earlier.

I probably assumed it was a lot cheaper to fix faults in software programs than in hardware microcode… Yeah, right!

OOP – Mind blown

The project remained a pet project while I completed my third year, and in May 1989, Turbo Pascal 5.5 arrived with something they called object oriented programming.

I was by now a fan of structured programming and was not overly sold on the new concept. The code I wrote was still mostly structured. I fiddled with the OOP concept and started wrapping some structures and code in objects. I still hadn’t grasped the concepts of polymorphism and abstraction, so my “OOP” code was mostly about code organisation.

After about 6 months, the penny dropped, big time! Oh Emm Gee, did it ever drop!

I realized that to encapsulate objects in such a way that they were identical on the outside, but different on the inside, I could hide the implementation details and have the objects that were compatible and interchangable when seen from the code that would use them!

After that, EVERYTHING started looking like objects.

I had made a “window” system for the text mode CRT, but then I got an idea. What if I could use OOP to do the same in Graphics mode? I already wrote some graphics code to present a line chart in graphics mode. Sure – I could more or less hide all the Gfx details from the core logic, and voila – I had something that looked a lot better than text mode.

A few years back – uh – 13 years is a few, isn’t it – I checked if I still could get the code to run – and in a DOSBox, I was able to run the app in it’s demo mode and capture it 🙂

With permission from Alfsen og Gunderson, here is the actual source code: https://github.com/LarsFosdal/DOSTimberDryingKiln

Alfsen og Gunderson

The final exams were in June 1990, and I was suddenly in the job market. The company my uncle worked for, Alfsen og Gunderson (AG) – an engineering company – wanted me to take on a two year engagement, with the purpose of refining the kiln control program. They also said that if they were satisfied, they would offer me a permanent position.

Naturally, I accepted – and in July 1990, I started with the improvements on what up until then had been a on-the-side-of-studies project.

After about 15 months, in the autumn of 1991 – I had a feature complete, graphical UI, configurable number of kilns and tunnels, multi PLC, multi printer, multi resolution Timber Drying Kiln control program, installed at multiple locations around Norway, bearing the name P4 for Program 4. P3 was the previous text version. P2 the first port and P1 the original DEC program.

I had also written a control program for a 20 meter long fishing trawl fixation stretch bench. You dip the “knitted” net in a liquid rubber-like solution, and put it under high tension, close a lid over it, and heat it up for the solution to dry and hold all the knots in the net in position. The control program defined the heat, tension and time parameters.

When I went to ReFa (Redskapsfabrikken) in Finnsnes in Northern Norway to install the fixation control program and make the final adjustments, I discovered that I had forgotten to copy a newly written unit onto my diskettes, and the program would not compile. I spent the night rewriting the unit from scratch and that ended up being the final version of that unit.

Black Monday

But – Black Monday and the slump in the economy did impact the business of AG. They had shrunk from 150 employees in 1990 to 50-some in the autumn of 1991, and they had been bought by ABB (Asea Brown Boveri). So, I got the sad message that due to the dire financial situitation, they were unable to uphold their promise of a permanent position and I was given notice two months before Xmas 1991 that I only had employment out the year. Bummer!

Anyways, I stumbled upon an ad for a job where they wanted someone to port a Turbo Pascal DOS application to Turbo Pascal for Windows. I applied, got the job, so I went directly from AG to Falcon Information Services.

Strictly speaking, I was not qualified, as I had never done any Windows programming, but as I said during the interview, I like learning new things – so I got the job. But – that is a story for another time.

I did do some consulting for AG until early 1993. When they in the late ’90s were replacing my program with an off-the-shelf solution for control programs, one of my former colleagues at AG said they had not realized the amount of thought and work that had gone into my programs until then. I have to say, that acknowledgement felt good.

Other fun features of P4

All the text in the application, such as help texts and prompts, is kept in a .mes file which is verified for link integrity and “compiled” into a binary file. I like to think of that as my version of HyperText cards – as well as fairly forward looking with regards to translation and modification of the texts, without need for changing the application code.

In addition, every lead and prompt text also is in the .mes file and hence it is trivial to write a translated version of the .mes file and copy that into place to run the UI in a different language.

Although I was working largely alone on this project, it was an incredible learning journey. Studying hardware manuals, I wrote native “drivers” for EGA, VGA and Hercules video cards. I found out how to create and load custom fonts and created text render routines, light-weight windowing and menu routines, rule based input methods with character set filters, max/min values, list selection, etc.

I wrote an interrupt driven serial port “driver” for talking to the PLCs, and added an OOP layer on top that eliminated the differences in the actual protocols used to talk to each PLC.

I learned about chart rendering and printing and figured out how to draw on world coordinates in a virtual drawingboard and then map the physical coordinates of the output bitmaps (needle printers FTW) back to the world coordinates, so it was basically the same code that rendered the charts on screen and to the printer.

There is nothing quite as efficient as learning by doing. At times, it will be hard, but the more you experiment, learn and master – the easier it gets to evolve your skills. Study the books, the manuals, the articles, the source code, and write testable code – and – try to include references to the source of inspiration and documentation in your source code.

Keep Calm and Keep Coding

Post a message to Teams from Delphi using WebHooks

Want to send a message to a Teams channel from your Delphi Application?
It is easy – simply post the message to your Teams channel’s WebHook URL.

Update – added Uwe Raabe’s TRESTClient version.

Well, perhaps not these hooks.
Photo by cottonbro on Pexels.com

Here is the low down basics of how to do it from Delphi. You simply do a http post with a preformatted Json string.

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling

interface
uses
  System.Classes, System.SysUtils, System.Json, Rest.Json,
  IDGlobal, IdHTTP, IdIOHandler, IdSSL, IdSSLOpenSSL;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FHTTP: TIdHTTP;
    FURL: string;
  protected
    property HTTP: TIdHTTP read FHTTP;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;

    procedure Post(const aJson: string);
    procedure PostMessage(const aMsg: TWebhookMessage);

    property URL:String read FURL write FURL;
  end;


implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  FURL := aURL;
  // Remember that you need libeay32.dll and ssleay32.dll in the path for https to work
  FHTTP := TIdHTTP.Create(nil);
  HTTP.Request.ContentType := 'application/json';
  HTTP.Request.ContentEncoding := 'utf-8';
  HTTP.Request.CacheControl := 'no-cache';
  HTTP.HTTPOptions := HTTP.HTTPOptions - [hoForceEncodeParams] + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
end;

destructor TWebHook.Destroy;
begin
  FHTTP.Free;
  inherited;
end;

procedure TWebHook.Post(const aJson: string);
var
  ReqString: TStringList;
  ResStream: TStringStream;
begin
  ReqString := TStringList.Create;
  try
    try
      ReqString.Text := aJson;
      ResStream := TStringStream.Create;
      try
//        DebugOut('HTTP Post ' + ReqString.Text);
        HTTP.Post(URL, ReqString, ResStream, IndyTextEncoding_UTF8);
//        DebugOut('Response: ' + ResStream.DataString);
      finally
        ResStream.Free;
      end;
    except // HTTP Exception
      on E: Exception
      do begin
//        DebugOutException(E, 'HTTP Post');
      end;
    end;
  finally
    ReqString.Free;
  end;
end;

procedure TWebHook.PostMessage(const aMsg: TWebhookMessage);
begin
  try
    Post(
      TJson.ObjectToJsonString(
        aMsg,
        [joIgnoreEmptyStrings, joIgnoreEmptyArrays, joDateIsUTC, joDateFormatISO8601]
      )
    );
  finally
    aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  Text := aText;
end;

end.

How to use it. This is a very minimal example for a single text line.

procedure TestExampleToATeamsChannel;
var
  Teams: TWebhook;
begin
  Teams := TWebhook.Create('https:// ... YourLongWebHookURLHere ... ');
  try
    Teams.PostMessage(TSimpleText.Create('Hello from Delphi! :"{/=''æ}[øå] ÆØÅ'));
  finally
    Teams.Free;
  end;
end;

Make sure to read up on throttling and other rules of engagement before you start spamming your Teams channels. See the MS article on WebHooks for more detail.

Update! Uwe Raabe did a refurbished version that eliminates Indy and the need for the the OpenSLL DLLs, and makes the code work cross platform!

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling
// Uwe Raabe replaced Indy for TRESTClient, eliminating the need for the OpenSLL DLLS

interface
uses
  System.Classes, System.SysUtils,
  REST.Json, REST.Client, REST.Types;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FClient: TRESTClient;
    FRequest: TCustomRESTRequest;
    FURL: string;
  protected
    property Client: TRESTClient read FClient;
    property Request: TCustomRESTRequest read FRequest;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;
    function PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
    property URL: string read FURL write FURL;
  end;

implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  inherited Create;
  FURL := aURL;

  FClient := TRESTClient.Create(nil);
  FRequest := TCustomRESTRequest.Create(nil);
  FRequest.Client := FClient;
end;

destructor TWebHook.Destroy;
begin
  FRequest.Free;
  FClient.Free;
  inherited;
end;

function TWebHook.PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
begin
  try
    Request.Client.BaseURL := URL;
    Request.Method := rmPOST;
    Request.AddBody(aMsg);
    Request.Execute;
    Result := Request.Response.Status.Success;
  finally
    if aOwnsMsg then
      aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  inherited Create;
  FText := aText;
end;

end.

There is a known issue with a workaround for Delphi 10.2.

Have fun!

Migrating your Editor Colors to Delphi 10.4 Sydney

RAD Studio 10.4 Sydney introduces Custom themes for editor colors. Here is how to migrate your 10.3 settings to 10.4.

First, we make a custom color theme in 10.4 – go to Options | User Interface | Editor | Color.
With the current colors, just click on [Save As] and and give your personal scheme a name. 
I called mine Lars.

This creates a Registry branch, named HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars

In RegEdit, go to HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight (i.e. the Rio branch)

Export to a file, f.x. MyColors.reg

In MyColors.reg, you’ll see

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\20.0\Editor\Highlight\Additional search match highlight]
... and so on

Now, Open MyColors.reg in Notepad, search for “20.0\Editor\Highlight\” and replace it with “21.0\Editor\Highlight\Custom themes\Lars”.

Add the two branch paths for good measure.

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars]
[HKEY_CURRENT_USER\Software\Embarcadero\BDS\21.0\Editor\Highlight\Custom themes\Lars\Additional search match highlight]
... and so on

Import the file into the registry, Restart the 10.4, go to Options | User Interface | Editor | Color and pick the custom theme “Lars”.

Embarcadero Launches LearnDelphi.org, a Delphi-Centric Learning Ecosystem, to Promote Delphi Education

LearnDelphi.org provides a platform for Delphi developers and educators, along with free licensing options and ways to get involved. Embarcadero (a division of Idera, Inc.), a provider of cross-platform app development productivity tools, today announced the launch of LearnDelphi.org, a learning ecosystem for Delphi developers, a teaching platform for Delphi educators, and a key destination for all things related to the Delphi Community.LearnDelphi.org is a collection of resources and free content for teaching and learning software development curated by and for Delphi developers. The site includes Delphi licensing options and resources such as ebooks, slideshows, and video tutorials, as well as ways to get involved. The content is compiled and supported by Embarcadero’s MVPs, community members, subject matter experts, and employees, and will be available in multiple languages over time. Potential contributors, educational liaisons, and educators are always welcome and encouraged to get involved.

Source: Embarcadero Launches LearnDelphi.org, a Delphi-Centric Learning Ecosystem, to Promote Delphi Education

RAD Studio 10.3 Rio – Release 3 is out

10.3.3 is the last expected release before 10.4, planned for some point in first half of 2020, and bring us

  • 64-bit Android
  • iOS 13
  • MacOS Catalina (Delphi)
  • Enterprise Connectors (Enterprise and Architect SKUs)
  • Numerous fixes to VCL, RTL and IDE
  • 180+ user reported issues

What’s new: 10.3 Rio – Release 3 – RAD Studio
ISO: http://cc.embarcadero.com/item/30896
Web Installer: http://cc.embarcadero.com/item/30893

Dalija Prasnikar have some useful observations on her blog.

Demystifying pointers in Delphi

Having a general understanding of how memory and addressing works, helps you understand pointers in Delphi (and C/C++). Learning assembler also gives you that knowledge, but there are simpler ways to think about it.

A pointer is a memory location that contains the address to the actual data you want.

Think of a street with houses. Make a list of the houses and their addresses.
This is a list of pointers. Each pointer in the list leads to the actual house it refers to.

Continue reading “Demystifying pointers in Delphi”

Generic Command Line Parser for Delphi 10.3.x

You know the feeling. You need a specific input from your command line, but you can’t find something lightweight that does the job. Hence, the FDC.CommandLine was born.

You can use – / or + as a switch, and use : or = as a flag for parameters.
/option:parameter

You can use single and double quotes to be able to parse parameters that contain spaces and quotes.
/quoteoftheday:”This is a ‘quoted’ parameter”

It also supports lists of parameters.
/inputfiles:(file1.txt , “file with space.txt”)

Take a look at the TestFDCCommandLine.dpr for more examples of how it works.

Continue reading “Generic Command Line Parser for Delphi 10.3.x”