PInvoke GetPackageId失败,错误122

时间:2013-01-15 09:01:23

标签: c# pinvoke

我正在尝试从kernel32.dll PInvoke这个函数(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很新,我不太清楚如何从这里开始。在调用函数之前是否需要初始化字符串?

2 个答案:

答案 0 :(得分: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);

然后您需要检查retval是否等于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来测试这个假设。

答案 1 :(得分:0)

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

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

或者误读了文档?