Reputation: 23
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
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
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