14

Our end deliverable has lot of MSI files.

I would ensure whether they has correct product name and product version.

I am using Orca and doing it manually.

How to do it using PowerShell?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Samselvaprabu
  • 16,830
  • 32
  • 144
  • 230

3 Answers3

30

This should have been an easy answer... To start with Windows Installer has a COM object you can use:

ProgID: WindowsInstaller.Installer

However when you create an object out of with PowerShell you don't get any of the properties or methods:

$object = New-Object -Com WindowsInstaller.Installer
$object | gm

...Nothing :-(

Apparently this is a problem with PowerShell and its type adapting system. See this blog post for a work around.

http://www.snowland.se/2010/02/21/read-msi-information-with-powershell/

If you use VBScript you shouldn't have this problem.

EDIT:

Here's some VBScript that will get the version I found:

Const msiOpenDatabaseModeReadOnly = 0
Dim msi, db, view

Set msi = CreateObject("WindowsInstaller.Installer")
Set db = msi.OpenDataBase("C:\Users\andy\Desktop\Module.msi", msiOpenDatabaseModeReadOnly)
Set view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'")
Call view.Execute()

GetVersion = view.Fetch().StringData(1)
Wscript.Echo GetVersion

You can call this from PowerShell:

$version = & cscript.exe /nologo GetVersion.vbs

Update! This type adaption problem was frustrating me and I wasn't happy with the VBS solution. After a bit of research I found a way to do this in PowerShell proper. I adapted code from his blog entry. Enjoy!

function Get-MsiDatabaseVersion {
    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)

        $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)

        return $productVersion

    } catch {
        throw "Failed to get MSI file version the error was: {0}." -f $_
    }
}

Get-MsiDatabaseVersion "Installer.msi"
Broam
  • 4,602
  • 1
  • 23
  • 38
Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • Thanks for the comment on my answer, I removed it based on your info. – JNK Jan 05 '12 at 14:29
  • Does not work for me, I get an error: Exception calling "InvokeMethod" with "3" argument(s): "Exception calling "InvokeMember" with "5" argument(s): "OpenDatabase,DatabasePath,OpenMode"" – LeBleu Mar 21 '12 at 18:50
  • The code contained 'smart quotes' which may have caught some people out, you will have seen the error @LeBleu had, I have updated the code snippet. – David Martin Oct 18 '12 at 10:08
  • I am using PS version 2 on Win 7, and "$object | gm" returns `TypeName: System.__ComObject` and more. – Sabuncu Jul 05 '13 at 20:43
  • 1
    @Sabuncu what I meant by nothing is returned is that none of the actual properties or methods of the object are returned. Only the generic com object ones e.g. `CreateObjRef`, `Equals`, `GetHashCode` etc... `OpenDatabase` should be listed by `Get-Member` but it's not. That's the issue. – Andy Arismendi Jul 05 '13 at 21:01
  • +1 Thank you for that lightning-fast clarification! Indeed, that is what I am seeing. – Sabuncu Jul 05 '13 at 21:09
  • 1
    @LeBleu, I had similar issues. Using absolute paths (Resolve-Path to the rescue) fixed the "OpenDatabase,DatabasePath,OpenMode" issue. – murgo Jun 01 '15 at 11:57
  • @AndyArismendi Is it possible to return the Product version as [Version] object? Because I am trying to convert the result of this function to [Version]InstallingVersion = Get-MsiDatabaseVersion "C:\Installer.msi" but it fails as "Cannot convert the "System.Object[]" value of type "System.Object[]" to type "System.Version"." – Samselvaprabu Jun 29 '16 at 10:40
  • Resolve-Path made this fail when providing a network share location. Removing that bit makes it work for that scenario. Great command. – Cody Konior Jul 07 '22 at 08:48
3

(Sorry, don't have rep to just add a comment to the accepted answer)

The answer from @davidmartin was super helpful for me.

Only bit I needed to add was to close the object before returning the version otherwise a handle is kept open on the msi and later access can fail:

$View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)

return $productVersion
sme
  • 101
  • 1
  • 1
1

The PowerShell module "Carbon" is awesome for doing exactly this.

I discovered it myself a few years ago while working a lot with PowerShell Desired State Configuration (DSC), which requires the Product Code (a guid) of the MSI to install. It's possible to get the product code with PowerShell natively if the application is installed on a computer, same with the version number, but Carbon can do it from the MSI.

http://get-carbon.org/

Import-Module -Name Carbon
Get-MSI -Path C:\PathToMSI
Oskar
  • 11
  • 1