C#中的`PROCESSENTRY32W`?

时间:2015-11-10 14:27:04

标签: c# structlayout

我声明了函数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=8StructLayoutAttribute。我总是将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);

3 个答案:

答案 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个字节,这意味着FieldOffsetAttributeParentProcessID的{​​{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 查看每个成员的个人规模。