1

我正在尝试从 kernel32.dll 调用此函数(GetPackageId):http: //msdn.microsoft.com/en-us/library/windows/desktop/hh446607 (v=vs.85).aspx

我将结构和导入定义如下:

    [StructLayout(LayoutKind.Sequential)]
    public struct PACKAGE_ID
    {
        uint reserved;
        uint processorArchitecture;
        PACKAGE_VERSION version;
        String name;
        String publisher;
        String resourceId;
        String publisherId;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct PACKAGE_VERSION
    {
        [FieldOffset(0)] public UInt64 Version;
        [FieldOffset(0)] public ushort Revision;
        [FieldOffset(2)] public ushort Build;
        [FieldOffset(4)] public ushort Minor;
        [FieldOffset(6)] public ushort Major;
    }  

    [DllImport("kernel32.dll", EntryPoint = "GetPackageId", SetLastError = true)]
    static extern int GetPackageId(IntPtr hProcess,out uint bufferLength,out PACKAGE_ID pBuffer);

并这样称呼它:

    PACKAGE_ID buffer = new PACKAGE_ID();
    result = GetPackageId(hProcess, out bufferLength, out buffer); 

但是我得到的返回值为 122 (ERROR_INSUFFICIENT_BUFFER)。我对 PInvoke 比较陌生,不太确定如何从这里开始。我需要在调用函数之前初始化字符串吗?

4

2 回答 2

3

您将需要更改 p/invoke:

[DllImport("kernel32.dll", SetLastError=true)]
static extern int GetPackageId(
    IntPtr hProcess,
    ref int bufferLength,
    IntPtr pBuffer
);

您一旦通过0长度就调用它:

int len = 0;
int retval = GetPackageId(hProcess, ref len, IntPtr.Zero);

然后你需要检查retvalequals ERROR_INSUFFICIENT_BUFFER。如果没有,那么你有一个错误。

if (retval != ERROR_INSUFFICIENT_BUFFER)
    throw new Win32Exception();

否则你可以继续。

IntPtr buffer = Marshal.AllocHGlobal(len);
retval = GetPackageId(hProcess, ref len, buffer);

现在您可以retval对照ERROR_SUCCESS.

if (retval != ERROR_SUCCESS)
    throw new Win32Exception();

最后我们可以将缓冲区转换为PACKAGE_ID.

PACKAGE_ID packageID = (PACKAGE_ID)Marshal.PtrToStructure(buffer, 
    typeof(PACKAGE_ID));

把它们放在一起,它看起来像这样:

int len = 0;
int retval = GetPackageId(hProcess, ref len, IntPtr.Zero);
if (retval != ERROR_INSUFFICIENT_BUFFER)
    throw new Win32Exception();
IntPtr buffer = Marshal.AllocHGlobal((int)len);
try
{
    retval = GetPackageId(hProcess, ref len, buffer);
    if (retval != ERROR_SUCCESS)
        throw new Win32Exception();
    PACKAGE_ID packageID = (PACKAGE_ID)Marshal.PtrToStructure(buffer,
        typeof(PACKAGE_ID));
}
finally
{
    Marshal.FreeHGlobal(buffer);
}

从评论看来,我们还需要对PACKAGE_ID结构的编组方式进行更改。

我建议如下:

[StructLayout(LayoutKind.Sequential)]
public struct PACKAGE_ID
{
    uint reserved;
    uint processorArchitecture;
    PACKAGE_VERSION version;
    IntPtr name;
    IntPtr publisher;
    IntPtr resourceId;
    IntPtr publisherId;
}

然后调用Marshal.PtrToStringUniIntPtr字符串字段转换为 C# 字符串。自然,这种转换需要在调用FreeHGlobal.

我的猜测是 API 实际上在PACKAGE_ID. 这就是为什么您必须询问要分配多少内存的原因。我手头没有 Windows 8 来测试这个假设。

于 2013-01-15T12:48:06.917 回答
0

从 GetPackageId 的文档看来,您应该在调用时将缓冲区的大小作为参数发送,即 bufferLength 应该使用传递的缓冲区的大小进行初始化。

返回时,bufferLength 将告诉您返回缓冲区的大小。

还是误读了文档?

于 2013-01-15T09:05:19.513 回答