Search code examples
c#structlayout

`PROCESSENTRY32W` in C#?


I declared the function Process32FirstW and the structure PROCESSENTRY32W like this:

[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;
}

When calling Process32FirstW (with a 64-bit process), I always get a TypeLoadException saying

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.

I also tried using char[] instead of string for ProcessEntry.ExeFile and using Pack=4 and Pack=8 in the structure's StructLayoutAttribute. I always set ProcessEntry.Size to 568 and I copied the offset data from a C++ program (64-bit build):

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

I can't figure out, what is going wrong, so how to declare PROCESSENTRY32W in C# for a 64-bit application? Do I have to use C++/CLI or am I simply doing something wrong here?


EDIT: Running this code as a 64-bit program works perfectly fine for me

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);

Solution

  • PROCESSENTRY32 is fully defined as

    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;
    

    You ignore ULONG_PTR th32DefaultHeapID;, that member is 4 bytes on 32 bit systems and 8 bytes on 64 bit systems, that means your FieldOffsetAttribute for ParentProcessID and ExeFile will have a different offset depending on if you are running 32 bits vs 64 bits. Looking at your math it appears you assumed it would always be 8 bytes.

    The easiest workaround is don't explicitly define the offsets and use IntPtr to dynamicly figure the correct offset.

    [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; 
     };