MSI Installer Design
Design notes for adding WiX 4 based MSI packaging for BN Tech Virtual Scanner.
1. Requirement
The project needs WiX Toolset 4.0.4 based MSI packages for 32-bit and 64-bit builds. Each MSI supports both English and Simplified Chinese UI via embedded language transforms.
Outputs:
build\installer\win32\bntech_virtual_scanner_win32.msi
build\installer\win64\bntech_virtual_scanner_win64.msi
The MSI installs the same files as build.bat install / cmake --install:
bntech_virtual_scanner.dsFreeImage.dllTWAIN_logo.png
Target directories:
32-bit: C:\Windows\twain_32\bntech\
64-bit: C:\Windows\twain_64\bntech\
The MSI auto-detects the system language: Chinese Windows shows Chinese UI and writes %APPDATA%\bntech\config.ini with language=zh_CN; English Windows shows English UI and writes no config.ini (the data source defaults to en_US).
2. Domain knowledge
A TWAIN Data Source is a DLL-like .ds file. 32-bit and 64-bit TWAIN sources are installed in different Windows directories. Because the target is under C:\Windows, installation usually requires administrator privileges.
WiX 4 uses wix build to generate MSI files from .wxs source. This project adds installer\bntech_virtual_scanner.wxs. CMake builds two single-language MSIs and merges them into one multi-language package using Windows SDK tools.
The Windows Installer does not support switching the built-in UI language at runtime in a single MSI. Multi-language MSIs use embedded transforms (.mst), which means building two single-language MSIs and merging them:
| Tool | Purpose |
|---|---|
MsiTran.exe |
Generate .mst transform from two single-language MSIs |
WiSubStg.vbs |
Embed .mst into MSI sub-storage |
WiLangId.vbs |
Update MSI summary info with supported language list |
Build flow:
wix build ... -culture en-us → win32_en_US.msi
wix build ... -culture zh-cn → win32_zh_CN.msi
MsiTran.exe -g en.msi zh.msi zh_CN.mst
copy en.msi → final.msi
WiSubStg.vbs final.msi zh_CN.mst 2052
WiLangId.vbs final.msi Package 1033,2052
cleanup en.msi zh.msi zh_CN.mst
The -culture parameter tells WiX UI extension to use the matching language for standard wizard text.
The project uses WixToolset.UI.wixext with WixUI_InstallDir, which provides a full installation wizard with directory selection, progress, and completion pages. The -culture parameter selects the matching language for the wizard.
3. Design goals
- Build MSI packages with WiX 4.0.4.
- Support separate 32-bit and 64-bit MSI outputs.
- Single MSI with embedded English + Chinese UI, auto-selected by system language.
- Install the same files as the existing install flow.
- Provide CMake targets
msi32andmsi64. - Support
build.bat msi32andbuild.bat msi64. - Keep no-argument
build.batbehavior: build, install toC:\Windows, and package both MSI files. - Support language configuration via
APP_LANGUAGEbaked into each transform. - Use WixUI_InstallDir standard wizard.
Non-goals:
- No runtime language selection page (MSI limitation; language is selected via embedded transforms).
- No VC Runtime/bootstrapper packaging yet.
- No all-user AppData synchronization yet.
- No install/uninstall success/failure message boxes.
4. Overall design
Added installer file:
installer\bntech_virtual_scanner.wxs
Added CMake targets and functions:
msi32
msi64
add_multilang_msi()
Added build script commands:
build.bat msi32
build.bat msi64
No-argument build.bat now performs:
build win32
build win64
install win32
install win64
build msi32 (en-us + zh-cn → merged)
build msi64 (en-us + zh-cn → merged)
5. Key decisions and rationale
5.1 Separate MSI per architecture
Separate MSI packages keep the TWAIN architecture-specific directories simple and avoid mixed-architecture component complexity.
5.2 Use CMake build output as WiX source
WiX packages files from build\win32 or build\win64, where CMake already places .ds, FreeImage.dll, and TWAIN_logo.png.
5.3 Embedded language transforms for multi-language
Decision: merge two single-language MSIs into one using Windows SDK tools.
Rationale:
- MSI cannot switch built-in UI text at runtime.
- Embedded transforms are the native Windows Installer mechanism for multi-language.
- Users get the right language automatically with no extra steps.
- Users can still force a language:
msiexec /i xxx.msi TRANSFORMS=:2052.
5.4 Use WixUI_InstallDir standard wizard
Decision: depend on WixToolset.UI.wixext with WixUI_InstallDir.
Rationale:
- The extension 4.0.6 is available and reliable in the current build environment.
- Provides a full wizard with directory selection, progress, and completion.
- Combined with
-culture, the wizard text auto-localizes.
5.5 No install/uninstall message boxes
Decision: remove all VBScript custom action message boxes.
Rationale:
- WixUI_InstallDir already provides a progress bar and completion page.
- Avoids VBScript compatibility issues in enterprise and silent-install scenarios.
- Simplifies the build process and file maintenance.
5.6 Compile-time AppLanguage preprocessor variable
Decision: APP_LANGUAGE is baked in at WiX compile time via $(var.AppLanguage).
Rationale:
- Each language variant naturally carries the correct
APP_LANGUAGEvalue. - Users do not need to pass a language parameter on the command line.
APP_LANGUAGEcontrols config.ini writing:en_US→ skip,zh_CN→ write.
6. Component changes
6.1 Installer layer
installer\bntech_virtual_scanner.wxs defines:
- product name and UpgradeCode per architecture via
$(var.Platform); - product display name and
APP_LANGUAGEper language via$(var.AppLanguage); - file components:
.ds,FreeImage.dll,TWAIN_logo.png; %APPDATA%\bntechdirectory;- conditional
config.iniwriting; WixUI_InstallDirstandard wizard.
6.2 CMake layer
CMakeLists.txt adds:
WIX_EXECUTABLE,WIX_UI_EXTENSION,WIX_SOURCE_FILEWINSDK_BIN,MSITRAN,WISUBSTG,WILANGIDadd_multilang_msi()function
Each add_multilang_msi call performs:
1. wix build English MSI (-culture en-us)
2. wix build Chinese MSI (-culture zh-cn)
3. MsiTran.exe -g to generate transform
4. Copy English MSI as final output
5. WiSubStg.vbs to embed transform (LCID 2052)
6. WiLangId.vbs to declare multi-language support (1033,2052)
7. Clean up intermediate files
6.3 build.bat layer
build.bat adds msi32, msi64, and a :msi helper. The no-argument path builds, installs, and packages both architectures.
6.4 i18n config layer
The installer does not duplicate runtime i18n logic. It only writes %APPDATA%\bntech\config.ini, which the data source already knows how to read.
7. Build and install flow
No-argument flow:
build.bat
→ elevate for Windows install
→ build win32 and win64
→ install both to C:\Windows
→ build msi32 and msi64
Single MSI flow:
build.bat msi32
build.bat msi64
Installation examples:
# Auto-detect system language
msiexec /i bntech_virtual_scanner_win64.msi
# Force Simplified Chinese
msiexec /i bntech_virtual_scanner_win64.msi TRANSFORMS=:2052
8. Limitations
- MSI cannot switch UI language at runtime (single-MSI limitation). Language selection is handled by embedded transforms at install time.
%APPDATA%behavior needs more testing under elevated and multi-user installs.- No install/uninstall result message boxes. Results are visible through the WixUI progress bar and completion page.
- WiX version is not checked automatically.
- MSI build is not yet part of CI.
- Silent installs are fully silent (no pop-ups), which may be desirable in some cases.
9. Next steps
- Add WiX version and Windows SDK tool version checks.
- Add pre-install checks for locked
.dsfiles. - Document install logging, for example
/l*v install.log. - Test elevated installs and multi-user AppData behavior.
- Define uninstall behavior for config.ini.
- Add CI jobs to build and archive both MSI files.