I have been using two tools to add additional data to my executable files in post build scripts for years:

Both tools open the freshly built executable and append chunks of data to it. And both started to fail with the error “[project].exe is open in another program” more often lately.

I never managed to find out exactly what causes the problem. First I suspected BitDefender antivirus, but the error still occurs after I have switched to Windows Defender, so it’s not just BitDefender but probably any antivirus software that is causing the trouble.

As a first try I added a 2 seconds delay between the compiling/linking and the execution of each of these tools. It seemed to help, the errors became much less frequently but they still occurred. Also those 4 seconds always felt like eternity (the whole compile usually takes less than 10 seconds, so 4 additional seconds is a whopping 40%).

So, a few days ago I had enough. I’ve got the source code of these tools, so why not simply add several tries to opening the file? That’s what I did: Both tools now try to open the file and if that fails wait for a second and try again. Rinse and repeat up to 10 times. In my tests, it rarely took more than 2 tries, usually the first one succeeded. That means it shaved these 4 seconds off the build time again with the rare exception of adding them again at very few occasions.

The changes to assemble are in the GnuGettext svn repository.

And here is a patch to jcldebug.pas which I used for MakeJclDbg. (If somebody wants to submit this as a pull request on github, go ahead.)

Both executables are available from my dzlib+buildtools project on OSDN.

If you would like to comment on this article, go to this post in the international Delphi Praxis forum.

By default, the width of the drop down list of a TComboBox is the same as the width of the control itself, and even in the latest Delphi version there apparently is no property to set it.

Why is that so? Good question. There are probably many third party controls that offer this because it is rather simple to implement. But on the other hand, if it is that simple, why isn’t it a feature of the default control? It can really be a pain in the lower back that some entries are just not displayed correctly as seen in the picture above.

Setting the drop down width is as simple as sending the CB_SETDROPPEDWIDTH message to the control’s handle:

SendMessage(TheCombobox.Handle, CB_SETDROPPEDWIDTH, MinimumWidthInPixels, 0);


It does not allow to shrink the width of the drop down list though, because it sets the minimum width, not the actual width. There is this answer on StackOverflow for that particular problem. The result isn’t very visually appealing though, because the list is left aligned rather than right.

Of course that can easily be fixed by adding the width difference to all coordinates passed to MoveWindow.

But the normal use case is probably that the list is not wide enough, not that it’s too wide.

While they help, these solutions still leave something to wish for: Usually you don’t want to set a fixed width in pixels, you want to make the drop down list wide enough so it fits the entries. So ideally you will take the list of entries, calculate their text width, optionally add the width of the scroll bar and set that as the width for the drop down list.

Of course, I am not the first one who want to do that. I found an ancient article by Zarko Gajic on ThoughtCo (formerly known as about.com) that deals with this issue. The result looks like this:

The article even goes a lot further. When do you call that code? Of course only after you added the items to the list. So, why not put it into the OnDropdown event of the ComboBox? (Because that clutters the sources and you also have to create an event handler for each of these ComboBoxes on your form, that’s why.). It also handles the case where the ComboBox is placed near the right border of the form and claims that it gets truncated there. I could not reproduce that problem. This looks fine to me:

So, why am I blogging about this? As said above, there are plenty of other articles on the web that handle these issues.

Last week I investigated several cases where the SendMessage code did not work at all. It turned out that somewhere between the SendMessage call in the form’s constructor and the time the user clicked the drop down button the window handle of the ComboBox got recreated, losing the changed drop down width. A solution for this would have been to put the code into the OnDropDown event. That would have required 4 event handlers, one for each of the ComboBoxes on that particular form, (or a shared event handler for all 4). I don’t like that, I prefer a solution that once and for all solves that problem by calling some function in the constructor and then forget about. So I went and wrote one:

// taken from:
// https://www.thoughtco.com/sizing-the-combobox-drop-down-width-1058301
// by Zarko Gajic

procedure TComboBox_AutoWidth(_cmb: TCustomComboBox);
const
var
itemsFullWidth: Integer;
Idx: Integer;
itemWidth: Integer;
begin
itemsFullWidth := 0;
// get the max needed with of the items in dropdown state
for Idx := 0 to -1 + _cmb.Items.Count do begin
itemWidth := _cmb.Canvas.TextWidth(_cmb.Items[Idx]);
Inc(itemWidth, 2 * HORIZONTAL_PADDING);
if (itemWidth > itemsFullWidth) then
itemsFullWidth := itemWidth;
end;
// set the width of drop down if needed
if (itemsFullWidth > _cmb.Width) then begin
//check if there would be a scroll bar
if TComboBoxHack(_cmb).DropDownCount < _cmb.Items.Count then
itemsFullWidth := itemsFullWidth + GetSystemMetrics(SM_CXVSCROLL);
SendMessage(_cmb.Handle, CB_SETDROPPEDWIDTH, itemsFullWidth, 0);
end;
end;

type
TComboAutoWidthActivator = class(TWindowProcHook)
protected
procedure NewWindowProc(var _Msg: TMessage); override;
public
constructor Create(_cmb: TCustomComboBox);
end;

{ TComboAutoWidthActivator }

constructor TComboAutoWidthActivator.Create(_cmb: TCustomComboBox);
begin
inherited Create(_cmb);
end;

procedure TComboAutoWidthActivator.NewWindowProc(var _Msg: TMessage);
begin
if _Msg.Msg = CBN_DROPDOWN then
TComboBox_AutoWidth(TCustomComboBox(FCtrl));
inherited;
end;

function TComboBox_ActivateAutoWidth(_cmb: TCustomComboBox): TObject;
begin
Result := TComboAutoWidthActivator.Create(_cmb);
end;


The TCombobox_AutoWidth procedure is taken from Zarko Gajic’s article, but adapted to take a TCustomComboBox parameter rather than TComboBox (And while I am writing this I have got a few ideas what to improve on it.) The TComboAutoWidthActivator class is based on the TWindowProcHook class I used earlier. It simply hooks the control’s WindowProc, checks for the CBN_DROPDOWN message (which will later cause the OnDropDown event to be called) and calls TCombobox_AutoWidth.

So, my form’s contructor now looks like this:

constructor TMyForm.Create(_Owner: TComponent);
begin
inherited;

TCombobox_ActiveAutoWidth(cmb_One);
TCombobox_ActiveAutoWidth(cmb_Two);
TCombobox_ActiveAutoWidth(cmb_Three);
TCombobox_ActiveAutoWidth(cmb_Four);
end;


Much cleaner than the solution with all those OnDropDown handlers.

The code above is part of u_dzVclUtils of my dzlib.

One possible improvement would be to have yet another function to enumerate over all controls on a form, find all ComboBoxes and call for them. I’m Not sure it’s worth the trouble, but it wouldn’t be rocket science either.

If you would like to comment on this article, go to this post in the international Delphi Praxis forum.

My recently released Delphi Tools Manager tool according to Virus Total and there the Microsoft virus scanner contains the PUA:Win32/Puwaders.B!ml virus.

The description on Microsoft’s site is as always pretty useless:

Summary

This application was stopped from running on your network because it has a poor reputation. This application can also affect the quality of your computing experience.

So, is this a virus or is it not? My interpretation would be that it isn’t actually a virus but just some glitch with the heuristics employed by that scanner.

No other virus scanner detected anything which is another hint that this isn’t actually a problem at all. Oddly enough, the Windows Defender on my computer hasn’t flagged the file either.

So far I don’t know what to make of it. Should I be worried that my system is infected and therefore the executables I create on it also could be infected? Probably not, since it is unlikely that only one of the engines used by Virus Total detects it and even the Microsoft scanner only detects it in the ZIP file, not in the executable itself.

I mentioned that I am working on the Delphi Tools Manager in my post about GExperts supporting import and export for the Tools menu.

Today I finished the first version of this tool. It supports all Delphi versions from 5 to 10.3 (it is written in Delphi 10.2).

So, what does it do:

1. It shows a list of all entries in the Delphi IDE’s tool menu in a similar manner as the Tools Options dialog.
2. It allows the same actions as that dialog: Add/Delete/Edit an entry and change the order of the entries.
3. It shows the details of each entry when it is selected.
4. It allows exporting and importing entries, using the same DTME file format as GExperts
5. Dragging one or more DTME files on the form will add them to the list

If it finds more than one Delphi version installed on the computer, it asks which one you want to modify.

While I have tested this program and found no bugs, I might have overlooked something. You use it on your own risk!

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

Apparently Embarcadero has decided to piss off the remaining Delphi users in the whole world, just to line their pockets. One of the annoyances of Delphi after Delphi 7 has been the enforced online activation. First, there was a 4 weeks grace period until that activation was necessary, but even that grace period has been gone lately. Also, there is a (not documented) limited number of allowed activations. Once you hit that limit, you had to contact Embarcadero to get a “bump”. Apparently that’s a manual process.

Now they have gone a step further:

You won’t get that “bump” unless you have got an active update subscription (which costs several hundred Euros per year).

Let me repeat that: You can’t install your legally paid for perpetual license of Delphi any more, unless pay a yearly fee of several hundred Euros.

Embarcadero’s GM (General Manager?) Atanas Popov wrote this on the topic:

Registration Limits

We have noticed compliance issues and increased Support efforts related to registration limit increases for customers on older product versions, who are no longer on Update Subscription. It is a standard industry practice to provide support to the most recent versions and to customers who have extended maintenance. We updated our processes and now route all issues raised from users who are not on Update Subscription to Sales and Renewals. We realize this is a change to previous operations and to reduce the impact to development projects, we issued a one-time registration limit increase for all customers who are close to hitting their registration count limit. This should address issues with re-installs of your licensed software on existing or new machines. Further, we will continue to look for options to make this more seamless through automation.
— Atanas Popov in his blog post From the GM: New Updates and Changes to the Registration “Bumps” Policy

I hope the part where he says “Further, we will continue to look for options to make this more seamless through automation.” he is not just trying to stall until the shit storm goes by, because, it won’t.

Just to make this clear:

It is a standard industry practice to provide support to the most recent versions and to customers who have extended maintenance.

It might be a standard industry practice, but in Germany, and I think in most other countries, this is illegal. You sold a perpetual license. So if you restrict your customers from using it, they are not getting the contractually guaranteed service. And you will probably find that some of them do employ lawyers.

At work, we have got 3 Delphi licenses: Two for Delphi 2007 and XE2 and another one (mine) on subscription. I have never used any of the support requests, I keep that subscription only be up to date on the features of the latest releases (and incidentally work on GExperts and other open source projects; with the consent of my employer of course). The other licenses used by two of my coworkers stay at the versions they are now because so far, I found nothing in later versions that would have made me want to update.

We currently are purely a Delphi shop, that is, all software development is done in Delphi. Most of these programs are for internal use only, with some very few exceptions. We do not sell software, we do road condition surveys all over Germany and Europe.

But if we cannot rely on Embarcadero delivering what they contractually agreed to deliver, we will not upgrade and pay for subscription, but probably look elsewhere. Not just because of the cost but also because it has become more difficult to find Delphi developers (Not just good ones. It has become nearly impossible to find any software developer who is willing to use Delphi at all.). I guess by pissing off Delphi developers world wide, Embarcadero will not increase their numbers, so this situation will not improve.

Many tools optionally add an “Open with [Name of tool]” entry to the context menu of the Windows Explorer. Some others don’t, even though it would be useful. Here is how to do it yourself:

1. Open any text editor (Notepad will do)
2. Copy and paste the following text into it:
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\shell\Open with [DisplayName]\command]
@="[Path\\to\\executable] %1"

3. Replace [DisplayName] with the name of the tool.
4. Replace [Path\\to\\executable] with the full path of the executable to open. Not that you must replace all backslashes with a dual backslash for this to work.
5. Save the file as “Add-Open-with-MyTool-to-Context-Menu.reg”. Make sure that it is saved as a .reg file, not as a .txt file! (One of the annoyances of Notepad.)
6. Open the created file with a double click and let the Registry Editor add it to the Registry

## Examples:

### Open with (portable) Notepad++

Windows Registry Editor Version 5.00



### Open with (portable) HxD

Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\Open with HxD\command]
@="C:\\PortableApps\\HxD\\HxD64.exe %1"


HxD is a free and powerful hex editor and can be downloaded from https://mh-nexus.de/en/hxd/.

## Created entries

The created entries look like this:

In my last post I wrote about the export and import feature for custom Tools menu entries that GExperts adds to the Delphi IDE. I also mentioned that I was thinking about adding a custom clipboard format for copying and pasting these entries between multiple Delphi instances / versions.
OK, I did that. GExperts now also adds a popup menu to the Tool Properties dialog with two entries:

• Copy entry to clipboard
• Paste entry from clipboard

These entries use a custom clipboard format registered with Windows as "Delphi.ToolsEntry". It’s a binary format with the following structure:

• Size: UInt32 -> the size of the structure
• Title: array of WideChar, zero terminated
• Path: array of WideChar, zero terminated
• WorkingDir: array of WideChar, zero terminated
• Params: array of WideChar, zero terminated

With this popup menu it is now possible to copy and paste Tools menu entries between multiple instances of the Delphi IDE. This works also between different Delphi versions, so you can copy the entries from Delphi 10.3 to Delphi 6 and vice versa.

There is no new GExperts release for now (I am waiting for the next Delphi point release which should be just around the corner. I’m just guessing, I have no internal knowledge about the release schedule.). In the meantime, if you’d like to get this feature, you will have to compile your own GExperts DLL.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

The Delphi IDE has the quite useful option to add custom entries to the Tools menu. These entries call external programs with some “Macros” that reference the current IDE status, e.g. the currently active project or source file and some of their properties like the output directory or the current editor column and row.

GExperts already enhances the Tools Properties dialog by adding auto completion for the file name and the working directory (for Delphi 6 and 7 it also adds support for Drag and Drop, but that doesn’t work for later versions).

It has always irked me that there was no easy way to port these custom tools entries from one Delphi version or installation to another. I always had to copy and paste four fields to achieve that.

GExperts now adds two new buttons to export and import the current entry:

These buttons write / read Delphi Tool Menu Entry files (*.dtme), which are in INI file format an look like this:

[Delphi Tool Menu Entry]
Title=Explore &Source Directory
Path=explorer.exe
WorkingDir=
Params=/e,/select, \$EDNAME


So, to move an entry from one IDE version to another, export it from the first one, add a new entry to the second one and import the exported file.

I’m thinking about adding a special clipboard entry format for this so creating a file to copy these entries will no longer be necessary, but that’s for some future time (which may be very near but maybe not).
Edit: Done, see here.

I am also currently working on a small side project called Delphi Tools Manager which will supply that functionality outside the IDE, but it’s not quite ready for prime time yet. I actually already made a release available but had to withdraw it because I found that there are apparently two different ways the IDE stores these entries in the registry.

There is no new GExperts release for now (I am waiting for the next Delphi point release which should be just around the corner. I’m just guessing, I have no internal knowledge about the release schedule.). In the meantime, if you’d like to get this feature, you will have to compile your own GExperts DLL. But that’s far from being rocket science.

If you would like to comment on this, go to this post in the international Delphi Praxis forum.

A while ago I blogged about the various possibilities for auto completion in TEdits. I created several units that supply auto completion based on

• a StringList
• files
• directories
• path (basically also directories)

They all followed a basic principle: Implement IAutoComplete and IAutoComplete2 and provide different implementations for the IEnumStrings interface. They worked fine in Delphi 2007 but I never tested them in later versions.

Today I wanted to add auto completion for files to a tool written in Delphi 10.2 and got an Access Violation in my implementation of the IEnumStrings.Next method. I could not figure out the problem, so I wrote a test program using the simplest implementation I had, the one using a StringList, to be sure there wasn’t any other part of the tool that wreaked havoc with the auto completion. The bug was still there and I still could not figure out what the problem was:

function TEnumStringStringList.Next(celt: Integer; out elt;
pceltFetched: PLongint): HResult;
var
i: Integer;
wStr: WideString;
begin
i := 0;
while (i < celt) and (FCurrIndex < FStrings.Count) do begin
wStr := FStrings[FCurrIndex];
TPointerList(elt)[I] := Pointer(wStr); // <= AV here
Pointer(wStr) := nil;
Inc(i);
Inc(FCurrIndex);
end;
if pceltFetched <> nil then
pceltFetched^ := i;
if i = celt then
Result := S_OK
else
Result := S_FALSE;
end;


The Access violation happens in the line that assigns something to the first item in the elt TPointerList. As I said: It works fine in Delphi 2007.

Googling turned up a slightly different implementation for this:

    TPointerList(elt)[i] := CoTaskMemAlloc(2 * (Length(wStr) + 1));
StringToWideChar(wStr, TPointerList(elt)[i], 2 * (Length(wStr) + 1));


Which unfortunately still caused an access violation. Finally I happened about this answer from Remy Lebau on StackOverflow that contained a type defintion with a comment:

type
TPointerList = array[0..0] of Pointer; //avoid bug of Classes.pas declaration TPointerList = array of Pointer;


Guess what? It solved the problem. The code now works in both, Delphi 2007 and 10.2.

So, what causes the Access Violation? And why not in Delphi 2007?

The declaration of TPointerList in Delphi 2007 looks like this:

  TPointerList = array[0..MaxListSize - 1] of Pointer;


while the one in Delphi 10.2 is this:

  TPointerList = array of Pointer;


The first one is a static array the second one is a dynamic array. A Static array is a block of memory which is allocated automatically by the compiler for the given size, while a dynamic array is a pointer to an array descriptor (initially it’s NIL). Once the dynamic array gets initialized using the SetLength procedure the array descriptor contains the size of the array and enough memory to store the entries. The dynamic array variable points to the first entry.

So the difference between typecasting the elt parameter to a static array of pointers or an dynamic array of pointers causes the Access Violation. The change happened between Delphi XE and Delphi XE2. (I reported this as a bug in the Delphi RTL: RSP-24616. because apparently nobody had.).

elt is declared as an untyped out parameter. That means it is a pointer to some memory that the caller provides and which is to be written to by the Next method.

Typecasting it to a static array of pointers means that we assume that elt points to some memory which is to contain pointers to OleStrings. Typecasting it to a dynamic array of pointers means that we assume that elt points to a pointer to some memory that is to contain pointers, so the first (usually uninitialized) pointer in the array is referenced and since it points to somewhere which should not be written to, writing to it causes an access violation.

Adding the type definition given above fixed the problem.

The difference is the same as between a PInteger and a PPInteger Parameter:

type
PInteger = ^Integer;
PPInteger = ^PInteger;

procedure bla(param: PInteger);
begin
param^ := 5;
end;

proceduer blub(param: PPInteger);
begin
Param^^ := 5;
end;

var
InvValue: integer;
begin
bla(@IntValue);
blub(@IntValue);
end;


If no type checking took place passing a pointer to an Integer to blub would compile but cause an Access Violation because it tries to use the value of IntValue as a pointer. Since it usually isn’t, the memory that “pointer” references would not be accessible.

Later I found another post on StackOverflow that actually was about that very Access Violation I was trying to find with an answer from Rudy Velthuis.

Uwe Raabe, who took over maintaining MMX Code Explorer is collecting money to pay for a designer who will create new icons for MMX Code Explorer.

While I don’t think that nice looking icons should be high on the priority list, it’s Uwe’s decision, not mine. I sent some of the money I got donated to GExperts his way, just because I like the tool.