user1912094
user1912094

Reputation: 23

PInvoke GetPackageId failing with error 122

I am trying to PInvoke this function (GetPackageId) from kernel32.dll: http://msdn.microsoft.com/en-us/library/windows/desktop/hh446607(v=vs.85).aspx

I defined the structs and imports as follows:

    [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);

And calling it like this:

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

However I get a return value of 122 (ERROR_INSUFFICIENT_BUFFER). I am rather new to PInvoke and am not quite sure how to proceed from here. Do I need to initialize the strings before calling the function?

Upvotes: 2

Views: 619

Answers (2)

David Heffernan
David Heffernan

Reputation: 613382

You are going to need to change the p/invoke:

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

You call it once passing 0 for the length:

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

Then you need to check that retval equals ERROR_INSUFFICIENT_BUFFER. If it does not then you have an error.

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

Otherwise you can continue.

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

Now you can check retval against ERROR_SUCCESS.

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

And finally we can convert the buffer to a PACKAGE_ID.

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

Put it all together and it looks like this:

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);
}

From the comments it appears that we also need to make changes to the way the PACKAGE_ID struct is marshalled.

I suggest the following:

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

followed by calls to Marshal.PtrToStringUni to convert the IntPtr string fields into C# strings. Naturally this conversion needs to happen before the call to FreeHGlobal.

My guess is that the API actually allocates the string buffers in the space beyond the end of PACKAGE_ID. Which is why you have to ask how much memory to allocate. I don't have Windows 8 at hand to test this hypothesis.

Upvotes: 4

Daniel
Daniel

Reputation: 911

From the docs for GetPackageId it seems you should send the size of the buffer as argument when calling, i.e. bufferLength should be initialized with the size of the passed buffer.

On return the bufferLength will tell you the size of the returned buffer.

Or did misread the docs?

Upvotes: 0

Related Questions