我正在尝试从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很新,我不太清楚如何从这里开始。在调用函数之前是否需要初始化字符串?
答案 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.PtrToStringUni
将IntPtr
字符串字段转换为C#字符串。当然,这种转换需要在调用FreeHGlobal
之前进行。
我的猜测是API实际上在PACKAGE_ID
末尾之外的空间中分配了字符串缓冲区。这就是为什么你要问分配多少内存的原因。我手头没有Windows 8来测试这个假设。
答案 1 :(得分:0)
在GetPackageId的文档中,您似乎应该在调用时将缓冲区的大小作为参数发送,即bufferLength应该使用传递的缓冲区的大小进行初始化。
返回时,bufferLength将告诉您返回缓冲区的大小。
或者误读了文档?