# Inno Setup Development Guide ## Overview **Inno Setup** is a free, open-source Windows installer creator by Jordan Russell and Martijn Laan. It creates professional single-EXE installers for Windows applications. - **Official Website**: https://jrsoftware.org/ - **Documentation**: https://jrsoftware.org/ishelp/ - **Our Projects Location**: `~/projects/inno/` ## What is Inno Setup? Inno Setup creates Windows installers that can: - Package applications into single EXE installers - Install files, create shortcuts, modify registry - Support 64-bit and Arm64 Windows architectures - Provide uninstall capabilities - Support multiple languages - Run silent/unattended installations - Execute custom scripts during installation **Notable users**: Visual Studio Code, Git for Windows, Embarcadero Delphi ## Supported Windows Versions - Windows 11, 10, 8.1, 8, 7 - Windows Server 2022, 2019, 2016, 2012 - All versions since 2006 - x64 and Arm64 architectures --- ## Our Inno Setup Template Based on `/home/camp/projects/inno/MappedDrive/MappedDrive.iss` This template demonstrates: - Modern wizard UI with custom branding - Custom wizard pages with user input - Password input fields - Multi-option selection pages - Embedded PowerShell script execution - Environment variable passing - No installation directory (utility-style installer) - Admin privileges requirement - Custom icons and images --- ## Inno Setup Script Structure ### File Format - **Extension**: `.iss` (Inno Setup Script) - **Encoding**: Plain text, UTF-8 recommended - **Comments**: Lines starting with `;` or `//` ### Script Sections All Inno Setup scripts are divided into sections marked with `[SectionName]`. #### **[Setup]** - Core Configuration Defines installer metadata and behavior. **Common Parameters from Our Template:** ```pascal [Setup] AppId={{GUID}} ; Unique identifier (use https://guidgenerator.com) AppName=Your Application Name ; Display name AppVersion=1.0 ; Version number AppPublisher=Your Company ; Publisher name AppPublisherURL=http://example.com ; Website AppSupportURL=http://example.com ; Support site AppUpdatesURL=http://example.com ; Updates page ; Installation behavior CreateAppDir=yes ; Create installation directory? (no for utilities) ChangesAssociations=no ; Register file associations? PrivilegesRequired=admin ; Require admin rights? (admin/lowest/none) ; Output configuration OutputDir=C:\Output ; Where to save installer EXE OutputBaseFilename=MyInstaller ; Installer filename (without .exe) SolidCompression=yes ; Better compression (slower build) ; UI customization WizardStyle=modern ; modern or classic SetupIconFile=icon.ico ; Installer icon WizardImageFile=image.bmp ; Left side image (164x314px) WizardSmallImageFile=small.bmp ; Top right image (55x58px) DisableWelcomePage=no ; Show welcome page? ; Uninstaller CreateUninstallRegKey=no ; Create uninstall entry? (no for utilities) ``` **Other Useful Parameters:** ```pascal DefaultDirName={autopf}\MyApp ; Default install path DefaultGroupName=My Application ; Start menu folder LicenseFile=license.txt ; Show license agreement InfoBeforeFile=readme.txt ; Show before install InfoAfterFile=info.txt ; Show after install Compression=lzma2 ; Compression algorithm (lzma2/lzma/zip/bzip) ArchitecturesInstallIn64BitMode=x64 arm64 ; 64-bit mode for these architectures UninstallDisplayIcon={app}\MyApp.exe ; Uninstaller icon ``` --- #### **[Languages]** - Localization Define supported languages. ```pascal [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" Name: "spanish"; MessagesFile: "compiler:Languages\Spanish.isl" Name: "french"; MessagesFile: "compiler:Languages\French.isl" ``` **Built-in languages**: English, French, German, Spanish, Italian, Dutch, Portuguese, and 30+ others. --- #### **[Messages]** - Custom Text Override default installer text. **From Our Template:** ```pascal [Messages] WelcomeLabel2=This utility will help you reconnect your mapped network drives... ``` **Common Messages to Override:** - `WelcomeLabel1` - Welcome page title - `WelcomeLabel2` - Welcome page description - `FinishedLabel` - Completion message - `ButtonInstall` - Install button text (default: "Install") - `ButtonNext` - Next button text - `ButtonCancel` - Cancel button text **Full list**: https://jrsoftware.org/ishelp/index.php?topic=messages --- #### **[Files]** - Files to Install Copy files from source to destination. ```pascal [Files] Source: "C:\MyApp\MyApp.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\MyApp\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs Source: "C:\MyApp\config.ini"; DestDir: "{app}"; Flags: onlyifdoesntexist ``` **Common Flags:** - `ignoreversion` - Always copy, overwrite existing - `recursesubdirs` - Include subdirectories - `onlyifdoesntexist` - Don't overwrite existing files - `createallsubdirs` - Create empty subdirectories - `restartreplace` - Replace on reboot if file is locked - `regserver` - Register DLL/OCX after install - `isreadme` - Offer to open file after install --- #### **[Dirs]** - Create Directories Create empty directories. ```pascal [Dirs] Name: "{app}\logs" Name: "{app}\cache"; Permissions: users-modify Name: "{commonappdata}\MyApp\data" ``` --- #### **[Icons]** - Create Shortcuts Create Start Menu, Desktop, or other shortcuts. ```pascal [Icons] Name: "{autoprograms}\My Application"; Filename: "{app}\MyApp.exe" Name: "{autodesktop}\My Application"; Filename: "{app}\MyApp.exe" Name: "{autostartup}\My Application"; Filename: "{app}\MyApp.exe" Name: "{group}\Uninstall My App"; Filename: "{uninstallexe}" ``` **Common Locations:** - `{autoprograms}` - Start Menu programs folder - `{autodesktop}` - Desktop - `{autostartup}` - Startup folder (runs on boot) - `{group}` - App's Start Menu folder (defined by DefaultGroupName) - `{commonstartmenu}` - All Users Start Menu --- #### **[Registry]** - Modify Windows Registry Add, modify, or delete registry keys. ```pascal [Registry] Root: HKLM; Subkey: "Software\MyCompany\MyApp"; ValueType: string; ValueName: "InstallPath"; ValueData: "{app}" Root: HKCU; Subkey: "Software\MyCompany\MyApp"; ValueType: dword; ValueName: "Version"; ValueData: "1" Root: HKLM; Subkey: "Software\MyCompany\MyApp"; Flags: uninsdeletekey ``` **Value Types:** - `string` - Text value (REG_SZ) - `expandsz` - Expandable string (REG_EXPAND_SZ) - `dword` - 32-bit number (REG_DWORD) - `qword` - 64-bit number (REG_QWORD) - `binary` - Binary data (REG_BINARY) - `multisz` - Multi-string (REG_MULTI_SZ) **Common Flags:** - `uninsdeletekey` - Delete entire key on uninstall - `uninsdeletekeyifempty` - Delete key if empty on uninstall - `uninsdeletevalue` - Delete value on uninstall --- #### **[Run]** - Execute Programs After Install Run programs at the end of installation. ```pascal [Run] Filename: "{app}\MyApp.exe"; Description: "Launch My Application"; Flags: nowait postinstall skipifsilent Filename: "{app}\setup.exe"; Parameters: "/config"; Flags: runhidden waituntilterminated ``` **Common Flags:** - `nowait` - Don't wait for program to finish - `waituntilterminated` - Wait for program to finish before continuing - `postinstall` - Show checkbox, let user choose to run - `skipifsilent` - Don't run during silent install - `runhidden` - Hide window - `runasoriginaluser` - Run as non-admin even if installer is admin --- #### **[UninstallRun]** - Execute Programs During Uninstall Run programs when uninstalling. ```pascal [UninstallRun] Filename: "{app}\cleanup.exe"; Parameters: "/silent" ``` --- #### **[Code]** - Pascal Script Advanced scripting for custom installer logic. **Our Template Demonstrates:** - Custom wizard pages - User input collection - Password fields - Validation logic - Executing PowerShell scripts - Passing data via environment variables - External DLL calls **Basic Structure:** ```pascal [Code] // Global variables var MyVariable: String; // Initialize wizard procedure InitializeWizard(); begin // Create custom pages here end; // Validate page before continuing function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; if CurPageID = wpWelcome then begin // Validate something if SomeCondition then begin MsgBox('Error message', mbError, MB_OK); Result := False; end; end; end; // Execute after installation procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep = ssPostInstall then begin // Do something after files installed end; end; ``` --- ## Constants (Directory/Path Placeholders) Inno Setup uses constants wrapped in `{brackets}` for common paths. ### Common Application Directories ```pascal {app} ; Installation directory (e.g., C:\Program Files\MyApp) {win} ; Windows directory (e.g., C:\Windows) {sys} ; Windows System32 directory {syswow64} ; System32 for 32-bit apps on 64-bit Windows {tmp} ; Temporary directory for this install session ``` ### User Directories ```pascal {userappdata} ; C:\Users\{username}\AppData\Roaming {localappdata} ; C:\Users\{username}\AppData\Local {userdocs} ; C:\Users\{username}\Documents {userdesktop} ; C:\Users\{username}\Desktop {userpf} ; C:\Users\{username}\AppData\Local\Programs (non-admin installs) ``` ### Common Directories (All Users) ```pascal {commonappdata} ; C:\ProgramData {commonpf} ; C:\Program Files {commonpf32} ; C:\Program Files (x86) on 64-bit, C:\Program Files on 32-bit {commonpf64} ; C:\Program Files (64-bit only) {commondocs} ; C:\Users\Public\Documents {commondesktop} ; C:\Users\Public\Desktop ``` ### Shortcuts ```pascal {autoprograms} ; User's Start Menu\Programs {autostartup} ; User's Startup folder {autodesktop} ; User's Desktop {group} ; App's Start Menu folder (DefaultGroupName) {commonstartmenu} ; All Users Start Menu ``` ### Other Useful Constants ```pascal {uninstallexe} ; Path to uninstaller {src} ; Directory where installer EXE is located {sd} ; System drive (usually C:) {%USERNAME} ; Current Windows username (environment variable) {computername} ; Computer name ``` --- ## Custom Wizard Pages (Pascal Script) ### Creating Input Pages **Our Template Uses These Page Types:** #### 1. **Input Option Page** (Checkboxes/Radio Buttons) ```pascal var ServerSelectionPage: TInputOptionWizardPage; procedure InitializeWizard(); begin ServerSelectionPage := CreateInputOptionPage(wpWelcome, 'Select Servers to Reconnect', 'Choose which network drives you need to reconnect', 'Select the servers below.', False, // Exclusive (radio buttons if True, checkboxes if False) False); // ListBox style? ServerSelectionPage.Add('Option 1 text'); ServerSelectionPage.Add('Option 2 text'); // Set defaults ServerSelectionPage.Values[0] := True; // First option checked ServerSelectionPage.Values[1] := False; // Second option unchecked end; ``` **Reading Values:** ```pascal if ServerSelectionPage.Values[0] then begin // Option 1 was selected end; ``` --- #### 2. **Input Query Page** (Text Fields) ```pascal var PasswordPage: TInputQueryWizardPage; procedure InitializeWizard(); begin PasswordPage := CreateInputQueryPage(wpWelcome, 'Enter Network Credentials', 'Please enter your password', 'Enter your password below:'); PasswordPage.Add('Password:', True); // True = password field (masked) PasswordPage.Add('Username:', False); // False = regular text field end; ``` **Reading Values:** ```pascal UserPassword := PasswordPage.Values[0]; UserName := PasswordPage.Values[1]; ``` --- #### 3. **Output Progress Page** (Progress Bar) ```pascal var ProgressPage: TOutputProgressWizardPage; procedure InitializeWizard(); begin ProgressPage := CreateOutputProgressPage('Processing...', 'Please wait while we process your data.'); end; // Later, when executing procedure ProcessData(); begin ProgressPage.SetProgress(0, 100); ProgressPage.Show; try // Do work ProgressPage.SetProgress(50, 100); // More work ProgressPage.SetProgress(100, 100); finally ProgressPage.Hide; end; end; ``` --- ### Page Order and IDs **Built-in Page IDs:** ```pascal wpWelcome ; Welcome page wpLicense ; License agreement wpPassword ; Password prompt wpInfoBefore ; Pre-install info wpUserInfo ; User info (name, company) wpSelectDir ; Installation directory wpSelectComponents ; Component selection wpSelectProgramGroup ; Start menu folder wpSelectTasks ; Task selection (checkboxes) wpReady ; Ready to Install wpPreparing ; Preparing to Install wpInstalling ; Installing (progress bar) wpInfoAfter ; Post-install info wpFinished ; Finished page ``` **Insert Custom Pages:** ```pascal // After wpWelcome MyPage := CreateInputQueryPage(wpWelcome, 'Title', 'Subtitle', 'Description'); // Before wpReady MyPage := CreateInputQueryPage(wpSelectDir, 'Title', 'Subtitle', 'Description'); ``` --- ### Validation and Logic **Validate Before Next Page:** ```pascal function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; // Allow by default if CurPageID = PasswordPage.ID then begin if Trim(PasswordPage.Values[0]) = '' then begin MsgBox('Password cannot be empty!', mbError, MB_OK); Result := False; // Prevent advancing end; end; end; ``` **Skip Pages Conditionally:** ```pascal function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; if PageID = wpSelectComponents then Result := True; // Skip component selection end; ``` --- ## Executing External Programs ### From [Run] Section ```pascal [Run] Filename: "{app}\MyApp.exe"; Description: "Launch application"; Flags: postinstall nowait Filename: "cmd.exe"; Parameters: "/c echo Done"; Flags: runhidden waituntilterminated ``` ### From Pascal Code ```pascal var ResultCode: Integer; // Execute and wait if Exec('powershell.exe', '-File "script.ps1"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin if ResultCode = 0 then MsgBox('Success!', mbInformation, MB_OK) else MsgBox('Failed with code ' + IntToStr(ResultCode), mbError, MB_OK); end; ``` **Exec Parameters:** ```pascal Exec(Filename, Parameters, WorkingDir, ShowCmd, Wait, ResultCode) ; ShowCmd options: SW_SHOW ; Show window normally SW_HIDE ; Hide window SW_SHOWNORMAL ; Normal window SW_MAXIMIZE ; Maximized window ; Wait options: ewNoWait ; Don't wait, return immediately ewWaitUntilTerminated ; Wait until program exits ewWaitUntilIdle ; Wait until program is idle ``` --- ## PowerShell Integration (Our Template Pattern) **Our MappedDrive template demonstrates:** 1. Build PowerShell script as a string in Pascal 2. Save script to temporary file 3. Pass sensitive data via environment variable 4. Execute PowerShell script 5. Clean up temporary files and variables **Code Pattern:** ```pascal var ScriptPath: String; PowerShellScript: String; ResultCode: Integer; function NextButtonClick(CurPageID: Integer): Boolean; begin if CurPageID = wpReady then begin // 1. Build PowerShell script PowerShellScript := '$username = "' + Username + '"' + #13#10 + '$password = [System.Environment]::GetEnvironmentVariable("TEMP_PASSWORD","Process")' + #13#10 + 'Write-Host "Hello, $username"' + #13#10; // 2. Save to temp file ScriptPath := ExpandConstant('{tmp}\script.ps1'); SaveStringToFile(ScriptPath, PowerShellScript, False); // 3. Set environment variable (for sensitive data) SetEnvironmentVariable('TEMP_PASSWORD', UserPassword); // 4. Execute PowerShell Exec('powershell.exe', '-NoProfile -ExecutionPolicy Bypass -File "' + ScriptPath + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode); // 5. Cleanup SetEnvironmentVariable('TEMP_PASSWORD', ''); DeleteFile(ScriptPath); end; end; ``` **Why This Pattern?** - **Security**: Password never written to disk, only in memory - **Flexibility**: Can build complex PowerShell scripts dynamically - **Clean**: Temporary files removed after execution - **Visibility**: User sees PowerShell output (or use SW_HIDE) --- ## Environment Variables ### Reading Environment Variables ```pascal var UserName: String; UserName := GetEnv('USERNAME'); UserName := ExpandConstant('{%USERNAME}'); // Alternative syntax ``` ### Setting Environment Variables (Windows API Call) ```pascal // Declare external function function SetEnvironmentVariable(lpName: String; lpValue: String): Boolean; external 'SetEnvironmentVariableW@kernel32.dll stdcall'; // Use it SetEnvironmentVariable('MY_VAR', 'MyValue'); ``` **Scope**: Process-level only (not permanent, cleared when installer exits). --- ## Working with Files in Pascal Script ### Save String to File ```pascal SaveStringToFile(FileName, Content, Append) SaveStringToFile('C:\output.txt', 'Hello World', False); // Overwrite SaveStringToFile('C:\output.txt', 'New line', True); // Append ``` ### Load File to String ```pascal var Content: String; if LoadStringFromFile('C:\input.txt', Content) then MsgBox(Content, mbInformation, MB_OK); ``` ### Delete File ```pascal DeleteFile('C:\temp\file.txt'); ``` ### File Exists Check ```pascal if FileExists('C:\MyApp\config.ini') then MsgBox('Config found!', mbInformation, MB_OK); ``` ### Directory Exists Check ```pascal if DirExists('C:\MyApp') then MsgBox('Directory exists!', mbInformation, MB_OK); ``` --- ## Common Pascal Script Functions ### Message Boxes ```pascal MsgBox('Message text', MessageType, Buttons) MsgBox('Installation complete!', mbInformation, MB_OK); MsgBox('Are you sure?', mbConfirmation, MB_YESNO); MsgBox('Error occurred!', mbError, MB_OK); MsgBox('Warning!', mbWarning, MB_OK); // Check result if MsgBox('Continue?', mbConfirmation, MB_YESNO) = IDYES then begin // User clicked Yes end; ``` **Message Types:** - `mbInformation` - Info icon - `mbConfirmation` - Question icon - `mbError` - Error icon - `mbWarning` - Warning icon - `mbCriticalError` - Critical error icon **Button Types:** - `MB_OK` - OK button only - `MB_OKCANCEL` - OK and Cancel - `MB_YESNO` - Yes and No - `MB_YESNOCANCEL` - Yes, No, Cancel - `MB_RETRYCANCEL` - Retry and Cancel ### String Functions ```pascal Trim(S) ; Remove leading/trailing spaces UpperCase(S) ; Convert to uppercase LowerCase(S) ; Convert to lowercase Length(S) ; String length Copy(S, Index, Count); Extract substring Pos(Substr, S) ; Find position of substring StringChangeEx(S, Find, Replace, ReplaceAll); Replace text ``` ### Conversion Functions ```pascal IntToStr(123) ; Integer to string: "123" StrToInt('123') ; String to integer: 123 FloatToStr(1.5) ; Float to string: "1.5" StrToFloat('1.5') ; String to float: 1.5 ``` ### System Functions ```pascal GetUserNameString() ; Current Windows username GetComputerNameString(); Computer name ExpandConstant('{app}'); Expand path constant IsAdmin() ; Is installer running as admin? IsWin64() ; Is Windows 64-bit? ``` --- ## Building the Installer ### Using Inno Setup Compiler GUI 1. Open Inno Setup Compiler 2. File → Open → Select `.iss` file 3. Build → Compile (or press F9) 4. Output EXE created in `OutputDir` ### Command Line Compilation ```bash # Windows "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "C:\path\to\script.iss" # Output # Successful compile: Exit code 0 # Compilation errors: Exit code 1 ``` ### Automated Build Script ```batch @echo off set INNO="C:\Program Files (x86)\Inno Setup 6\ISCC.exe" set SCRIPT="C:\Projects\MyApp\installer.iss" echo Building installer... %INNO% %SCRIPT% if %ERRORLEVEL% EQU 0 ( echo Success! Installer created. ) else ( echo Build failed! exit /b 1 ) ``` --- ## Our Project Template Assets ### Required Files for MappedDrive Template ``` MappedDrive/ ├── MappedDrive.iss ; Main script ├── gea-logo.ico ; Setup icon (for EXE) ├── patrick.bmp ; Large wizard image (164x314px) └── patrick-sm.bmp ; Small wizard image (55x58px) ``` ### Image Specifications - **SetupIconFile**: `.ico` format, 32x32 or 48x48px - **WizardImageFile**: `.bmp` format, 164x314px (left side of wizard) - **WizardSmallImageFile**: `.bmp` format, 55x58px (top-right corner) --- ## Creating a New Project (Quick Start) ### 1. Create Project Directory ```bash cd ~/projects/inno mkdir MyNewProject cd MyNewProject ``` ### 2. Copy Template ```bash # Copy the MappedDrive template as starting point cp ../MappedDrive/MappedDrive.iss ./MyProject.iss cp ../MappedDrive/*.bmp . cp ../MappedDrive/*.ico . ``` ### 3. Edit Basic Settings ```pascal [Setup] AppId={{NEW-GUID-HERE}} ; Generate at https://guidgenerator.com AppName=My New Application AppVersion=1.0 AppPublisher=WJDT OutputBaseFilename=MyNewApp ; Adjust these based on your needs CreateAppDir=yes ; yes if installing files PrivilegesRequired=lowest ; lowest/admin based on needs ``` ### 4. Add Your Files ```pascal [Files] Source: "C:\Source\MyApp.exe"; DestDir: "{app}"; Flags: ignoreversion ``` ### 5. Customize Wizard Pages - Modify or remove custom pages in `[Code]` section - Adjust welcome message in `[Messages]` - Update PowerShell script logic if needed ### 6. Build and Test ```bash # Build on Windows (can't build on Linux, Inno Setup is Windows-only) # Copy .iss and assets to Windows, then compile ``` --- ## Best Practices ### Security - ✅ **DO**: Pass sensitive data via environment variables (cleared after use) - ✅ **DO**: Use temporary files for scripts, delete after execution - ❌ **DON'T**: Hardcode passwords or secrets in `.iss` file - ❌ **DON'T**: Write sensitive data to permanent files ### Performance - ✅ Use `SolidCompression=yes` for smaller installers (slower build) - ✅ Use `Compression=lzma2` for best compression ratio - ✅ Set `DiskSpanning=no` for single-file installers ### User Experience - ✅ Always provide meaningful `WelcomeLabel2` message - ✅ Use `postinstall` flag with `skipifsilent` for optional launch - ✅ Show progress/feedback for long operations - ✅ Provide clear error messages with actions to take ### Code Organization - ✅ Use constants for repeated values - ✅ Comment complex Pascal code - ✅ Break long PowerShell scripts into logical sections - ✅ Validate all user inputs before proceeding ### Testing - ✅ Test on clean VM (no previous installs) - ✅ Test with non-admin user (if `PrivilegesRequired=lowest`) - ✅ Test silent install: `MyInstaller.exe /VERYSILENT /NORESTART` - ✅ Test uninstall process --- ## Common Patterns from MappedDrive Template ### Pattern 1: Custom Input Collection ```pascal var InputPage: TInputQueryWizardPage; UserInput: String; procedure InitializeWizard(); begin InputPage := CreateInputQueryPage(wpWelcome, 'Title', 'Subtitle', 'Prompt'); InputPage.Add('Enter value:', False); end; function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; if CurPageID = InputPage.ID then begin UserInput := InputPage.Values[0]; if UserInput = '' then begin MsgBox('Value required!', mbError, MB_OK); Result := False; end; end; end; ``` ### Pattern 2: Multi-Option Selection ```pascal var OptionPage: TInputOptionWizardPage; procedure InitializeWizard(); begin OptionPage := CreateInputOptionPage(wpWelcome, 'Title', 'Subtitle', 'Prompt', False, False); OptionPage.Add('Option 1'); OptionPage.Add('Option 2'); OptionPage.Values[0] := True; end; function NextButtonClick(CurPageID: Integer): Boolean; begin Result := True; if CurPageID = OptionPage.ID then begin if not (OptionPage.Values[0] or OptionPage.Values[1]) then begin MsgBox('Select at least one option!', mbError, MB_OK); Result := False; end; end; end; ``` ### Pattern 3: Confirmation Dialog ```pascal if MsgBox('Are you sure you want to proceed?', mbConfirmation, MB_YESNO) = IDYES then begin // User confirmed end else begin // User cancelled end; ``` ### Pattern 4: Dynamic PowerShell Execution ```pascal var PSScript: String; ScriptPath: String; ResultCode: Integer; // Build script dynamically PSScript := 'Write-Host "Hello"' + #13#10; PSScript := PSScript + 'Write-Host "World"' + #13#10; // Save and execute ScriptPath := ExpandConstant('{tmp}\temp.ps1'); SaveStringToFile(ScriptPath, PSScript, False); Exec('powershell.exe', '-NoProfile -ExecutionPolicy Bypass -File "' + ScriptPath + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode); DeleteFile(ScriptPath); ``` --- ## Debugging Tips ### Enable Debug Mode Add to `[Setup]`: ```pascal [Setup] OutputDebugString=yes ``` ### View Installer Log Run installer with `/LOG`: ```cmd MyInstaller.exe /LOG="C:\install.log" ``` ### Test in Virtual Machine - Use Windows Sandbox or VM for clean testing - Snapshot before install, restore after test ### Common Issues 1. **"Error reading file"**: Check file paths in `[Files]` section 2. **"Access denied"**: Verify `PrivilegesRequired=admin` if needed 3. **PowerShell fails**: Check execution policy and script syntax 4. **Custom page not appearing**: Verify page ID in `CreateInputXXXPage(PageID, ...)` --- ## Silent Installation ### Command Line Parameters ```cmd MyInstaller.exe /SILENT ; Silent mode, shows progress MyInstaller.exe /VERYSILENT ; Very silent mode, no UI MyInstaller.exe /SUPPRESSMSGBOXES ; Suppress message boxes MyInstaller.exe /NORESTART ; Don't restart computer MyInstaller.exe /DIR="C:\MyApp" ; Override install directory MyInstaller.exe /LOG="C:\log.txt"; Create install log ``` ### Combine Parameters ```cmd MyInstaller.exe /VERYSILENT /NORESTART /LOG="C:\install.log" ``` ### Unattended Install Script ```batch @echo off MyInstaller.exe /VERYSILENT /NORESTART /LOG="C:\install.log" if %ERRORLEVEL% EQU 0 ( echo Installation successful ) else ( echo Installation failed with code %ERRORLEVEL% ) ``` --- ## Reference Links - **Official Documentation**: https://jrsoftware.org/ishelp/ - **Pascal Scripting**: https://jrsoftware.org/ishelp/index.php?topic=scriptintro - **Example Scripts**: https://jrsoftware.org/ishelp/index.php?topic=scriptexamples - **Command Line Parameters**: https://jrsoftware.org/ishelp/index.php?topic=setupcmdline - **Download Inno Setup**: https://jrsoftware.org/isdl.php --- ## Quick Reference Summary | Task | Code Example | |------|--------------| | Create installer | `[Setup] AppName=MyApp` | | Install files | `[Files] Source: "app.exe"; DestDir: "{app}"` | | Create shortcut | `[Icons] Name: "{autodesktop}\MyApp"; Filename: "{app}\app.exe"` | | Custom page | `CreateInputQueryPage(wpWelcome, 'Title', 'Desc', 'Prompt')` | | Execute program | `Exec('cmd.exe', '/c echo done', '', SW_SHOW, ewWaitUntilTerminated, Code)` | | Message box | `MsgBox('Hello', mbInformation, MB_OK)` | | Get username | `GetUserNameString()` | | Check if admin | `IsAdmin()` | | Build installer | Press F9 in Inno Setup Compiler | | Silent install | `MyInstaller.exe /VERYSILENT` | --- **Template Location**: `/home/camp/projects/inno/MappedDrive/MappedDrive.iss` **Documentation**: This file **Last Updated**: 2025-10-10