C# 使用pinvoke和低级变量的Powershell

C# 使用pinvoke和低级变量的Powershell,c#,powershell,winapi,pinvoke,C#,Powershell,Winapi,Pinvoke,我正在尝试在PowerShell中创建一个脚本,从位于磁盘某处的.msi包中提取[ProductCode]。我发现我需要使用下面两种方法:和。基于此,我编写了下一个代码片段: $signature_GetProperty = @' [DllImport("msi.dll", CharSet=CharSet.Unicode)] public static extern int MsiGetProperty( int hInstall, string szName, [Out] StringBuil

我正在尝试在PowerShell中创建一个脚本,从位于磁盘某处的.msi包中提取[ProductCode]。我发现我需要使用下面两种方法:和。基于此,我编写了下一个代码片段:

$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", 
我正在努力解决如何声明和使用定义为参数MsiGetProperty和MsiOpenPackageEx的变量

例如,OpenPackage中的最后一个参数是void**hProduct。我应该如何在.ps1脚本中声明它,以便以后在MsiGetProperty函数中使用。ref int pchValueBuf也是如此


对于这样一个蹩脚的问题,我很抱歉,但我非常感谢您对此类问题的任何帮助、澄清或文章。

以下是我们内部使用的一个片段:

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

这不是一个蹩脚的问题:-)您应该能够分别使用
[ref]$ptr
[ref]$int
,其中
$ptr
[intptr]的已创建副本::零
$int
是已实例化的整数。您可能需要将
out
关键字添加到
void**hProduct
parameter@MathiasR.Jessen首先,谢谢你的建议,它让我走上了正确的方向。但现在我面对的是一件让我震惊的事情。[链接]这是最后一个有错误的代码,我不知道如何修复。很抱歉打扰你和我太坚持了。C#中的无效指针需要标记为不安全,我能想到的唯一方法是在dllimport的旁边定义另一个C#方法,然后包装对MsiOpenPackageEx的调用-然后从PowerShellYeah调用C#方法,我将OpenPackage标记为不安全,并添加了
-CompilerParameters$unsafe
。这显然是不够的。我想我理解你的建议,只是我现在没有那么熟练。无论如何,真的非常感谢你的帮助,我会试着把我的想法用在它上面,也许试着实施你的建议。再次感谢您的时间。我认为您应该能够将
pchValueBuf
定义为
out IntPtr
,而不是
void**