If your floating point calculations differ after a trivial change

 Delphi  Comments Off on If your floating point calculations differ after a trivial change
Sep 062018
 

Today I had a curious bug: After changing the way GPS coordinates were read from a file (not calculated!) all of a sudden lots of unrelated floating point calculations had different results.

I reverted the changes just to be sure and, yes, the results were back to the original values.

I added the new code again, same problem.

I changed the code to first use the new method for reading the file, followed by the old one, overwriting the data. The problem still occurred.

I reduced the new code to just constructing the object that does the reading. The problem still occurred.

I even removed the object construction. The problem still occurred.

Finally I had a closer look at the code in the unit that contains the object declaration. Nothing obvious, but after about an hour staring at not very complex code it turned out that it used another unit which automatically loads the proj4.dll which we use for converting geographic coordinates. It doesn’t actually call that dll, just loads it. And then it dawned to me: There is this thing called 8087 Control Word which controls how various operations are done, e.g. rounding. So if the dll changes that control word in its initialization, it will change the calculations done in other parts of the program.

To prove this, I added code that reads the 8087 Control Word before and after the dll was loaded. The values were different. So that probably was the culprit.

The SysUtils unit exports a SafeLoadLibrary function. In addition to calling the LoadLibrary Windows API function, it saves the error mode and the 8087 Control Word before loading the dll and restores these values afterwards. I could not use that function because the unit loads the dll from a resource rather than from a file, using my TdzResourceDllLoader (from dzlib). So I added code to safe and restore the control word to that class.

Guess what? The problem went away.

This is the code (copied from SysUtils.SafeLoadLibrary) I used:

var
  FPUControlWord: Word;
begin
  // [...]
  // save the FPU Control Word
  asm
    FNSTCW  FPUControlWord
  end;
  try
    // code to
    // load the dll into memory
    // relocate it
    // call dllmain
  finally
    // restore the FPU Control Word
    asm
      FNCLEX
      FLDCW FPUControlWord
    end;
  end;

Enable debug logging in gnugettext.pas

 Delphi  Comments Off on Enable debug logging in gnugettext.pas
Sep 032018
 

gnugettext.pas has got a conditional define called DXGETTEXTDEBUG. If it is defined, various debug messages are written to a MemoryStream. That won’t help much, if you can’t read that stream, so you need a way to write that stream to a file.

Guess what, that’s easily possible. Just call DefaultInstance.DebugLogToFile passing it a file name to which you want the log to be written. An additional parameter determines whether the file should be overwritten or been appended to.

In that call, the whole memory stream gets written to that file, so no log messages get lost. Also, any subsequent log messages will be written directly to that file instead of the memory stream.

GExperts Enhancements for docking in the Delphi IDE

 Delphi, GExperts  Comments Off on GExperts Enhancements for docking in the Delphi IDE
Sep 022018
 

The Delphi IDE has supported docking of various forms for a long time (I don’t remember if it ever did not). Unfortunately if not docked, the floating forms always seem to be in the way, and if you dock them, they take up screen space that you might rather use for the editor window. Given that most of us have got more than one monitor, wouldn’t it be nice to move some of these docking forms to the second monitor?

You can already do that by just undocking one form and docking other forms to it. The resulting floating window can be moved freely, even to another monitor. What you can’t do with it is minimize it or use WIN+Arrow Keys to snap them to the left or right monitor edge.

There are now two enhancements in GExperts that address this:

The first one is the new IDE enhancement “Enhance Dock Forms to allow minimize and Win+arrow positioning” which can be enabled on the IDE tab of the configuration dialog.

Once set, those floating forms get a minimize button (which of course works) and can be moved to the left or right half of the monitor with Win-Left or Win-Right arrow keys. The usual mouse gestures you can do with any window are also available.

Unfortunately this only works if more than one form have been docked together, so a single floating form will not show these enhancements.

The second enhancement is the new “Add dock window” expert. It creates a new top level window to which any docking form can be docked. It too allows all the usual Windows shortcuts for placing and moving forms. In addition it has got a button on the taskbar so you can minimize it and restore it easily. It can also be put behind the IDE main window or on a different monitor. The taskbar button makes it easy to bring it back.

You can even have multiple of these GExperts dock windows.

Unfortunately they have some drawbacks too:

  • You cannot close them as long as anything is docked to them. I tried to allow that but it crashed the IDE. To really get rid of the window, undock all docked forms from it and then close it.
  • Also, if you dock only a single form to it, you won’t get a caption for it to move it elsewhere. Tip: Disable docking using its context menu (which will make it float) and enable it again. Alternatively select one of your saved desktop layouts or restart the IDE.
  • There is no option to save the currently docked forms and their layout yet.

I’ll try to address these problems in a later version.

Both enhancements work for all supported Delphi IDEs, btw.

There is no release with these features yet. To get them, for now you have to compile your own GExperts dll which isn’t exactly rocket sciences anyway.

This idea is based on a question by Codehunter on the German Delphi board Delphi Praxis.

If your lines seem too thick in a TeeChart

 Delphi  Comments Off on If your lines seem too thick in a TeeChart
Aug 312018
 

… you might not be used to anti-aliasing. Recent (#1) versions of TeeChart switched to GDI+ as their default drawing engine which also switched on anti-aliasing by default. The difference looks like this:

(Apparently this wasn’t obvious: These screen shots have been magnified to show the cause of the “thick line” effect.)

It’s a matter of personal taste (and the use case) which one looks “better”. Some of my users started to complain about thick and blurry lines. So, how do you turn off this feature, once you know that it exists? There is nothing about anti-aliasing in the chart’s properties. Of course it’s somewhere in the complex setup dialog, but where? It’s here:

But it’s more complex than that: There is no property for this in the TChart control. Instead this property editor adds a new component of the type TTeeGDIPlus to the form. And there we can find that setting:

I guess it’s yet another case of RTFM, but you know how they say “If everything else fails, read the docs!”

As Daniela Osterhagen commented on my Google+ post, it can also be done in code only:

procedure TForm1.FormCreate(Sender: TObject);
var
  Renderer: TTeeGDIPlus;
begin
  Renderer := TTeeGDIPlus.Create(Self);
  Renderer.TeePanel := TheChart;
  Renderer.Antialias := False;
end;

(No need to keep that instance around, it will automatically be freed with the form.)

(#1: Where recent means something like 5 years ago. We just recently moved some of our projects to use the latest version)

Using escape characters in GExperts Grep replace

 Delphi, GExperts  Comments Off on Using escape characters in GExperts Grep replace
Aug 262018
 

If you enable regular expressions in the GExperts Grep search form, you can search for escape sequences like \t which is expanded to a tab character or \x20 which is expanded to char($20), the space character.

Up to now it was not possible to use \t (and similar escape sequences) in the Grep replace dialog. They were not expanded properly but the \ was treated as escaping the character following it, meaning \t was replaced by a simple t character.

I changed that, so now the same escape characters that are allowed in the search expression are also allowed in the replace expression. Note though, that \x{nnnn} for Unicode characters is not supported, maybe I’ll add that later.)

This solves Bug #82.

I also removed the restriction, that you could not search for space character(s). (Bug #81)

A word of warning:
Please keep in mind, that the Grep Replace functionality is still pretty much experimental. It replaces literal characters, so it is probably not a good idea to replace ‘\r\n’ with “\n” only, it will break the editor. Also, be very careful with replacing matches in all files. It might completely destroy your source code as it also replaces matches in the dpr file and dfm files (I had to revert the full project using SubVersion the first time I tried it. I lost all my not committed changes because of that. You learn from your mistakes, especially when they hurt you like this.)

There is no release with this functionality yet, but you can always compile your own dll.

svnrdump for dumping and loading remote svn repositories

 Delphi, TortoiseSVN  Comments Off on svnrdump for dumping and loading remote svn repositories
Aug 252018
 

Since I keep forgetting what the tool is called and how to use it:

svnrdump is a tool that can dump a remote svn repostory to a text file and also load that text file into a different remote svn repository. It comes with the TortoiseSVN installation (but is really part of the standard Subversion tool set).

My use case is migrating projects from SourceForge to OSDN. On the OSDN side the option “Allow to modify revision properties for project members” must be enabled which is available on the Subversion -> Admin page. It really takes a few minutes for that option to become active.

To dump a repository:

svnrdump dump https://source.site/some/repository/ > somerepository.dump

To load a repository:

svnrdump load https://dest.site/some/repository < somerepository.dump

Of course, since svnrdump writes to stdout and reads from stdin, it should be possible to do both in one go:

svnrdump dump https://source.site/some/repository/ | svnrdump load https://dest.site/some/repository

But I have never tried it. I prefer getting a dump file first, just in case anything goes wrong.

Today I moved bdsproj2cfg and dof2cfg.

GNUGetText support for the GExperts Message Dialog expert

 Delphi, GExperts  Comments Off on GNUGetText support for the GExperts Message Dialog expert
Aug 182018
 

Based on an idea and partial implementation submitted by Sven Harazim from Landrix Software I have just added support for GNUGetText to the GExperts Message Dialog expert. It now looks like this:

And generates Code like this:

MessageDlg(_('first line'+#13#10+'second line'), mtWarning, [mbOK], 0);

Or, if you’d rather use the Windows API:

which generates code like this:

MessageBox(0, PChar(_('first line'+#13#10+'second line')), PChar(_('The Caption')), MB_ICONWARNING or MB_OK);

There are even two new configuration options:

Depending on the settings the code could look like any of this:

MessageDlg(_('first line'+#13#10+'second line'), mtWarning, [mbOK], 0);
MessageDlg(GetText('first line'+#13#10+'second line'), mtWarning, [mbOK], 0);
MessageDlg(_('first line')+#13#10+_('second line'), mtWarning, [mbOK], 0);
MessageDlg(GetText('first line')+#13#10+GetText('second line'), mtWarning, [mbOK], 0);

or this:

MessageBox(0, PChar(GetText('first line')+#13#10+GetText('second line')), PChar(GetText('The Caption')), MB_ICONWARNING or MB_OK);
MessageBox(0, PChar(_('first line')+#13#10+_('second line')), PChar(_('The Caption')), MB_ICONWARNING or MB_OK);
MessageBox(0, PChar(_('first line'+#13#10+'second line')), PChar(_('The Caption')), MB_ICONWARNING or MB_OK);
MessageBox(0, PChar(GetText('first line'+#13#10+'second line')), PChar(GetText('The Caption')), MB_ICONWARNING or MB_OK);

It also generates code for C++, but I have no idea whether it works:

MessageDlg(GetText("first line\nsecond line"), mtWarning, TMsgDlgButtons() << mbOK, 0);
MessageBox(0, GetText("first line\nsecond line"), GetText("The Caption"), MB_ICONWARNING|MB_OK);

I hope it will be useful for some. I myself don't use that expert at all.

There is no release with this feature yet. If you want to use it, you will have to compile your own DLL.

Building less annoying user interfaces (part 1)

 Delphi  Comments Off on Building less annoying user interfaces (part 1)
Aug 182018
 

I am not claiming to be an expert in building good user interfaces, in fact I have been known to use terrible color combinations, overload forms with too many controls and confusing the user. But on the other hand I have been using computers for all of my adult life and I have seen quite a lot of really bad user interfaces, so I think I know a bit about what not to do.

Being a Delphi programmer by profession, even by passion, most of this applies to Delphi, but probably a lot of it applies to other tools. So, here we go:

Do not trap your users

One of the worst feelings for a computer user is that he has done something and cannot get back. Some programmers think it is a good idea to check the user input and force him to enter valid data. E.g. there is an entry field that only allows numerical input and if the user dares to enter something the program does not like, that field will turn red and will not let go of the input focus until the user has done what is required of him. Often this even means that he cannot press the Cancel button or close the window. Instead an error dialog is shown with a more or less generic message. Combine that with some annoying Pling sound and you will certainly entertain the whole office.

So, what do I suggest?

  • Input validation, even coloring the background is a good thing, but use a color that won’t reduce readability, like a very light red. I for one prefer yellow.
  • Do not force the input focus anywhere. If the user decides he wants to fill in other fields first, before going back to a field that was marked as invalid, that’s fine. He might have a reason for it. E.g. he might have just copied some text from another application to the clipboard in order to paste it into that input field.
  • Of course, any button for submitting the input should be disabled until the input passes validation.
  • Do not play error sounds. In today’s open plan offices but even in a smaller, shared office, this will absolutely annoy not just the user but also his colleagues.
  • Do not pop up error dialogs. They unnecessarily disrupt the user’s focus on your dialog. Most people nowadays are keyboard averse and will in that case use the mouse to close that dialog which disrupts their work even further. Also, as we all know, users don’t read error messages, they just want that annoying dialog go away.
  • If input validation fails, the user needs to know, where and why. Do not just disable the OK button, but give visual feedback what is wrong. Personally I prefer invalid input to get yellow background (As I said above: My color perception may be warped.). Also, there should be a text telling the user what kind of input is expected. That can be a text in the status bar or maybe even better near the input field that contains the offending input.
  • Do not automatically clear input fields with wrong content (Yes, I have seen programs do that, but that bad habit is most common with online forms.). It’s always easier to correct the input than to type it again. Imagine some user pasting a 10 digit number from his spreadsheet into your program and getting the decimal separator wrong (Yes, my American readers: There is such a thing as decimal separator other than the point. Many European countries use the comma. And don’t even get me started about date formats!). He might then simply replace that one character with the correct one rather than typing the whole 11 characters again.
  • Do not prevent pasting from the clipboard. Some people think it is a security risk to allow users to paste data from the clipboard into their program. In particular some programs do not allow pasting passwords. I might be wrong here, but I don’t see how not allowing paste from the clipboard improves security. If the system is compromised, the user is f***ed anyway. Of course copying from password input fields to the clipboard is a different matter.
  • Another thing about password input fields is the display. In some environments, showing the password in clear text is definitely not a good idea as somebody might be “shoulder surfing”. On the other hand, the user needs some way to determine whether he might have mistyped his password and correct it. Passwords are commonly displayed as *** or similar characters. Personally I like an option to temporarily display the password in the clear, e.g. a button which shows it as long as it is being pressed. There is a slight security risk (apart from “shoulder surfing”) here: If the user leaves the computer with this input mask open, somebody else can also press that button and read the password.
  • Be consistent! If you in one part of your program use yellow as the error color and display the input hint in the status bar, you should do that everywhere. If you develop internally used software for a company, somebody should decide how to do error handling and this should be enforced.

The above would not be complete without a link to my own input validation unit in dzlib.

That’s the end of part 1. I am sure I will continue that series at some time. Be prepared to hear me rant about tab order and resizable forms next.

Delphi disabled packages in the registry

 Delphi  Comments Off on Delphi disabled packages in the registry
Aug 142018
 

Note to self:

The Delphi IDE stores a list of disabled packages (that is packages where the user removed the check mark in front of the package under Components -> Packages) in the registry under HKCU\<basekey>disabled packages.

<basekey> is the base key of the Delphi configuration, e.g.

Software\Embarcadero\BDS\9.0

So the disabled packages for Delphi XE2 are stored in

HKCU\Software\Embarcadero\BDS\9.0\disabled packages

If you remove the package from the list of known packages (stored in the registry under Known Packages) and later on add them again, these packages might still be disabled and sometimes setting the check mark in the dialog does not enable them. To fix that, just delete these packages from the disabled packages list using the Registry Editor.

dzMdbViewer moved to OSDN

 Delphi  Comments Off on dzMdbViewer moved to OSDN
Aug 122018
 

Even though it has become better again, the stability of svn hosting on SourceForge has not been up to par for several months. I got tired of this, so, I moved one more of my projects from SourceForge to OSDN.

dzMdbViewer is a viewer for Microsoft Access database files which I wrote when I was stuck without an MS Access installation but had to look into an MDB.

It can open these files and display a list of queries and tables stored in them as well as the data they contain.

Table Data

Also, for tables, it can show the field definitions.

Table Definition