Skip to content

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.ds
  • FreeImage.dll
  • TWAIN_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 msi32 and msi64.
  • Support build.bat msi32 and build.bat msi64.
  • Keep no-argument build.bat behavior: build, install to C:\Windows, and package both MSI files.
  • Support language configuration via APP_LANGUAGE baked 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_LANGUAGE value.
  • Users do not need to pass a language parameter on the command line.
  • APP_LANGUAGE controls 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_LANGUAGE per language via $(var.AppLanguage);
  • file components: .ds, FreeImage.dll, TWAIN_logo.png;
  • %APPDATA%\bntech directory;
  • conditional config.ini writing;
  • WixUI_InstallDir standard wizard.

6.2 CMake layer

CMakeLists.txt adds:

  • WIX_EXECUTABLE, WIX_UI_EXTENSION, WIX_SOURCE_FILE
  • WINSDK_BIN, MSITRAN, WISUBSTG, WILANGID
  • add_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 .ds files.
  • 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.