C# 中的“PROCESSENTRY32W”?
`PROCESSENTRY32W` in C#?
我这样声明函数 Process32FirstW
和结构 PROCESSENTRY32W
:
[DllImport("KERNEL32.DLL", CallingConvention = CallingConvention.StdCall, EntryPoint = "Process32FirstW")]
private static extern bool Process32FirstW (IntPtr hSnapshot, ref ProcessEntry pProcessEntry);
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Size = 568)]
internal struct ProcessEntry {
[FieldOffset(0)] public int Size;
[FieldOffset(8)] public int ProcessId;
[FieldOffset(32)] public int ParentProcessID;
[FieldOffset(44), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string ExeFile;
}
当调用 Process32FirstW
(使用 64 位进程)时,我总是得到 TypeLoadException
说
The type ProcessEntry
couldn't be loaded, because the object field at offset 44 is aligned wrong or is overlapped by another field, which isn't an object field.
我还尝试使用 char[]
而不是 string
作为 ProcessEntry.ExeFile
并在结构的 StructLayoutAttribute
中使用 Pack=4
和 Pack=8
。我总是将 ProcessEntry.Size
设置为 568 并且我从 C++ 程序(64 位构建)复制了偏移数据:
typedef unsigned long long ulong;
PROCESSENTRY32W entry;
wcout << sizeof(PROCESSENTRY32W) << endl; // 568
wcout << (ulong)&entry.dwSize - (ulong)&entry << endl; // 0
wcout << (ulong)&entry.th32ProcessID - (ulong)&entry << endl; // 8
wcout << (ulong)&entry.th32ParentProcessID - (ulong)&entry << endl; // 32
wcout << (ulong)&entry.szExeFile - (ulong)&entry << endl; // 44
我不知道出了什么问题,所以 如何在 C# 中为 64 位应用程序声明 PROCESSENTRY32W
? 我必须使用 C++/CLI 还是我只是在这里做错了什么?
编辑: 运行 这段代码作为 64 位程序对我来说工作得很好
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(hSnapshot, &entry)) {
do {
// Do stuff
} while (Process32NextW(hSnapshot, &entry));
}
CloseHandle(hSnapshot);
尝试设置对齐方式 Pack=8 and Charset.Unicode。
开始字段 szExeFile 是 40,而不是 44。
查看每个成员的个人尺寸。
PROCESSENTRY32 完全定义为
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
你忽略了ULONG_PTR th32DefaultHeapID;
,那个成员在32位系统上是4字节,在64位系统上是8字节,也就是说你的FieldOffsetAttribute
对于ParentProcessID
和ExeFile
将有不同的偏移量,具体取决于您是 运行 32 位还是 64 位。看看你的数学,你似乎假设它总是 8 个字节。
最简单的解决方法是不要明确定义偏移量并使用 IntPtr
动态计算正确的偏移量。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] public string szExeFile;
};
是的,这行不通。当您使用 LayoutKind.Explicit
时,managed 结构的结构布局将是您指定的布局。以及结构的非托管版本。然而,在这种特定情况下,这违反了 .NET 内存模型。这表明对象引用,如 ProcessEntry.ExeFile,总是 atomic.
只有变量正确对齐才能实现原子性。所以它可以用一个内存总线周期更新。在 64 位模式下,这需要将对象引用对齐到 8,因为对象引用是 8 字节指针。问题是,偏移量 44 仅与 4 对齐,而不是 8。
在非托管版本的结构中完全没有问题,ExeFile 成员实际上是一个 WCHAR[] 数组。这只需要与 2 对齐,因此无需填充以获取 48 处的成员。
您必须放弃 LayoutKind.Explicit,改用 LayoutKind.Sequential
。简单易行,还可以让您感觉良好,因为您的代码在 32 位模式下仍能正常工作。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct ProcessEntry {
public int Size;
public int Usage;
public int ProcessId;
public IntPtr DefaultHeapId;
public int ModuleId;
public int Threads;
public int ParentProcessID;
public int Priority;
public int Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string ExeFile;
}
而且一张支票永远不会伤害:
System.Diagnostics.Debug.Assert(IntPtr.Size == 8 &&
Marshal.OffsetOf(typeof(ProcessEntry), "ExeFile") == (IntPtr)44);
我这样声明函数 Process32FirstW
和结构 PROCESSENTRY32W
:
[DllImport("KERNEL32.DLL", CallingConvention = CallingConvention.StdCall, EntryPoint = "Process32FirstW")]
private static extern bool Process32FirstW (IntPtr hSnapshot, ref ProcessEntry pProcessEntry);
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Size = 568)]
internal struct ProcessEntry {
[FieldOffset(0)] public int Size;
[FieldOffset(8)] public int ProcessId;
[FieldOffset(32)] public int ParentProcessID;
[FieldOffset(44), MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string ExeFile;
}
当调用 Process32FirstW
(使用 64 位进程)时,我总是得到 TypeLoadException
说
The type
ProcessEntry
couldn't be loaded, because the object field at offset 44 is aligned wrong or is overlapped by another field, which isn't an object field.
我还尝试使用 char[]
而不是 string
作为 ProcessEntry.ExeFile
并在结构的 StructLayoutAttribute
中使用 Pack=4
和 Pack=8
。我总是将 ProcessEntry.Size
设置为 568 并且我从 C++ 程序(64 位构建)复制了偏移数据:
typedef unsigned long long ulong;
PROCESSENTRY32W entry;
wcout << sizeof(PROCESSENTRY32W) << endl; // 568
wcout << (ulong)&entry.dwSize - (ulong)&entry << endl; // 0
wcout << (ulong)&entry.th32ProcessID - (ulong)&entry << endl; // 8
wcout << (ulong)&entry.th32ParentProcessID - (ulong)&entry << endl; // 32
wcout << (ulong)&entry.szExeFile - (ulong)&entry << endl; // 44
我不知道出了什么问题,所以 如何在 C# 中为 64 位应用程序声明 PROCESSENTRY32W
? 我必须使用 C++/CLI 还是我只是在这里做错了什么?
编辑: 运行 这段代码作为 64 位程序对我来说工作得很好
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W entry;
entry.dwSize = sizeof(PROCESSENTRY32W);
if (Process32FirstW(hSnapshot, &entry)) {
do {
// Do stuff
} while (Process32NextW(hSnapshot, &entry));
}
CloseHandle(hSnapshot);
尝试设置对齐方式 Pack=8 and Charset.Unicode。
开始字段 szExeFile 是 40,而不是 44。
查看每个成员的个人尺寸。
PROCESSENTRY32 完全定义为
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
你忽略了ULONG_PTR th32DefaultHeapID;
,那个成员在32位系统上是4字节,在64位系统上是8字节,也就是说你的FieldOffsetAttribute
对于ParentProcessID
和ExeFile
将有不同的偏移量,具体取决于您是 运行 32 位还是 64 位。看看你的数学,你似乎假设它总是 8 个字节。
最简单的解决方法是不要明确定义偏移量并使用 IntPtr
动态计算正确的偏移量。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PROCESSENTRY32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=260)] public string szExeFile;
};
是的,这行不通。当您使用 LayoutKind.Explicit
时,managed 结构的结构布局将是您指定的布局。以及结构的非托管版本。然而,在这种特定情况下,这违反了 .NET 内存模型。这表明对象引用,如 ProcessEntry.ExeFile,总是 atomic.
只有变量正确对齐才能实现原子性。所以它可以用一个内存总线周期更新。在 64 位模式下,这需要将对象引用对齐到 8,因为对象引用是 8 字节指针。问题是,偏移量 44 仅与 4 对齐,而不是 8。
在非托管版本的结构中完全没有问题,ExeFile 成员实际上是一个 WCHAR[] 数组。这只需要与 2 对齐,因此无需填充以获取 48 处的成员。
您必须放弃 LayoutKind.Explicit,改用 LayoutKind.Sequential
。简单易行,还可以让您感觉良好,因为您的代码在 32 位模式下仍能正常工作。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct ProcessEntry {
public int Size;
public int Usage;
public int ProcessId;
public IntPtr DefaultHeapId;
public int ModuleId;
public int Threads;
public int ParentProcessID;
public int Priority;
public int Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string ExeFile;
}
而且一张支票永远不会伤害:
System.Diagnostics.Debug.Assert(IntPtr.Size == 8 &&
Marshal.OffsetOf(typeof(ProcessEntry), "ExeFile") == (IntPtr)44);