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
.msifile to theWindowsInstaller.InstallerCOM 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).Pathis 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}
