Tag Archives: Pascal

Configurable Data Directories via Inno Setup

Recently, I’ve been relying on Inno Setup installation scripts to build setup executables for my Windows installs. I’ve given up on .NET deployment projects, and even InstallShield Express (why does my brain insist on reading ‘Express’ as ‘CrippleWare‘?) given their limitations.

The two big wins with Inno Setup for me are 1) it’s freeware, and 2) if I need to do something funky, I can roll my own behaviour into the script with its Pascal scripting option.

Today’s post is how I wrote an installer wizard that prompts the user for a “Data Directory” that receives data that I don’t want sitting in the “Program Files” area of Windows, and a configuration file so I can re-install to a different data-directory and allow the program toggle between them via config file modifications.

I do this because a very wise man once pointed out to me that:

Data that evolves at different rates should be designed to evolve separately.

Programming tends to evolve more slowly than input data, which can typically evolve more slowly than output data. This approach is a tip of the hat to his wisdom. Also, as I’ve learned several times:

The programs can come and go, but if you let it, the data will outlive them.

So, back to Inno Script and getting it to generate a config-file at install time that points at the Data-directory we will store our data to. I’ve settled on INI files, given their ease of understanding for my target users, and Inno Setup’s favouring of them.

First, I define a directory under the [Dirs] section. The name of the directory is delegated to being resolved via a call to some code I define later called GetDataDir(). I check that the directory doesn’t exist. I ask that it not be uninstalled with the software, and I grant the user permission to modify the directory contents:

[Dirs]
Name: {code:GetDataDir}; Check: not DataDirExists; Flags: uninsneveruninstall; Permissions: users-modify

Then in the [Files] section, I identify the files I want (here an entire directory I’ve pre-defined with the constant {#DataDir}. I ask that it confirms any over-write that it might have to do of pre-existing data, to recurse through any sub-directory and again, that the contents are to be left intact on an uninstall of the software:

[Files]
Source: "{#DataDir}\*"; DestDir: "{code:GetDataDir}";  Flags: confirmoverwrite recursesubdirs uninsneveruninstall

Next, I write some Pascal code to prompt the user for a data directory. If an INI file doesn’t exist, it will initially supply text based on the default location for the user’s application data. If the file does exist, the wizard will use the value it finds in the file instead.

A warning I should point out here is that turned out to be a bit tricky to get working as intended.

Because I want the wizard to prompt the user for a data directory directly after they’ve been prompted for the program directory, I’ve learned that program directory constant {app} isn’t available yet when this code runs. I need to reach under the hood, and pluck the value out of the running Delphi API code via a call to WizardDirDrive(), which returns whatever the user accepted as the program directory in the install wizard’s previous step.

[Code]
// global vars
var
  DataDirPage: TInputDirWizardPage;
  SampleDataPage: TInputOptionWizardPage;
  DataDirVal: String;

function GetDataDir(Param: String): String;
begin
  { Return the selected DataDir }
  Result := DataDirPage.Values[0];
end;

function GetDefaultDataDirectory() : String;
begin
  Result := ExpandConstant('{localappdata}\{#MyShortAppName}');
end;

function GetIniFilename() : String;
begin
    Result :=  WizardDirValue() + '\{#MyShortAppName}.ini';
end;

  // custom wizard page setup, for data dir.
procedure InitializeWizard;
var
  myLocalAppData: String;
begin
  DataDirPage := CreateInputDirPage(
    wpSelectDir,
    '{#MyLongAppName} Data Directory',
    '',
    'Please select a directory to install {#MyShortAppName} data to.',
    False,
    '{#MyShortAppName}'
  );
  DataDirPage.Add('');

  DataDirPage.Values[0] := GetIniString('{#MyShortAppName}', '{#INI_DataDirKey}', GetDefaultDataDirectory(), GetIniFilename());
end;

function DataDirExists(): Boolean;
begin
  { Find out if data dir already exists }
  Result := DirExists(GetDataDir(''));
end;

Finally, whatever the user chose via the scripted code will ned to be stored in this INI file. I add an entry to the [INI] section telling the script to write the DataDirectory value to the INI file:

[INI]
Filename: "{app}\{#MyShortAppName}.ini"; Section: "{#MyShortAppName}"; Key: "DataDirectory"; String: "{code:GetDataDir}"; Flags: createkeyifdoesntexist

And there we have it. Inno Setup. It’s powerful, it’s free, and it allows me to tack hard against default behaviour if I need something non-standard. If you’re programming for Windows, and need a a top-notch installer that’s free, take it for a spin.

Using Perl to Test External Process STDOUT Piping

Last week, I was introduced to FreePascal and it’s recently released IDE Lazarus. I was made aware of it because I was sniffing around for something that would create native binaries of GUI windows, and that would be very quick to draw up (ideally, as powerful as the Microsoft .NET GUI designer).

Before jumping into the issue that this blog post is about, I’m happy to report that the form designer in Lazarus is top-notch. I think I just found a new rapid-prototyping GUI tool that costs me no money, and minimal time.

Now… onto the pain and embarrassment. I convinced myself that I had an issue where FreePascal’s TProcess component (an adapter class through to external processes) was ignoring its poUsePipes property (the one that ensures piped output from the external process is made available to Pascal as soon as it is generated). The symptoms were no matter what I tried, my long-running external process output wasn’t hitting the GUI memo widget I’d set up until the end of the process.

I built a small isolation test to confirm what I was seeing, and submitted a bug report. It was quickly marked as resolved with a comment that left me feeling very derpy indeed.

Of course! Did I ensure the STDOUT buffer was being flushed after every write? No, I did not! My problem turned out to be the external process, not FreePascal. Now, because I never want to forget this again, I’m leaving myself a note on Perl and flushing STDOUT when I know my utility’s output will be piped to other processes.

Turns out in Perl there a special variable devoted to autoflushing STDOUT whenever something is written to the stream. The toy metronome perl script I wrote and submitted as part of my bug report is reproduced below, with the key line to flush the STDOUT stream appearing on line 7.

#!/usr/local/bin/perl

package ToyMetronome;

use strict;

$| = 1;  # Autoflush, so other processes pipe in the STDOUT stream unbuffered

sub processCommandLineArgs() {
  my (@arguments) = @_;

  my $returnArgs = {
    Ticks => 5
  };

  for (my $argIdx= 0; $argIdx < scalar @arguments; $argIdx++) {
    my $arg = lc($arguments[$argIdx]);

    if ($arg eq "--ticks") {
      if ($argIdx + 1 >= scalar @arguments) {
        print "Error: invalid number of ticks specified.\n";
        exit 1;
      } else {
        $returnArgs->{Ticks} = $arguments[$argIdx + 1];
        $argIdx++;

        if ($returnArgs->{Ticks} !~ /^*\d$/) {
          print "Error: invalid number of ticks specified.\n";
          exit 1;
        }
      }
    }
  }

  return $returnArgs;
}

sub ToyMetronome() {
  my $args = &processCommandLineArgs(@_);

  for(my $tick = 1; $tick <= $args->{Ticks}; $tick++) {
    sleep(1);
    printf "ToyMetronome: tick %d.\n", $tick;
  }
};

####### Application below ########

&ToyMetronome(@ARGV);

If I ever again become suspicious about external processes not piping their output to my utilities, I now have a Perl script where I can knock out whether it’s the external process without first embarrassing myself with spurious bug reports.

Deep-derp learning! It’s the new shiz!