I keep running into an InvokeMember with 5 arguments errors, but I believe it has to do with my path. I want to run this from any computer from any location the script folder is located.
$ScriptDir = Split-Path $MyInvocation.MyCommand.Path
$ScriptName = $MyInvocation.MyCommand.Name
Function Get-MsiDBVersion {
param (
[string] $fn
)
try {
$FullPath = (Resolve-Path $fn).Path
$windowsInstaller = New-Object -com WindowsInstaller.Installer
$database = $windowsInstaller.GetType().InvokeMember(
"OpenDatabase","InvokeMethod", $Null,
$windowsInstaller, @($Fullpath, 0)
)
$q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
$View = $database.GetType().InvokeMember(
"OpenView", "InvokeMethod", $Null, $database, ($q)
)
$View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null) | Out-Null
$record = $View.GetType().InvokeMember(
"Fetch", "InvokeMethod", $Null, $View, $Null
)
$productVersion = $record.GetType().InvokeMember(
"StringData", "GetProperty", $Null, $record, 1
)
$View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null) | Out-Null
return $productVersion
} catch {
throw "Failed to get MSI file version the error was: {0}." -f $_
}
}
$Program = @{
Installer = "$ScriptDir\Program.exe"
Version = (get-item "$ScriptDir\Program.exe").VersionInfo.FileVersion.TrimEnd()
Arguments = "/SILENT /ALLUSERS /NORESTART"
Exe = "C:\Program Files (x86)\Program.exe"
ExeVersion = (Get-ChildItem $RegUninstall32 -ErrorAction SilentlyContinue | where name -like "*program*" | Get-ItemProperty ).DisplayVersion
}
CodePudding user response:
You need to pass a full, file-system-native path to a
.msi
file to theWindowsInstaller.Installer
COM object (Windows Installer COM Automation interface, given that PowerShell's current directory (location) usually differs from that of other in-process environments, namely .NET and the unmanaged processes used by COM.While
$FullPath = (Resolve-Path $fn).Path
is an attempt to get the full path from a potentially relative one, it isn't robust, because the resolved path may be based on a PowerShell-only drive (created withNew-PSDrive
), which the outside world knows nothing about.Instead, use
Convert-Path
-LiteralPath $fn
, which returns a file-system-native path, as known to all environments.In either case, the resolving is based on the current location, as reflected in
$PWD
/Get-Location
.
There is no need to use reflection via
.GetType().InvokeMember()
in order to call the methods and access the properties of instances of the types exposed by the Windows Installer COM object - just call / access them directly, as usual. (In fact, I couldn't get your reflection-based code to work.)
Thus, the following should work:
Function Get-MsiDBVersion {
param (
[string] $LiteralPath
)
try {
# Convert to a full, native path.
$fullPath = Convert-Path -ErrorAction Stop -LiteralPath $LiteralPath
$windowsInstaller = New-Object -ComObject WindowsInstaller.Installer
$database = $windowsInstaller.Opendatabase($fullPath, 0)
$q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
$view = $database.OpenView($q)
$null = $View.Execute()
$record = $View.Fetch()
$productVersion = $record.StringData(1)
$null = $View.Close()
return $productVersion
}
catch {
throw "Failed to get MSI file version; the error was: {0}." -f $_
}
}
CodePudding user response:
I am not sure currently what your goal here is, but if you want to receive information about MSIpackages you can do:
Get-CimInstance -query "select * from win32_product"
Your query won't work - your code:
$q = "SELECT Value FROM Property WHERE Property = 'ProductVersion'"
The syntax have to be:
"Select [Property] from [class] where [property] = [propertyvalue]"
e.g.:
get-ciminstance -query "select version,installState,Description,version,IdentifyingNumber from win32_product where Name = 'Blender'"
output:
Name : Blender
Version : 2.82.1
InstallState : 5
Caption :
Description : Blender
IdentifyingNumber : {EDFAE2A8-E73B-4CD1-9648-46A7E4434BDA}