Comparing INI files

 Delphi, Windows  Comments Off on Comparing INI files
Jun 282014
 

INI files are a simple but powerful means of storing a program’s configuration. They have existed since the 16 bit Windows times, where they were stored in the Windows directory, later on they were stored in the program’s executable directory and nowadays they usually go somewhere into a user’s home directory. Microsoft has tried to replace them with more structured storage, first the Registry and later (in dotNET) with XML files, but they are still popular because they are simple to copy and move around, easy to use and can be edited with a text editor. Programmers and system administrators love this kind of configuration.

They have one problem, though: They tend to get larger than expected and since they are only barely structured, it is rather tedious to compare them. Is there anybody who has not edited an INI file in notepad and changed the order of sections and entries so he can use a tool like Beyond Compare to find the actual differences between two INI files? I have done that a lot more often than I care to remember and it was always painful and slow.

Enter dzIniFileFormatter: This is a tool I wrote actually several years ago but only today remembered that I never blogged about it. It allows to sort sections and entries in two different ways:

  • Alphabetically
  • By Template

Using either it also tries to keep comments where they belong (I use // to mark comments, even though Microsoft proposed a semicolon, but unfortunately the TIniFile implementation in Delphi removes these latter comments while it leaves // comments intact.).

I hope the user interface is self explanatory:

dzIniFileFormatter

The most useful function is probably sorting by template. It allows you to resort e.g. a modified version of an INI file so it matches the original and can easily be compared using the above mentioned Beyond Compare or any other side by side file comparison utility.

The source code (Delphi 2010) is available from the dzIniFileFormatter page on OSDN (no longer on SourceForge) and I have just now uploaded a pre-compiled executable. I hope you’ll find this tool as useful as I did. If you have any suggestions, please contact me via google plus (see link in the upper right corner).

 Posted by on 2014-06-28 at 22:29

Setting the caret in a TMemo

 Delphi  Comments Off on Setting the caret in a TMemo
Jun 252014
 

Once in a while you want to set the caret position (aka cursor position) in a TMemo to a given line and character in that line. If you google for it you will find lots of hits that tell you to do the following:

With Memo1 do
    SelStart := Perform(EM_LINEINDEX, Line, 0);

or without the ugly with statement

Memo1.SelStart := Memo1.Perform(EM_LINEINDEX, Line, 0);

But that looks to be at least pre-Delphi 2007 because there already is a TMemo.CaretPos property that does the same:

Memo1.CaretPos := Point(0, Line);

This is a lot easier to understand than the above. Note that you can’t just assign only x or y like this:

Memo1.CaretPos.y := Line; // does not work!

This won’t work, because accessing CaretPos calls a getter method that returns a TPoint value. Changing this value does not change the CaretPos property, you must write the actual CaretPos property.

Unfortunately, while this works fine in general, Borland has introduced a bug in TCustomGrid.Paint that breaks this (both) code. If you have got a TCustomGrid descendant (TStringGrid and friends) on the form or a different form, you might find that the caret of the memo shows at the position where it would be on the grid. See QC 25702 for a description and workaround.
Warning:
The workaround requires you to modify the VCL unit Grids.pas. To do that you should copy it from the Delphi installation to your project’s source code, add it to your project and only then modify it. Otherwise you might end up with a Delphi update overwriting it or even worse, failing to install.

 Posted by on 2014-06-25 at 10:40

Weak references – or why you should enable ReportMemoryLeaksOnShutdown

 Delphi  Comments Off on Weak references – or why you should enable ReportMemoryLeaksOnShutdown
Jun 192014
 

I was enhancing my dzMdbViewer tool with a view that shows the table structure rather than the data of the table, when I out of curiosity added

  ReportMemoryLeaksOnShutdown := True;

to the source code. I found some memory leaks that were easy to fix (and which I would probably have fixed anyway later when reviewing the code).

I continued working on the new functionality and used some rather old code I wrote about 10 years ago (part of dzlib, but only used in a special tool) for it. It allows reading the table structure of an MS Access database and puts it into a structure of interfaces. Everything worked fine, until I closed the program and got gazillions of memory leak reports.

It turned out that back then I had created circular references between these interfaces: Tables were referencing Indexes and Columns, Indexes were referencing Tables and Columns, Columns were referencing Tables and Indexes. Of courses, this had to create memory leaks because the reference counter of a circular reference can never reach 0 so these objects were never freed.

There is a cure for this kind of problem: It’s called weak references. Since the Delphi compiler for desktop (Windows) does not support weak references (The mobile compiler supports it via a [weak] attribute, but that attribute is ignored by the desktop compiler.), it must be implemented in code. I am a lazy bastard, so I turned to Google for an answer. This is an ancient problem, so somebody was bound to have blogged about it. I found several hits on StackOverflow and also this Synopse blog post that covers the problem in depth.

So, the solution was this procedure:

procedure SetWeak(_InterfaceField: PIInterface; const _Value: IInterface);
begin
  PPointer(_InterfaceField)^ := Pointer(_Value);
end;

It is used like this:

type
  TChild = class(TInterfacedObject, IChild)
  private
    FParent: IParent; // This must be a weak reference!
  public
    constructor Create(_Parent: IParent);
    destructor Destroy; override;
  end;

constructor TChild.Create(_Parent: IParent);
begin
  inherited Create;
  SetWeak(@FParent, _Parent);
end;

destructor TChild.Destroy;
begin
  SetWeak(@FParent, Nil);
  inherited;
end;

Assuming that TParent (which implements IParent) maintains a strong reference to its TChild objects via IChild interfaces, but the TChild object also needs a reference to its parent (via an IParent interface), we get a circular reference. To solve this, the FParent reference is declared as a weak reference (which is only a comment) and assigned via the SetWeak procedure. Now, assigning FParent does not increment the reference counter of the parent, so when there are no other references to it, it will get destroyed. When the parent gets destroyed all references to its children also get removed, which in turn triggers the child to be destroyed. The child still maintains the reference to the parent, but it is a weak reference, so it must not decrement the reference counter when FParent gets NILed. So, again, the destructor of TChild must use SetWeak to assign NIL to it.

 Posted by on 2014-06-19 at 20:57

Delphi7Help4BDS revisited

 Delphi  Comments Off on Delphi7Help4BDS revisited
Jun 152014
 

Today I updated the Delphi Help Expert (formerly called Delphi 7 Help for BDS) I wrote back then when Delphi 2005 came out and Borland managed to turn the online help into a mess (again).

It now supports Delphi 2005 to XE6 and allows to reconfigure F1 + any of the modifier keys Shift, Ctrl, Alt and Alt+Ctrl to call either a help file (you will need the old winhelp viewer that Microsoft dropped with (Vista?)), chm file or internet url. It comes preconfigured with a few example internet urls for searching with Google, Bing, the Embarcadero docwiki or MSDN.

To install it, get the sources from OSDN, open the package for your Delphi version, compile and install it. You should then find a new entry in the IDE’s Help menu called “Configure Delphi Help Expert” which gets you the following dialog where you can configure the actions for each of the key combinations.

Delphi Help Expert

 Posted by on 2014-06-15 at 20:16

Adding remote repositories to your mercurial.ini

 Mercurial  Comments Off on Adding remote repositories to your mercurial.ini
Jun 142014
 

Among a lot of other things you can add names for remote repositories to your mercurial.ini so you can access them without having to type that long path. This can be quite convenient e.g.

[path]
dzlib=ssh://twm@hg.code.sf.net/p/dzlib/hgdzmaincode

allows me to clone a copy of my dzlib+tools main repository on sourceforge like this:

hg clone dzlib dzlib+tools

rather than having to type:

hg clone ssh://twm@hg.code.sf.net/p/dzlib/hgdzmaincode dzlib+tools

Unfortunately this also tends to add the possibility for undesired side effects. Consider this:

hg clone dzlib dzlib

What is it supposed to do? What I wanted it to do, is simple: Clone the remote repository dzlib (as configured in the [path] section) to the subdirectory dzlib.
What it actually tries to do is: Clone the remote repository dzlib to the remote repository dzlib, which is definitely not what I wanted it to do.

Since I rarely create new clones from the remote repository I have removed the entries in [path] again, because the time potentially spent on troubleshooting these side effects is much longer than having to look up the remote url the few times I actually want to clone a remote repository.

 Posted by on 2014-06-14 at 18:33

Avoiding long timeouts when connecting to Mercurial repositories on SourceForge

 Mercurial  Comments Off on Avoiding long timeouts when connecting to Mercurial repositories on SourceForge
Jun 142014
 

As described in a previous post I initially had some problems connecting to Mercurial repositories on SourceForge that went away without me changing anything. In that post I give the following entry for mercurial.ini:

[ui]
ssh="C:\Program Files (x86)\PuTTY\plink.exe" -ssh -agent -v -i "D:\path\to\my\private_key.ppk"

While this works well, if Pageant is already running and has loaded the key, it results in a non responsive console if either of these conditions is missing. This is quite annoying because I tend to forget to start Pageant and it takes me quite a while to realize what the problem is. A little bit of digging into the command line parameters of plink gave me the fix: Add the -batch switch, so it won’t accept any interactive prompts. So it should look like this:

[ui]
ssh="C:\Program Files (x86)\PuTTY\plink.exe" -ssh -batch -agent -v -i "D:\path\to\my\private_key.ppk"

If Pageant is not running or the private key not loaded, any connection attempt will within seconds result in the following error message:

hg incoming
abort: no suitable response from remote hg!
 Posted by on 2014-06-14 at 18:11

AutoComplete for TEdits

 Delphi  Comments Off on AutoComplete for TEdits
Jun 092014
 

I am sure you know about the useful controls TJvDirectoryEdit and TJvFilenameEdit from the JVCL. They come in handy whenever you need an edit field that should allow autocomplete for file or directory names. It always irked me that I had to include not only the JVCL but also the JCL in order to just have this autocompletion feature especially since I don’t want the selection buttons that are part of the controls. So, having some time on my hands this weekend I decided to check how it is done and whether it would be possible to get the feature without the JVCL. It turned out to be easy.

But first be warned: What I describe here is not what these controls do. I took the easiest route, losing some of the features along the way.

Windows offers basically out of the box what I want with the SHAutoComplete API function in the Shlwapi.dll. The function takes the handle of an edit control (it also supports the embedded edit control of a combobox) and adds autocompletion to it. Basically all you need to do is call

  SHAutoComplete(_ed.Handle, Options);

Where _ed is some TEdit control and Options is a DWORD containing several flags.

There are two ways of autocompletion:

  • Autosuggest
  • Autoappend

Autosuggest means that the control displays a list of matching items and the user can select one of these using either the mouse or the up/down arrow keys.

Autoappend means that the control automatically appends the first matching item to the user input and selects it, so that it gets overwritten if the user continues typing. The user can use TAB to switch focus to the next control in which case he will accept the suggestion.

Now, to suggest input, the function also needs to know where to get the entries. The function supplies three possible sources:

  • Filesystem
  • URL history
  • URL MRU (most recently used)

The first one is easy: The function tries to match the user’s input with files and directories in the file system, just as TJvFilenameEdit does.

I am not sure about the difference between URL history and URL MRU. I guess they are both about Internet Explorer URLs, but I haven’t tried them because I only wanted to get the lookup behaviour of the aforementioned controls.

I ended up with the following wrapper code for the function:

type
  TAutoCompleteSourceEnum = (acsFileSystem, acsUrlHistory, acsUrlMru);
  TAutoCompleteSourceEnumSet = set of TAutoCompleteSourceEnum;
type
  TAutoCompleteTypeEnum = (actSuggest, actAppend);
  TAutoCompleteTypeEnumSet = set of TAutoCompleteTypeEnum;
type
  TErrorHandlingEnum = (ehReturnFalse, ehRaiseException);
const
  // constants and descriptions from MSDN
  // http://msdn.microsoft.com/en-us/library/windows/desktop/bb759862(v=vs.85).aspx

  // Ignore the registry default and force the AutoAppend feature off.
  // This flag must be used in combination with one or more of the
  // SHACF_FILESYS* or SHACF_URL* flags.
  SHACF_AUTOAPPEND_FORCE_OFF = $80000000;

  // Ignore the registry value and force the AutoAppend feature on. The completed string will be
  // displayed in the edit box with the added characters highlighted.
  // This flag must be used in combination with one or more of the
  // SHACF_FILESYS* or SHACF_URL* flags.
  SHACF_AUTOAPPEND_FORCE_ON = $40000000;

  // Ignore the registry default and force the AutoSuggest feature off.
  // This flag must be used in combination with one or more of the
  // SHACF_FILESYS* or SHACF_URL* flags.
  SHACF_AUTOSUGGEST_FORCE_OFF = $20000000;

  // Ignore the registry value and force the AutoSuggest feature on.
  // A selection of possible completed strings will be displayed as a
  // drop-down list, below the edit box. This flag must be used in
  // combination with one or more of the
  // SHACF_FILESYS* or SHACF_URL* flags.
  SHACF_AUTOSUGGEST_FORCE_ON = $10000000;

  // The default setting, equivalent to
  // SHACF_FILESYSTEM | SHACF_URLALL.
  // SHACF_DEFAULT cannot be combined with any other flags.
  SHACF_DEFAULT = $00000000;

  // Include the file system only.
  SHACF_FILESYS_ONLY = $00000010;

  // Include the file system and directories, UNC servers, and UNC server shares.
  SHACF_FILESYS_DIRS = $00000020;

  // Include the file system and the rest of the Shell (Desktop, Computer, and Control Panel, for example).
  SHACF_FILESYSTEM = $00000001;

  // Include the URLs in the user's History list.
  SHACF_URLHISTORY = $00000002;

  // Include the URLs in the user's Recently Used list.
  SHACF_URLMRU = $00000004;

  // Include the URLs in the users History and Recently Used lists. Equivalent to
  // SHACF_URLHISTORY | SHACF_URLMRU.
  SHACF_URLALL = SHACF_URLHISTORY or SHACF_URLMRU;

  // Allow the user to select from the autosuggest list by pressing the TAB key.
  // If this flag is not set, pressing the TAB key will shift focus to the next
  // control and close the autosuggest list.
  // If SHACF_USETAB is set, pressing the TAB key will select the first item
  // in the list. Pressing TAB again will select the next item in the list,
  // and so on. When the user reaches the end of the list, the next TAB key
  // press will cycle the focus back to the edit control.
  // This flag must be used in combination with one or more of the
  // SHACF_FILESYS* or SHACF_URL*
  // flags
  SHACF_USETAB = $00000008;

  SHACF_VIRTUAL_NAMESPACE = $00000040;

function SHAutoComplete(hwndEdit: HWnd; dwFlags: DWORD): HResult; stdcall; external 'Shlwapi.dll';

function TEdit_SetAutocomplete(_ed: TCustomEdit; _Source: TAutoCompleteSourceEnumSet = [acsFileSystem];
  _Type: TAutoCompleteTypeEnumSet = []; _ErrorHandling: TErrorHandlingEnum = ehReturnFalse): boolean;
var
  Options: DWORD;
  Res: HRESULT;
begin
  Options := 0;
  if acsFileSystem in _Source then
    Options := Options or SHACF_FILESYSTEM;
  if acsUrlHistory in _Source then
    Options := Options or SHACF_URLHISTORY;
  if acsUrlMru in _Source then
    Options := Options or SHACF_URLMRU;
  if actSuggest in _Type then
    Options := Options or SHACF_AUTOSUGGEST_FORCE_ON;
  if actAppend in _Type then
    Options := Options or SHACF_AUTOAPPEND_FORCE_ON;

  Res := SHAutoComplete(_ed.Handle, Options);
  Result := (Res = S_OK);
  if not Result then
    raise EOleException.Create(_('Call to SHAutoComplete failed.'), Res, 'Shlwapi.dll', '', 0);
end;

And I call it like this:

  TEdit_SetAutocomplete(ed_Directory, [acsFileSystem], [actSuggest, actAppend]);

As you can see in the descriptions for the possible Option values, there apparently is a registry entry for setting the default for autocomplete, but I wasn’t able to find out where. Also, for some of the options it’s not obvious, what they do. I wonder what SHACF_VIRTUAL_NAMESPACE is supposed to mean?

Another thing to keep in mind, is that the function does not offer any filtering options, especially it does not distinguish between files and directories, so it is not possible to get the equivalent of TJvDirectoryEdit using this function. In order to get the full functionality of these two JVCL controls, you would have to do what the JVCL developers did (actually that functionality is much older than the JVCL, it originally came from rxlib), and use the IAutoComplete interface described in this answer on StackOverflow. But as I said above: I took the easiest route.

 Posted by on 2014-06-09 at 15:26

TStringList.CaseSensitive

 Delphi  Comments Off on TStringList.CaseSensitive
Jun 042014
 

Did you know that TStringList has got a property called CaseSensitive? It’s no surprise that it is used when comparing the strings while sorting.

But did you know that it defaults to FALSE?

WTF?

(This is Delphi 2007.)

 Posted by on 2014-06-04 at 17:15

When TAB jumps to the next row

 Delphi  Comments Off on When TAB jumps to the next row
Jun 032014
 

I just stumbled over an oddity in TJvDbGrid (which probably also applies to the standard Delphi TDbGrid):

Including dgTab in the grid’s Options resulted in TAB moving the cursor to the next row rather than to the next column as expected.

It turns out, that this is due to the (protected) property TabStops which for each column determines whether it should be included in the tab order. This property is handled internally in SetColumnAtrributs which in turn is called whenever the Options changes. It sets TabStops to true, for a column, if

  • dgTab is in the grid’s Options
  • Readonly of the grid is false

and a few other conditions of the column itself, e.g. whether it is a calculated column.

This method is not called when you change the ReadOnly property. So, if you change the grid from ReadOnly to ReadWrite like this:

    TheGrid.ReadOnly := False;
    TheGrid.Options := TheGrid.Options + [dgEditing];

everything works as expected. But if you change the order of these two lines like this:

    TheGrid.Options := TheGrid.Options + [dgEditing];
    TheGrid.ReadOnly := False;

you end up with a grid where TAB moves the cursor to the next row.

I found this in Delphi 2007, haven’t checked if it also happens in later versions.

 Posted by on 2014-06-03 at 16:03

Delphi IDE Explorer Expert for Delphi XEx

 Delphi  Comments Off on Delphi IDE Explorer Expert for Delphi XEx
Jun 012014
 

Based on a similar expert by David Hoyle for Delphi 3/4/5, which I found on Embarcadero CodeCentral, I have written the Delphi IDE Explorer Expert for Delphi XE .. XE6. It is a package based expert that installs into the Delphi IDE and displays its internal component structure.

The original purpose was to find the button for showing and hiding the background for the Firemonkey mobile form designer and turn the bloody thing off for good. Unfortunately I could not find that form and that button, so I assume it’s not written with the VCL but itself a Firemonkey form.

Here are some screenshots of the expert:

Display Properties

Display Events

Display Inheritance

The tree view on the left side displays the hierarchy of the components as it is available via the global VCL Screen object. the right side shows the properties, events and inheritance of the selected component.

Filter Dialog

The expert also allows to filter for specific class types, e.g all TAction, TActionList etc. objects.

The full source code is available from SourceForge.

Please note: The package for Delphi XE3 is actually a Delphi XE2 package for now. I cannot start XE3 because for some reason it thinks it isn’t registered. Did I mention I hate the forced online activation?

Edit: Apparently I am not only the second but at least the third who wrote such an expert. Rudy Velthuis also did it.

 Posted by on 2014-06-01 at 16:21