Inno Setup: setting the source directory dynamically

10 or so years ago I worked on a CD-ROM product which continues to sell from time to time. At the time, we used a product called PC-Install to make the installer; since then we’ve moved on to Inno Setup.

Unfortunately, although our software was 32-bit, the installer produced by PC-Install was 16-bit, and some flavours of recent versions of Windows no longer run 16-bit software. So although the application could run, there was no way for users to install it.

So I set about using Inno Setup to create an installer which would work against the existing stock of CD-ROMs (binning them and issuing a new version isn’t financially viable).

The basic idea was for the installer to detect the CD-ROM, copy the files across, and set up the icons. Obviously, I couldn’t rely on the D: drive always being the optical drive, so I’d need to detect it dynamically. This stackoverflow post links to the code I used and adapted (it doesn’t work out of the box). It implements a function to discover the optical drive letter, and then uses a scripted constant to invoke it for use as the file source.

So, I got the function working, and then used it in my Source line:

Source: "{code:GetCdromDrive}\*.*"; DestDir: "{app}"; Flags: recursesubdirs;

However, when compiling/running this, I got the error:

Unknown filename prefix "{code:"

suggesting one couldn’t use scripted constants in Source lines. After a bit of fruitless experimentation and googling though I returned to the original example code and spotted I’d missed one of the Flags: ‘external’. This turns out to be the key:

Source: "{code:GetCdromDrive}\*.*"; DestDir: "{app}"; Flags: recursesubdirs external;

Apparently the source directory is ordinarily determined at compile time, and the ‘external’ keyword defers it to run time. (This is hardly obvious from the documentation though.)

  1. Walter said:

    I am new to using Inno Setup and was trying to figure out the dynamic assignment of the CD ROM drive also. Can you provide the code for “GetCdromDrive” so that I can use it in a script that I am attempting?

    Thank you,

    • Hi – here’s the code. It seems fairly magical, so I must have got it from elsewhere… It’s looking for a particular file on the CD-ROM (dd_icon32x32.ico) which I know ought to be there if it’s the CD-ROM I’m expecting. (Sorry for the formatting – I’ve put it in a code block, but WordPress doesn’t seem to preserve the indentation.)

      function GetCdromDrive(): string;
      S: string;
      I: Integer;
      DriveRoot: string;
      Result := '';
      SetLength(S, 65);
      I := GetLogicalDriveStrings(64, S);
      if I 0 then
      SetLength(S, I);
      I := Pos(#0, S);
      while I > 0 do
      DriveRoot := Copy(S, 1, I - 1);
      if (GetDriveType(DriveRoot) = 5) and FileExists(DriveRoot + 'files\dd_icon32x32.ico') then
      Result := DriveRoot;
      Delete(S, 1, I);
      I := Pos(#0, S);


  2. Dima Rabinowitz said:

    1. the link to the question in StackOverflow is broken.
    2. seems like this trick no longer works out-of-the-box:
    I’m getting “Invalid prototype for ‘GetCdromDrive'”

    the only way to fix it is to add a dummy string parameter.

    function GetCdromDrive(): string;
    turns into
    function GetCdromDrive(dummyString:string): string

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: