Reputation: 81
I'm trying to create a script in PowerShell that extracts [ProductCode] from .msi package located somewhere on disk. I found that I need to use next two methods: MsiOpenPackage and MsiGetProperty. Based on that, I wrote next code snippet:
$signature_GetProperty = @'
[DllImport("msi.dll", CharSet=CharSet.Unicode)]
public static extern int MsiGetProperty(
int hInstall,
string szName,
[Out] StringBuilder szValueBuf,
ref int pchValueBuf);
'@
$signature_OpenPackage = @'
[DllImport("msi.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 MsiOpenPackageEx(
string szPackagePath,
UInt32 dwOptions,
void **hProduct);
'@
$OpenPackageType = Add-Type -MemberDefinition $signature_OpenPackage -Name "WinMsiOpenPackageEX" -Namespace Win32Functions -PassThru
$OpenPackageType::MsiOpenPackageEx($path, 1, STRUGGLING HERE)
$GetInfoType = Add-Type -MemberDefinition $signature_GetProperty -Name "WinGetProperty" -Namespace Win32GetProductCodeMSI -Using System.Text -PassThru
$GetInfoType::MsiGetProperty(AND HERE, "ProductCode",
I'm struggling with how should I declare and use variables that are defined as parameters MsiGetProperty and MsiOpenPackageEx.
For instance, last parameter in OpenPackage is void **hProduct. How should I declare it in .ps1 script in order to use later within MsiGetProperty function. The same goes with ref int pchValueBuf.
I'm sorry for such a lame question, but I would really appreciate any help or clarification or article to read about this type of issue.
Upvotes: 0
Views: 1365
Reputation: 1400
Here's a snippet we're using internally:
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.Text;
public static class Msi
{
public static string GetProductVersion(string fileName)
{
IntPtr hInstall = IntPtr.Zero;
try
{
uint num = MsiOpenPackage(fileName, ref hInstall);
if ((ulong)num != 0)
{
throw new Exception("Cannot open database: " + num);
}
int pcchValueBuf = 255;
StringBuilder szValueBuf = new StringBuilder(255);
num = MsiGetProperty(hInstall, "ProductVersion", szValueBuf, ref pcchValueBuf);
if ((ulong)num != 0)
{
throw new Exception("Failed to Get Property ProductVersion: " + num);
}
return szValueBuf.ToString();
}
finally
{
if(hInstall != IntPtr.Zero)
{
MsiCloseHandle(hInstall);
}
}
}
[DllImport("msi.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern int MsiCloseHandle(IntPtr hAny);
[DllImport("msi.dll", CharSet = CharSet.Unicode, EntryPoint = "MsiOpenPackageW", ExactSpelling = true, SetLastError = true)]
private static extern uint MsiOpenPackage(string szDatabasePath, ref IntPtr hProduct);
[DllImport("msi.dll", CharSet = CharSet.Unicode, EntryPoint = "MsiGetPropertyW", ExactSpelling = true, SetLastError = true)]
private static extern uint MsiGetProperty(IntPtr hInstall, string szName, [Out] StringBuilder szValueBuf, ref int pchValueBuf);
}
"@
[Msi]::GetProductVersion("R:\PathTo.msi")
Upvotes: 2