我声明了函数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
说
无法加载类型
ProcessEntry
,因为偏移44处的对象字段对齐错误或与另一个字段重叠,而该字段不是对象字段。
我还尝试使用char[]
代替string
ProcessEntry.ExeFile
,并在结构的Pack=4
中使用Pack=8
和StructLayoutAttribute
。我总是将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);
答案 0 :(得分:4)
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
的{{1}}将具有不同的偏移量取决于您是否运行32位与64位。看看你的数学,看起来你假设它总是8个字节。
最简单的解决方法是不明确定义偏移量并使用ExeFile
动态计算正确的偏移量。
IntPtr
答案 1 :(得分:3)
是的,这不行。使用LayoutKind.Explicit
时,托管结构的结构布局将是您指定的结构布局。以及结构的非托管版本。但是在这种特殊情况下违反了.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);
答案 2 :(得分:0)
尝试设置对齐Pack=8 and Charset.Unicode。
开始字段szExeFile是40,而不是44 查看每个成员的个人规模。