Monstryyy
Monstryyy

Reputation: 81

Powershell Using pinvoke and low-level variables

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

Answers (1)

aolszowka
aolszowka

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

Related Questions