rubrapack Manual←↑→

39 Checking your output against Windows

Microsoft does not publish the MSI table encoding, so the reference is Windows itself. These checks need only a Windows machine and the Windows Installer API (msi.dll); no SDK tools.

39.1 The database opens and decodes#

  1. MsiOpenDatabase your file read-only - the compound file and string pool are acceptable.
  2. MsiDatabaseExport every table (and _ForceCodepage, _SummaryInformation) to IDT files.
  3. Compare with what your own reader exports, or with the IDT of a reference database made through msi.dll (MsiOpenDatabase with create mode, MsiDatabaseImport of a _ForceCodepage.idt containing 65001, SQL CREATE TABLE/INSERT via views). Byte-identical IDT means your encoding matches.

39.2 The package installs#

Drive the installer from a program rather than msiexec, so every step is synchronous and returns an error code:

StepAPIExpect
silent UIMsiSetInternalUI(INSTALLUILEVEL_NONE)
verbose logMsiEnableLog (all modes)read it when anything fails
installMsiInstallProduct(path, "")0
namesFindFirstFile on each installed path, compare the entry's own UTF-16 name exactlyequal
contentMsiGetFileHash of each installed file vs your MsiFileHash rowequal
registrationMsiGetProductInfo(ProductCode, InstalledProductName)your ProductName
repairdelete one installed file, MsiReinstallProduct(ProductCode, ...)file back, same name
upgradeinstall version N+1 over Nold ProductCode gone, new one installed
downgradeinstall version N againrefused (1603, the log names your refusing action)
featuresMsiQueryFeatureState; MsiConfigureFeature(..., INSTALLSTATE_LOCAL)levels above INSTALLLEVEL absent until turned on
rollbackcopy of version N+1 with a deferred custom action that fails after InstallFiles (type 34 + 1024, "[SystemFolder]cmd.exe" /c exit 1) and a new package code, installed over N1603 (log: error 1722); N still registered, its files unchanged
uninstallMsiConfigureProduct(ProductCode, ..., INSTALLSTATE_ABSENT)0; folders, keys and registration gone

Per-machine installs need an elevated process. Keep test products on their own UpgradeCodes and remove them in the same run.

39.3 Checks before writing#

msi.dll accepts many broken databases and only fails at install time, if at all (it does not even enforce column widths). A writer should check its own tables first. rubrapack's build refuses to write a package that breaks any of these (diagnostics RP2001-RP2015):

39.4 Microsoft's ICE rules [observed]#

The Windows SDK's "MSI Tools" include MsiVal2 and darice.cub, the Internal Consistency Evaluators (ICEs) that Microsoft's own authoring tools run: MsiVal2 package.msi darice.cub -f prints what fails. They run only on Windows, and they go further than the checks above - they know the meaning of the tables, not only their shape. What they report for rubrapack's test packages (every source of its test suite, 39 packages) since 0.8.0: only ICE52, a private property in AppSearch (RpFound_<ID>, a remembered folder). It stays private on purpose, so the command line cannot pretend a search found something; AppSearch runs in both sequences, so nothing needs passing to the server side.

What 0.8.0 changed to satisfy the others - none of it changes what an installation does:

Three findings were real and are fixed (0.4.3): the finished, cancelled and failed pages were missing from AdminUISequence, so an administrative installation (msiexec /a) with the full UI ended without them (ICE20); a component whose key path is its folder (an INI entry, a [remove]) was not listed in CreateFolder (ICE18); and a package with dialogs had no ControlCondition table, which ICE17 reads for every dialog (it stopped with error 2228 instead of checking).

39.5 Things that look fine but are not#