Mar 192017

A while ago I stopped using the formatter branch and officially took over the trunk of the GExperts repository. When I announced that on G+, Stefan Glienke asked me about my plans for GExperts.

My answer today is still the same as back then:

I will release new GExperts versions when I feel like it. And I will continue to call them experimental. There will be no rigorous testing, since nobody has volunteered to take on that responsibility. I will be using the latest development version in my daily work, so for now you can assume that at least the Delphi 2007 and XE2 versions are reasonably stable. This will change once I move on to newer versions, but unfortunately I don’t see that in the near future.

So, if you don’t like this, I would welcome volunteers to step forward and assume the responsibility of testing GExperts preferably with the Delphi versions they use every day. Your reward will be a place in the contributor list and possibly the pain of working with a not quite stable IDE.

Mar 192017

Some people have started pestering me about making a new release (you know how you are!). Don’t think that this has done anything to actually make me do it, I simply thought it to be the right time with Delphi 10.2 Tokyo apparently right around the corner.

I have created installers for all supported Delphi versions.

I don’t remember all the new features and bug fixes that went into this release, but I am pretty sure it is more stable and has more features than the previous one.

But anyway:

Please be aware that I mostly work with Delphi 2007, so this version can be regarded as tested quite well, followed by Delphi XE2. The others are only known to compile and new features are usually tested superficially with all versions. This is particularly true for Delphi 6/7 and 2005/2006.

Head over to the Experimental GExperts page to download the latest release it.

Mar 122017

For my dzComputerInfo tool I created a window without a title that can still be moved with the mouse. This is quite easy to do:

  1. To remove the title, set BorderStyle to bsNone.
  2. To let the user move it with the mouse, add the following message handler:
  TMyForm = class(TForm)
    procedure WMNCHitTest(var Msg: TWMNcHitTest); message WM_NCHITTEST;

procedure TMyForm .WMNCHitTest(var Msg: TWMNcHitTest);
  if (Msg.Result = htClient) then
    Msg.Result := htCaption;

It tells Windows, that the user clicked on the title rather than the client area. Windows then does the rest, and the user can move the window with the mouse as if he clicked on the window title.

If you also want the window to have a context menu, you’ll have to change the message handler, so it does not affect right mouse clicks:

procedure TMyForm .WMNCHitTest(var Msg: TWMNcHitTest);
  Res: SmallInt;
  Res := GetKeyState(VK_RBUTTON);
  if Res >= 0 then
    // only if the right mouse button was not pressed
    // (otherwise the popup menu wont show)
    if (Msg.Result = htClient) then
      Msg.Result := htCaption;
Mar 122017

USB serial converters from FTDI are quite popular. We also use them at work quite a lot because they do not have the problem of the competing products (like Prolific): Windows does not detect devices on them as Microsoft ball point devices.

These converters can be configured interactively using a dialog accessible from the hardware manager’s device property dialog, page “Port Settings” by pressing the “Advanced …” button.

There are various settings, the most common ones to change are

  • COM Port Number
  • BM Options: Latency Timer
  • Miscellaneous Options: Serial Enumerator

The first one is obvious: It sets the COM port number of the emulated serial port. Every converter ever connected to the computer will reserve one COM port, so if you attach many of them you will sooner or later get rather high port numbers which many tools cannot use. The workaround is to force the driver to use a particular COM port here.

The second one, Latency Timer is not that obvious: It sets the latency timer in milliseconds to be used when the data received is not large enough to fill the buffer. Reducing this value from the default 16 to e.g. 4 solves many problems where data is being received with a delay of several seconds (e.g. the GPS position displayed is lagging behind your vehicle position by several seconds, which results in several 10th of metres at higher velocities. I have seen 4 seconds which at 60 km/h equals about 80 metres.)

The last one, Serial Enumerator, solves the Microsoft Ball Point detection mentioned above. As long as it is checked and a device is attached that sends data, Windows might mistakenly think it’s a mouse and the mouse cursor will jump all around the screen and even randomly click everywhere. This is quite annoying when it happens (and it happens very often when you connect a GPS). To resolve the problem, uncheck this option.
(Btw: Microsoft Ball Point devices have not been in use for over a decade, but the bug is still present in Windows XP, 7 and 8/8.1. (don’t know about Windows 10) despite users having problems because of it for many years. Shame on you, Microsoft!)

Mar 122017

I managed to mess up my blog. The content is still there, but all the screenshots are missing since apparently they are not part of the export. Fortunately my hoster 1&1 makes a daily backup of my webspace which is stored for 6 days, so I could restore the pictures. (After praising them, let me add that I lost the data because their automatic conversion from managed to normal blog did not work, so I tried to revert, which deleted everything.)

Display and edit keyboard macros with GExperts

 Delphi, GExperts, Uncategorized  Comments Off on Display and edit keyboard macros with GExperts
Mar 112017

The GExperts Macro Library expert can now display and edit keyboard macros that have been recorded in the Delphi IDE.

It uses the information I described in my article on Interpreting Delphi IDE Keyboard Macros.

You get to this dialog via the context menu of a macro.

It allows you not only to display and edit text.

But also special keys.

And, on top of that, it works with Unicode, in Unicode aware IDEs (>= Delphi 2005).

(The above are Arabic characters, but I have no idea whether it means anything. I just typed a few keys.)

Just in case you are interested in these two keyboard macros:

ReadOnly Property
Read Write Property

Save these files anywhere and load them into the Macro Library expert.

They are supposed to generate a property for a class’ field: Copy the field definition from the private section to the public section. Position the cursor on the first character and start the macro.

There are at least 30000 Delphi 2007-2010 users

 Delphi  Comments Off on There are at least 30000 Delphi 2007-2010 users
Mar 072017

At least that’s a possible interpretation of the download statistics of my dzEditorLineEndsFix tool

As of today, it has been downloaded 33994 times since I wrote the first version in 2014.

Of course there will be duplicates and also those who have updated to later Delphi versions or stopped using Delphi all together after downloading the tool.

On the other hand there will be those who got the source code and compiled it themselves as suggested by Embarcadero, or those who use an alternative solution. So maybe 30000 isn’t such a bad guesstimate.

Interpreting Delphi IDE Keyboard Macros (Updated)

 Delphi, GExperts, Uncategorized  Comments Off on Interpreting Delphi IDE Keyboard Macros (Updated)
Mar 042017

Keyboard macros have been part of the Delphi IDE since basically forever (I remember using them in Delphi 5 but I wouldn’t rule it out that they already existed in Delphi 1 which I never used.)

GExperts also has had the Macro Library Expert since I know about it to overcome the Delphi IDE’s shortcoming of having only one keyboard macro.

This expert handles the macros as a black box, storing them in a binary stream which is used to get them from and pass them to the IDE and to save and load them from files.

So far I haven’t seen any documentation of the format these macros are stored in. That’s a real shame because it prevented the Macro Library Expert to become more useful, by allowing to edit existing macros.

I have had a look at data stored in its configuration files and tried to interpret them. Here is what I found:

A keyboard macro is a stream of bytes. It always seems to start with the four bytes 54 50 45 52 (ASCII “TPOR”, no idea what it means. TP looks suspiciously like Turbo Pascal). This seems to be a kind of signature. I have not tried to modify it to see what the IDE makes of it, I just want to understand the actual macros. This signature is followed by a single byte that is either 00 or 01. 00 seems to mark the end of the macro. 01 is followed by 4 bytes, which seem to belong together as two separate words, e.g. 61 00 00 00, and describe a key press. Each such 4 byte sequence is again followed by a single byte that is either 00 or 01. 00 again, marks the end of the macro, 01 means that another another key press follows etc.

54 50 54 52
01  61 00  00 00

would be a keyboard macro that simply types the lower case letter “a”.

54 50 54 52
01  61 00  00 00
01  62 00  00 00

would be “ab”.

Let’s have a closer look at the four bytes describing a key press:

The first word describes the main key, the second word describes modifier keys. The modifier word can have the following values:

  • 00 00 – meaning the main key is a normal character
  • 88 00 – meaning the main key is a special key like the arrow keys or insert / del / page up/down etc.
  • 10 00 added to it means that the Ctrl key is being pressed
  • 20 00 added to it means that the Shift key is being pressed
  • 40 00 added to it means that the Alt key is being pressed

I have not seen any other values.

Please keep in mind that I represent the bytes in the order they show up in the stream. When interpreting two consecutive bytes as words, you have to switch them around according to Intel convention, so the bytes 10 00 actually mean the the number $0010, and 88 00 is the number $0088.

If the modifier word is $0000, the main key is in its simplest form the ASCII code of a character, e.g. like you can see in GExperts ASCII Chart:

I have yet to check whether the value is actually a WideChar.
update: It definitely is a WideChar, so the code below won’t work correctly for input that uses the high byte of the character.

If the modifier value is <> $0000 it describes a special key, e.g.
$0020 is the Space key, $0021 is PgUp, $0022 is PgDn, $002D is Ins key, $002E is Del etc.

update: The above applies to all Delphi versions that I checked. That includes Delphi 6, 7, 2005, 2006, 2007 and 10.1. Note that Delphi 6 and 7 apparently do not support Unicode characters in keyboard macros. I tried to enter some Arabic characters (Unicode $30xx) and they were converted to characters in $00xx range.

Based on my observations I have so far written the following code to interpret a keyboard macro:

  TMenuKeyCap = (
    mkcBkSp, mkcTab, mkcEsc, mkcEnter, mkcSpace, mkcPgUp,
    mkcPgDn, mkcEnd, mkcHome, mkcLeft, mkcUp, mkcRight,
    mkcDown, mkcIns, mkcDel, mkcShift, mkcCtrl, mkcAlt);

var // these resource strings are declared in unit Consts
  MenuKeyCaps: array[TMenuKeyCap] of string = (
    SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp,
    SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight,
    SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt);

function KeyCodeToText(Code: Word; Modifier: Word): string;
  LoByte: Byte;
  Result := Format('unkown (%.4x)', [Code]);
  if (Modifier and $88) <> 0 then begin
    // special keys
    Result := '';
    if (Modifier and $10) <> 0 then
      Result := Result + MenuKeyCaps[mkcCtrl];
    if (Modifier and $40) <> 0 then
      Result := Result + MenuKeyCaps[mkcShift];
    if (Modifier and $20) <> 0 then
      Result := Result + MenuKeyCaps[mkcAlt];
    LoByte := (Code and $FF);
    case LoByte of
    // $00..$07
      $08, $09: // backspace / tab
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte - $08)];
      $0D: Result := Result + MenuKeyCaps[mkcEnter];
      $1B: Result := Result + MenuKeyCaps[mkcEsc];
    // $1B..$1F ?
      $20..$28: // space and various special characters
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + LoByte - $20)];
    // $29..$2C ?
      $2D..$2E: // Ins, Del
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + LoByte - $2D)];
    // $2F ?
      $30..$39: // 0..9
        Result := Result + Chr(LoByte - $30 + Ord('0'));
      $41..$5A: // A..Z
        Result := Result + Chr(LoByte - $41 + Ord('A'));
      $60..$69: Result := Result + Chr(LoByte - $60 + Ord('0'));
      $70..$87: Result := Result + 'F' + IntToStr(LoByte - $6F);
  end else begin
    LoByte := (Code and $FF);
    case LoByte of
      // $00..$07
      $08, $09: // backspace / tab
        Result := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte - $08)];
      $0D: Result := MenuKeyCaps[mkcEnter];
      $1B: Result := MenuKeyCaps[mkcEsc];
      // $1B..$1F ?
      $20..$7E: Result := Chr(LoByte);
      $7F: Result := MenuKeyCaps[mkcDel];
      $80..$FF: Result := Chr(LoByte);

function TMacroInfo.TryDecode(AStrings: TStrings): Boolean;

  function Read(var Buffer; Count: Longint): Boolean;
    Result := (FStream.Read(Buffer, Count) = Count);

  Magic: Longword;
  Flag: Byte;
  HiWord: Word;
  LoWord: Word;
  s: string;
  Result := False;
  FStream.Position := 0;
  if not Read(Magic, SizeOf(Magic)) or (Magic <> $524F5054) then
  if not Read(Flag, SizeOf(Flag)) then
  while Flag = $01 do begin
    if not Read(LoWord, SizeOf(LoWord)) then
    if not Read(HiWord, SizeOf(HiWord)) then
      s := KeyCodeToText(LoWord, HiWord);
    if not Read(Flag, SizeOf(Flag)) then
  Result := True;

It’s not finished and might never be. It’s also far from perfect. I am posting this here in the hope that it might be useful to somebody. I might use it to extend the Macro Library Expert in GExperts to allow limited editing of keyboard macros.

dzComputerInfo tool

 Delphi  Comments Off on dzComputerInfo tool
Mar 032017

A few days ago I was working in one of our measurement vehicles and found, that I couldn’t for the life of me tell, which of the 3 computers (soon to become 4) was selected on the KVM switch. This isn’t the first time this has happened to me and my colleagues have the same problem. We tried to solve this with custom background pictures and colours, but these have a tendency to get lost due to bugs in Windows, or operators who don’t like them. Also, they are only visible on the desktop background and any application windows will obscure them.

Up to Windows XP, pressing Ctrl+Alt+Del used to bring up a window displaying computer name and logged on user, but since Windows 7 that useful information has been replaced by a full screen menu without any information on it.

Enter dzComputerInfo. It’s a small tool that I wrote the evening after the above incident which does exactly one thing: It shows a window on top of all other windows displaying the computer name and currently logged on user. Since the window is so small and it places itself automatically just above the start button, it does not really become a nuisance.

The tool and the source code is available from sourceforge, if anybody else thinks he has a use for it.

(That start button is from Classic Shell btw.)

Fixed HideNavBar functionality in GExperts

 Delphi, GExperts, Uncategorized  Comments Off on Fixed HideNavBar functionality in GExperts
Feb 262017

When Embarcadero added the Navigation Toolbar to the Delphi code editor in Delphi 10 there were a few people who didn’t like it because it took up some more of the vertical screen space. But there was no option to disable it.

Achim Kalwa wrote an expert to hide this toolbar and contributed the code, which I integrated into GExperts. Unfortunately it didn’t work reliably. The Navigation Toolbar came back whenever one opened a new edit window and went away again when switching between tabs, creating an annoying flicker.

Today I investigated the issue and found that apparently the IDE sets the control’s visible property to true whenever it opens a new editor tab. The fix was actually quite simple: Create a new panel, set its Parent to the toolbar’s original parent control and set the toolbar’s parent to this new panel. So it ends up between the toolbar and its parent. Then set this panel to be invisible instead of the toolbar. Since the IDE doesn’t know about this new panel, it does not change its visibility. Voila, problem solved.

Of course it was a bit more involved because I had to size the panel correctly and also make sure that I don’t insert a new panel every time I check for the visibility. But once I had the general principle it was just a matter of fine tuning the solution.

  if TryFindComponentByName(Ctrl, 'TEditorNavigationToolbar', C) then begin
    Ctrl := TWinControl(C);
    ParentCtrl := Ctrl.Parent;
    if Assigned(ParentCtrl) and (ParentCtrl is TPanel) and (ParentCtrl.Name = GX_HideNavbarPanel) then
      pnl := TPanel(ParentCtrl)
    else begin
      pnl := TPanel.Create(ParentCtrl);
      pnl.Parent := ParentCtrl;
      pnl.Name := GX_HideNavbarPanel;
      pnl.Align := alTop;
      pnl.BevelOuter := bvNone;
      pnl.Height := ctrl.Height;
      Ctrl.Parent := pnl;
    pnl.Visible := FIsNavbarVisible;
    pnl.Enabled := FIsNavbarVisible;
    if FIsNavbarVisible then
      Ctrl.Visible := True;
    Result := True;

I also found, that in Delphi 10.1 Berlin there is already an option to show or hide the Navigation Toolbar (Tools -> Options -> Editor Options -> Display), so I removed the functionality from GExperts for Delphi 10.1 again.

%d bloggers like this: