`PROCESSENTRY32W` in C #?

I declared the Process32FirstW function and the PROCESSENTRY32W structure as follows:

 [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 message

It is not possible to load the ProcessEntry type because the object field at offset 44 is not aligned correctly or is overlapped by another field that is not an object field.

I also tried using char[] instead of string for ProcessEntry.ExeFile and using Pack=4 and Pack=8 in the StructLayoutAttribute structure. 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 understand what is going wrong, so how to declare PROCESSENTRY32W in C # for a 64-bit application? Should I use C ++ / CLI, or am I just doing something wrong here?


EDIT: Running this code as a 64 bit program works 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); 
+5
source share
3 answers

PROCESSENTRY32 is completely 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 are ignoring ULONG_PTR th32DefaultHeapID; , this member has 4 bytes on 32-bit systems and 8 bytes on 64-bit systems, this means that your FieldOffsetAttribute for ParentProcessID and ExeFile will have a different offset depending on whether 32 bits work versus 64 bits. Looking at your math, you seemed to assume that it would always be 8 bytes.

The simplest workaround is to implicitly detect offsets and use IntPtr to dynamically display 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; }; 
+4
source

Yes, that will not work. When you use LayoutKind.Explicit , then the structure structure of the managed structure will be the one you specify. Just like an unmanaged version of the structure. In this particular case, however, this violates the .NET memory model. Which dictates that object references, such as ProcessEntry.ExeFile, are always atomic.

Atomization can only be achieved if the variable is correctly aligned. Thus, it can be updated with a single memory bus cycle. In 64-bit mode, the binding of an object link to 8 is required, since object references are 8-byte pointers. The problem is that offset 44 is only aligned to 4, not 8.

Not a problem in an unmanaged version of the structure in general, the ExeFile element is actually a WCHAR [] array. This requires only alignment by 2, so there is no need to type a block to get an element at 48.

You should discard LayoutKind.Explicit and use LayoutKind.Sequential instead. Light peasy can also make you feel good that your code is still working correctly in 32-bit mode.

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

And the check never hurts:

  System.Diagnostics.Debug.Assert(IntPtr.Size == 8 && Marshal.OffsetOf(typeof(ProcessEntry), "ExeFile") == (IntPtr)44); 
+3
source

Try setting alignment to Pack = 8 and Charset.Unicode .

The szExeFile launch field is 40, not 44.
See the individual size of each member.

0
source

Source: https://habr.com/ru/post/1235621/


All Articles