C# 32位与64位版本类型的条件编译的首选方法
某个任务需要我枚举系统中的所有句柄。到目前为止,我发现最好的方法是使用文档不足的C# 32位与64位版本类型的条件编译的首选方法,c#,.net,interop,pinvoke,marshalling,C#,.net,Interop,Pinvoke,Marshalling,某个任务需要我枚举系统中的所有句柄。到目前为止,我发现最好的方法是使用文档不足的SystemHandleInformation标志作为class参数 到目前为止还不错。但是,在64位Windows上以32位模式运行时,所需的结构如下: // 32-bit version [StructLayout(LayoutKind.Sequential, Pack=1)] public struct SYSTEM_HANDLE_INFORMATION { public uint ProcessID;
SystemHandleInformation
标志作为class参数
到目前为止还不错。但是,在64位Windows上以32位模式运行时,所需的结构如下:
// 32-bit version
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public uint ProcessID;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public uint Object_Pointer;
public UInt32 GrantedAccess;
}
// 64-bit version
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public int Reserved; // unknown, no documentation found
public uint ProcessID;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public long Object_Pointer;
public UInt32 GrantedAccess;
}
对于64位Windows(x64,我没有测试安腾,我希望不会有什么不同…),其结构如下:
// 32-bit version
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public uint ProcessID;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public uint Object_Pointer;
public UInt32 GrantedAccess;
}
// 64-bit version
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public int Reserved; // unknown, no documentation found
public uint ProcessID;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public long Object_Pointer;
public UInt32 GrantedAccess;
}
现在,我应该将Object\u指针
更改为IntPtr
。我曾一度希望我能用ProcessId
做同样的事情,有一篇参考文章说这实际上是一个句柄,实际上是一个64位的值。但是,Reserved
始终为零,因此我无法以相同的方式将其合并到IntPtr
中
这可能不是发生这种情况的唯一情况。我正在寻找处理此类差异的最佳实践方法:
- 除非我想维护单独的二进制文件,否则在这里使用像WIN32(在IntPtr的参考源代码中内部使用)那样的常量是行不通的
- 如果IntPtr.Size==4,我可以编写两个不同的函数和两个不同的结构,创建一个包装器并在代码中使用
。这适用于外部函数,但不适用于类型
- 我可以重载
GetType
,但我不确定这会导致什么(可能有助于编组?)
- 还有别的吗
所有这些似乎都不理想,但到目前为止,唯一简单的方法似乎是使用if IsWin64()
语句提升我的系统。我希望听到比我更好的方法。鉴于IntPtr的大小不同,为什么不尝试以下方法:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct SYSTEM_HANDLE_INFORMATION
{
public IntPtr ProcessID; // mask with 0xffffffff
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public IntPtr Object_Pointer; // again good for 32/64bit
public UInt32 GrantedAccess;
}
这应该适用于32位和64位不变的情况。就是这样-SystemHandleInformation的结构只提供16位PID。您可以在XP及更高版本上使用SystemExtendedHandle信息
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
public IntPtr Object;
public IntPtr UniqueProcessId;
public IntPtr HandleValue;
public uint GrantedAccess;
public ushort CreatorBackTraceIndex;
public ushort ObjectTypeIndex;
public uint HandleAttributes;
public uint Reserved;
}
internal enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45,
SystemExtendedHandleInformation = 64,
}
[DllImport("ntdll.dll", CharSet=CharSet.Auto)]
private static extern int NtQuerySystemInformation(int InfoType, IntPtr lpStructure, int StructSize, out int returnLength);
public static void Main(string[] args)
{
Console.WriteLine(Environment.Is64BitProcess ? "x64" : "x32");
Console.WriteLine();
var infoSize = Marshal.SizeOf(typeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX));
Console.WriteLine("sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX): {0}", infoSize);
int allSize = 1000 * infoSize;
var buffer = Marshal.AllocHGlobal(allSize);
var status = NtQuerySystemInformation((int)SYSTEM_INFORMATION_CLASS.SystemExtendedHandleInformation, buffer, allSize, out allSize);
Console.WriteLine("status: {0:x}, return len: {1}", status, allSize);
if (status != 0)
{
allSize += 40 * infoSize;
Marshal.FreeHGlobal(buffer);
buffer = Marshal.AllocHGlobal(allSize);
status = NtQuerySystemInformation((int)SYSTEM_INFORMATION_CLASS.SystemExtendedHandleInformation, buffer, allSize, out allSize);
Console.WriteLine("status: {0:x}, return len: {1}", status, allSize);
}
Console.WriteLine();
var info = new SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX();
//for (var i = 0; i < allSize; i += infoSize)
for (var i = 0; i < Math.Min(allSize, 20 * infoSize); i+= infoSize) // for testing purpose only 20
{
Marshal.PtrToStructure(IntPtr.Add(buffer, i), info);
Console.WriteLine("{0,16:x}, {1,16:x}, {2,16:x}, {3,6:x}, {4,8:x}", info.Object.ToInt64(), info.UniqueProcessId.ToInt64(), info.HandleValue.ToInt64(), info.GrantedAccess, info.HandleAttributes);
}
Marshal.FreeHGlobal(buffer);
}
这类代码就像青少年性行为,一个错误,你将支持它到你的余生。使用Process类来运行SysInternals的Handle.exe实用程序,至少您可以让其他人支持它。@HansPassant:LOL!是的,我已经考虑过使用SysInternal的任何实用程序,但是没有,不适合我的场景。谢谢你的指针。是的,我试了几次,但是它产生了错误的ProcessID。也许这是一个好主意,然后我应该用一个方法GetProcessId()
来展开这个结构,该方法进行解蔽并将ProcessID标记为private。@Abel:在64位中,只需右移32位即可。我不确定endian的问题:)它从字节序列00 00 04 00
获取进程ID 4(系统)作为0x000000040000000
,即17179869184
。因此,是的,endianness是一个问题(在这个意义上,前导保留零是一个障碍)<代码>0x000000040000000>>32
可以在64位的情况下工作。这非常好。我问的是一种处理咬人问题的更好方法,但我更具体的问题得到了答案。我不知道扩展版,谢谢!