Summary
During a routine upgrade of a Windows MSI installer, a prerequisite update for Strawberry Perl triggered a false-positive failure report in InstallShield 2019. While the actual software installation succeeded, the InstallShield engine flagged the prerequisite step as an error. This issue stems from how the prerequisite editor interprets process exit codes and how the batch file handles multiple sequential command executions.
Root Cause
The failure is caused by a combination of non-zero exit codes and improper command chaining within the batch script:
- Exit Code Propagation: In a batch file, the
%errorlevel%variable reflects the exit code of the last command executed. If the finalMsiExec.execall returns a code like3010(Restart Required), InstallShield interprets any non-zero value as a hard failure. - Unsuccessful Uninstalls: The script attempts to uninstall specific GUIDs. If those versions are not present on the target machine,
MsiExecreturns an error code (e.g.,1605– Product not found). Because these are not checked, the error state persists or pollutes the execution flow. - Encoding Mismatch (Kanji Issue): The appearance of Kanji in the log file indicates a character encoding mismatch. The batch file is likely being interpreted in a different code page (like Shift-JIS) than what the logger expects, or the
msiexeclog output is being piped into a stream that misinterprets the byte order mark. - Command Chaining: The script lacks logical operators (
&&or||) to handle the success or failure of individual steps, leading to an unpredictable final state.
Why This Happens in Real Systems
In large-scale production environments, this pattern is common due to:
- Non-Standard Exit Codes: Many enterprise installers use “Success with warnings” codes (like
3010). Standard shell environments often treat any value> 0as a failure. - Brittle Prerequisite Scripts: Engineers often write “fire and forget” scripts that assume a specific system state (e.g., “the old version is definitely there”), which fails in clean-room or varied environments.
- Silent Failures: When piping output to logs, improper redirection can hide the actual error, leaving the engineer with only a generic “Process exited with error” message.
Real-World Impact
- Deployment Blockage: Automated CI/CD pipelines or enterprise deployment tools (like SCCM/MECM) will mark the entire installation as Failed, even if the core application is fully functional.
- Increased MTTR (Mean Time To Recovery): Engineers waste hours debugging the application code when the issue is actually located in the packaging layer.
- Configuration Drift: If the installer fails halfway through, the system might be left in a “half-installed” state, where old versions are gone but new ones are not fully registered.
Example or Code (if necessary and relevant)
To fix this, we must ensure the batch file explicitly exits with 0 if we want InstallShield to proceed, and we must handle the uninstallation errors gracefully.
@echo off
set LOG_FILE="c:\perl_install.log"
echo Starting Perl Update... > %LOG_FILE%
:: Attempt uninstalls; ignore error if product is not found
MsiExec.exe /passive /X{0BE917CD-6CE8-1014-9C0C-9680A6A774DD} /norestart >> %LOG_FILE% 2>&1
MsiExec.exe /passive /X{8075BCC9-804A-1014-97A8-A0999374D9D1} /norestart >> %LOG_FILE% 2>&1
:: Install the new version
MsiExec.exe /i strawberry-perl-5.42.0.1-64bit.msi /qb /norestart /L*V %LOG_FILE%
:: Capture the specific exit code
set EXIT_CODE=%errorlevel%
echo Final Exit Code: %EXIT_CODE% >> %LOG_FILE%
:: IMPORTANT: Force exit 0 for InstallShield if the code is 3010 (Restart Required)
:: or if the installation was actually successful.
if %EXIT_CODE% EQU 3010 exit /b 0
if %EXIT_CODE% EQU 0 exit /b 0
:: Otherwise, return the actual error
exit /b %EXIT_CODE%
How Senior Engineers Fix It
- Standardize Exit Logic: They write wrappers that map complex MSI exit codes (like
3010) to a standard0for the orchestrator (InstallShield). - Idempotency: They design scripts so that they can be run multiple times without error, regardless of whether the “previous version” actually exists.
- Robust Logging: Instead of simple redirection, they use
/L*V(verbose logging) and ensure thatstdoutandstderrare both captured correctly to avoid encoding artifacts. - Validation: They don’t just check the exit code; they verify the presence of the binary in the file system after the script runs.
Why Juniors Miss It
- Assuming 0 is the only Success: Juniors often forget that in Windows Installer land,
0isn’t the only successful outcome;3010is a standard “Success, but reboot” code. - Ignoring Shell Context: They treat batch files like Python or C# scripts, not realizing that
%errorlevel%is a volatile global state that changes with every single command. - Overlooking Encoding: They might see “garbage text” in a log and assume it’s a bug in the software, rather than realizing the shell environment is misinterpreting the bytes.
- Lack of Defensive Programming: They write “Happy Path” scripts that work on their local machine but fail in production because they don’t account for missing prerequisites or existing registry keys.