Home > Software engineering >  Powershell - "InvokeMember" with "5" argument(s) Error
Powershell - "InvokeMember" with "5" argument(s) Error

Time:09-08

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 the WindowsInstaller.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 with New-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}
  • Related