Inno Setup script for installation on a usb stick or any other removable media

inno

Sometimes you want to copy or install files to a removable disk (usb stick, sd card, …)

This inno setup script detects removable disks and creates a setup dialog for the removeable devices only.

Features

  • detects any removeable disk
  • Install to root usb folder
  • ignores hidden disks
  • compatible with unicode and ansi version of inno setup
  • x32 and x64 compatible

[Setup]
AppName=Setup to USB
AppVersion=1.0
DefaultDirName=USB
CreateUninstallRegKey=no
AllowRootDirectory=yes
Uninstallable=no

[Messages]
SelectDirBrowseLabel=Please insert a USB stick into your computer.

[Files]
Source: {src}\Files\* ;DestDir: {app}; Flags: recursesubdirs external


[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif
type
  TDriveType = (
    dtUnknown,
    dtNoRootDir,
    dtRemovable,
    dtFixed,
    dtRemote,
    dtCDROM,
    dtRAMDisk
  );
  TDriveTypes = set of TDriveType;


function GetDriveType(lpRootPathName: string): UINT;
  external 'GetDriveType{#AW}@kernel32.dll stdcall';
function GetLogicalDriveStrings(nBufferLength: DWORD; lpBuffer: string): DWORD;
  external 'GetLogicalDriveStrings{#AW}@kernel32.dll stdcall';
function GetLogicalDrives(): DWORD;
  external 'GetLogicalDrives@kernel32.dll stdcall';




var
  DirCombo: TNewComboBox;

#ifndef UNICODE
function IntToDriveType(Value: UINT): TDriveType;
begin
  Result := dtUnknown;
  case Value of
    1: Result := dtNoRootDir;
    2: Result := dtRemovable;
    3: Result := dtFixed;
    4: Result := dtRemote;
    5: Result := dtCDROM;
    6: Result := dtRAMDisk;
  end;
end;
#endif

function GetVolumeInformation(
#ifdef UNICODE
  lpRootPathName: PAnsiChar;
  lpVolumeNameBuffer: PAnsiChar;
  nVolumeNameSize: DWORD;
  var lpVolumeSerialNumber: DWORD;
  var lpMaximumComponentLength: DWORD;
  var lpFileSystemFlags: DWORD;
  lpFileSystemNameBuffer: PAnsiChar;
  nFileSystemNameSize: DWORD
  ): BOOL;
  external 'GetVolumeInformationA@kernel32.dll stdcall';
#else
  lpRootPathName: PChar;
  lpVolumeNameBuffer: PChar;
  nVolumeNameSize: DWORD;
  var lpVolumeSerialNumber: DWORD;
  var lpMaximumComponentLength: DWORD;
  var lpFileSystemFlags: DWORD;
  lpFileSystemNameBuffer: PChar;
  nFileSystemNameSize: DWORD
  ): BOOL;
  external 'GetVolumeInformationA@kernel32.dll stdcall';
  #endif



function LoWord(dw: DWORD): WORD;
begin
  Result := WORD(dw);
end;

function HiWord(dw: DWORD): WORD;
begin
  Result := WORD((dw shr 16) and $FFFF);
end;

function WordToHex(w: WORD): string;
begin
  Result := Format('%.4x', [w]);
end;

function FindVolumeSerial(const Drive: string): string;
var
  FileSystemFlags: DWORD;
  VolumeSerialNumber: DWORD;
  MaximumComponentLength: DWORD;
begin
  Result := '';
  // Note on passing PChars using RemObjects Pascal Script:
  // '' pass a nil PChar  
  // #0 pass an empty PChar    
  if GetVolumeInformation(
#ifdef UNICODE
    PAnsiChar(Drive), 
#else
    PChar(Drive),
#endif 
    '', // nil
    0,
    VolumeSerialNumber,
    MaximumComponentLength,
    FileSystemFlags,
    '', // nil
    0)
  then
    Result := WordToHex(HiWord(VolumeSerialNumber)) + '-' + WordToHex(LoWord(VolumeSerialNumber));
end;

function ListLogicalDrives(Filter: TDriveTypes; Drives: TStrings): Integer;
var
  S: string;
  I: Integer;
  DriveRoot: string;
begin
  Result := 0;
  GetLogicalDrives();
  I := GetLogicalDriveStrings(0, #0);
  if I > 0 then
  begin
    SetLength(S, I);
    if GetLogicalDriveStrings(Length(S), S) > 0 then
    begin
      S := TrimRight(S) + #0;
      I := Pos(#0, S);
      while I > 0 do
      begin
        DriveRoot := Copy(S, 1, I - 1);

        Log('The Value is: ' + IntToStr(GetDriveType(DriveRoot)));

        #ifdef UNICODE
        if (Filter = []) or
          ((TDriveType(GetDriveType(DriveRoot)) in Filter)) and (FindVolumeSerial(DriveRoot) <> '') then
        #else
        if (Filter = []) or
          (IntToDriveType(GetDriveType(DriveRoot)) in Filter) and (FindVolumeSerial(DriveRoot) <> '') then
        #endif
        begin
          Drives.Add(DriveRoot);
        end;
        Delete(S, 1, I);
        I := Pos(#0, S);
      end;
      Result := Drives.Count;
    end;
  end;
end;






procedure DriveComboChange(Sender: TObject);
begin
  WizardForm.DirEdit.Text := DirCombo.Text;
end;

procedure InitializeWizard;
var
  I: Integer;
  StringList: TStringList;
begin
  StringList := TStringList.Create;
  try
    if ListLogicalDrives([dtRemovable], StringList) > 0 then
    begin
      WizardForm.DirEdit.Visible := False;
      WizardForm.DirBrowseButton.Visible := False;

      DirCombo := TNewComboBox.Create(WizardForm);
      DirCombo.Parent := WizardForm.DirEdit.Parent;
      DirCombo.SetBounds(WizardForm.DirEdit.Left, WizardForm.DirEdit.Top,
        WizardForm.DirBrowseButton.Left + WizardForm.DirBrowseButton.Width -
        WizardForm.DirEdit.Left, WizardForm.DirEdit.Height);
      DirCombo.Style := csDropDownList;
      DirCombo.OnChange := @DriveComboChange;

      for I := 0 to StringList.Count - 1 do
        DirCombo.Items.Add(StringList[I]);

      DirCombo.ItemIndex := 0;
      DirCombo.OnChange(nil);
    end

    else
      begin
        MsgBox('No usb stick detected. Please insert an usb stick and restart setup', mbCriticalError, MB_OK);
        Abort;
    end;

    finally
      StringList.Free;
    end;


end;

Download example project: sd_setup_inno_example.zip

Leave a Reply

Your email address will not be published. Required fields are marked *