From 19d845d3275620018d539de100b81cb8eaa9f909 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sat, 1 Aug 2026 22:56:53 +0100 Subject: [PATCH] Refactor Windows MSI build script for improved readability and efficiency - Simplified database interaction by replacing InvokeMember calls with direct method invocations in the build-windows-msi.ps1 script. - Enhanced handling of null values in database records to ensure proper insertion and management of data. - Updated SQL commands to use null for optional fields, improving clarity and consistency in the database schema. --- scripts/build-windows-msi.ps1 | 217 ++++++---------------------------- 1 file changed, 37 insertions(+), 180 deletions(-) diff --git a/scripts/build-windows-msi.ps1 b/scripts/build-windows-msi.ps1 index 561f101..fd79366 100644 --- a/scripts/build-windows-msi.ps1 +++ b/scripts/build-windows-msi.ps1 @@ -16,8 +16,6 @@ param( $ErrorActionPreference = "Stop" # Windows Installer OpenDatabase modes (winuser.h / MSI API). -$msiOpenDatabaseModeReadOnly = 0 -$msiOpenDatabaseModeTransact = 1 $msiOpenDatabaseModeCreate = 3 function Get-MsiVersion { @@ -37,13 +35,7 @@ function Invoke-InstallerExecute { [string]$Sql ) - $null = $Database.GetType().InvokeMember( - "Execute", - "InvokeMethod", - $null, - $Database, - @($Sql) - ) + $null = $Database.Execute($Sql) } function Invoke-InstallerViewInsert { @@ -54,45 +46,23 @@ function Invoke-InstallerViewInsert { [object[]]$Values ) - $view = $Database.GetType().InvokeMember( - "OpenView", - "InvokeMethod", - $null, - $Database, - @($Sql) - ) - - $record = $Installer.GetType().InvokeMember( - "CreateRecord", - "InvokeMethod", - $null, - $Installer, - @($Values.Count) - ) + $view = $Database.OpenView($Sql) + $record = $Installer.CreateRecord($Values.Count) for ($index = 0; $index -lt $Values.Count; $index++) { $value = $Values[$index] - if ($value -is [int]) { - $record.GetType().InvokeMember( - "IntegerData", - "SetProperty", - $null, - $record, - @($index + 1, $value) - ) + $field = $index + 1 + if ($null -eq $value) { + $record.SetNull($field) + } elseif ($value -is [int]) { + $record.IntegerData($field) = $value } else { - $record.GetType().InvokeMember( - "StringData", - "SetProperty", - $null, - $record, - @($index + 1, [string]$value) - ) + $record.StringData($field) = [string]$value } } - $view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, @($record)) - $view.GetType().InvokeMember("Close", "InvokeMethod", $null, $view, $null) + $view.Execute($record) + $view.Close() } function Set-InstallerBinaryStream { @@ -103,48 +73,18 @@ function Set-InstallerBinaryStream { [string]$FilePath ) - $deleteSql = "DELETE FROM `Binary` WHERE `Name` = '$Name'" try { - Invoke-InstallerExecute -Database $Database -Sql $deleteSql + Invoke-InstallerExecute -Database $Database -Sql "DELETE FROM `Binary` WHERE `Name` = '$Name'" } catch { # Ignore when the Binary table is empty on first insert. } - $insertSql = "INSERT INTO `Binary` (`Name`, `Data`) VALUES (?, ?)" - $view = $Database.GetType().InvokeMember( - "OpenView", - "InvokeMethod", - $null, - $Database, - @($insertSql) - ) - - $record = $Installer.GetType().InvokeMember( - "CreateRecord", - "InvokeMethod", - $null, - $Installer, - @(2) - ) - - $record.GetType().InvokeMember( - "StringData", - "SetProperty", - $null, - $record, - @(1, $Name) - ) - - $record.GetType().InvokeMember( - "SetStream", - "InvokeMethod", - $null, - $record, - @(2, (Resolve-Path -LiteralPath $FilePath).Path) - ) - - $view.GetType().InvokeMember("Execute", "InvokeMethod", $null, $view, @($record)) - $view.GetType().InvokeMember("Close", "InvokeMethod", $null, $view, $null) + $view = $Database.OpenView("INSERT INTO `Binary` (`Name`, `Data`) VALUES (?, ?)") + $record = $Installer.CreateRecord(2) + $record.StringData(1) = $Name + $record.SetStream(2, (Resolve-Path -LiteralPath $FilePath).Path) + $view.Execute($record) + $view.Close() } function New-WrapperMsiDatabase { @@ -167,7 +107,6 @@ function New-WrapperMsiDatabase { } $installer = New-Object -ComObject WindowsInstaller.Installer - # Mode 3 (create) is required for new databases; mode 1 (transact) only opens existing MSIs. $database = $installer.OpenDatabase($outputPath, $msiOpenDatabaseModeCreate) Invoke-InstallerExecute -Database $database -Sql @" @@ -275,7 +214,7 @@ CREATE TABLE `InstallExecuteSequence` ( Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `Directory` (`Directory`, `Directory_Parent`, `DefaultDir`) VALUES (?, ?, ?)" ` - -Values @("TARGETDIR", "", "SourceDir") + -Values @("TARGETDIR", $null, "SourceDir") Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `Directory` (`Directory`, `Directory_Parent`, `DefaultDir`) VALUES (?, ?, ?)" ` @@ -292,7 +231,7 @@ CREATE TABLE `InstallExecuteSequence` ( Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `Feature` (`Feature`, `Feature_Parent`, `Title`, `Description`, `Display`, `Level`, `Directory_`) VALUES (?, ?, ?, ?, ?, ?, ?)" ` - -Values @("EmptyFeature", "", "Empty", "", 0, 0, "") + -Values @("EmptyFeature", $null, "Empty", $null, 0, 0, $null) Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `FeatureComponents` (`Feature_`, `Component_`) VALUES (?, ?)" ` @@ -309,119 +248,37 @@ CREATE TABLE `InstallExecuteSequence` ( Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES (?, ?, ?)" ` - -Values @("InstallValidate", "", 1400) + -Values @("InstallValidate", $null, 1400) Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES (?, ?, ?)" ` - -Values @("InstallInitialize", "", 1500) + -Values @("InstallInitialize", $null, 1500) Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES (?, ?, ?)" ` - -Values @("ProcessComponents", "", 1600) + -Values @("ProcessComponents", $null, 1600) Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES (?, ?, ?)" ` - -Values @("RunInstaller", "", 1601) + -Values @("RunInstaller", $null, 1601) Invoke-InstallerViewInsert -Installer $installer -Database $database ` -Sql "INSERT INTO `InstallExecuteSequence` (`Action`, `Condition`, `Sequence`) VALUES (?, ?, ?)" ` - -Values @("InstallFinalize", "", 6600) + -Values @("InstallFinalize", $null, 6600) - $summary = $database.GetType().InvokeMember( - "GetSummaryInformation", - "InvokeMethod", - $null, - $database, - @(20) - ) + $summary = $database.GetSummaryInformation(20) + $summary.Property(1) = ";\1033" + $summary.Property(2) = "Farm Control Server" + $summary.Property(3) = [Guid]::NewGuid().ToString().ToUpper() + $summary.Property(4) = "Farm Control Server" + $summary.Property(7) = "x64;1033" + $summary.Property(9) = "Farm Control Server" + $summary.Property(14) = "200" + $summary.Property(15) = "2" + $summary.Property(19) = "2" + $summary.Persist() - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(1, ";\1033") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(2, "Farm Control Server") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(3, [Guid]::NewGuid().ToString().ToUpper()) - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(4, "Farm Control Server") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(7, "x64;1033") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(9, "Farm Control Server") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(14, "200") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(15, "2") - ) - - $summary.GetType().InvokeMember( - "Property", - "SetProperty", - $null, - $summary, - @(19, "2") - ) - - $summary.GetType().InvokeMember( - "Persist", - "InvokeMethod", - $null, - $summary, - $null - ) - - $database.GetType().InvokeMember( - "Commit", - "InvokeMethod", - $null, - $database, - $null - ) + $database.Commit() [System.Runtime.InteropServices.Marshal]::ReleaseComObject($summary) | Out-Null [System.Runtime.InteropServices.Marshal]::ReleaseComObject($database) | Out-Null