2015-11-10 2 views
5

Я объявил функцию Process32FirstW и структуру PROCESSENTRY32W так:`PROCESSENTRY32W` в C#?

[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 не мог» t, потому что поле объекта со смещением 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 

Я не могу понять, что происходит не так, так , как объявитьPROCESSENTRY32Wв C# для 64-битного приложения? Должен ли я использовать C++/CLI или я просто делаю что-то неправильно здесь?


EDIT: Выполнение этого кода в виде 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

Ваш структура не соответствует MSDN-х [PROCESSENTRY32] (https://msdn.microsoft.com/en-us/library/windows/desktop/ ms684839 (v = vs.85) .aspx) где угодно. –

+0

+ scott-Chamberlain Почему? dwSize имеет размер 4 байта, как и поле «Размер». Оба они расположены со смещением 0. PID также имеет 4 байта, как и мой, и со смещением 8. Родительский PID также имеет размер 4 байта и, кажется, находится в смещении 32 в соответствии с образцом C++. Я использовал 260 широких символов при смещении 44. Где моя структура отличается от той, что находится в MSDN? – Cubinator73

+0

Вы игнорируете тот факт, что 'ULONG_PTR' имеет разный размер в 32 и 64-битных системах, написал полный ответ. –

ответ

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;, этот член составляет 4 байта на 32-битных системах и 8 байт на 64-битных системах, это означает, что ваш 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; 
}; 
+0

Несмотря на то, что он работает (спасибо :), мне все равно нужно спросить, как я могу игнорировать th32DefaultHeapID, когда на самом деле использую данные смещения из 64-битной программы? – Cubinator73

+0

См. Ответ Ганса, вы действительно не можете из-за модели управляемой памяти. –

0

Попробуйте установить выравнивание Pack=8 and Charset.Unicode.

Начальное поле szExeFile - 40, а не 44.
См. Индивидуальный размер каждого члена.

+1

IMO, этот ответ должен быть либо комментарием, либо более подробным. –

+0

Я уже пробовал 'Pack = 8', и я уже установил' CharSet = Unicode' (все упоминалось в моем вопросе выше). Я также устанавливаю Charset для импорта функции, даже если у нее нет никаких строковых параметров. – Cubinator73

3

Да, это не сработает. Когда вы используете LayoutKind.Explicit, тогда структура структуры управляется struct будет той, которую вы укажете. Также как неуправляемая версия структуры. В этом конкретном случае, однако, это нарушает модель памяти .NET. Что диктует, что ссылки на объекты, такие как ProcessEntry.ExeFile, всегда атомный.

Атомация может быть достигнута только в том случае, если переменная правильно выровнена. Таким образом, он может быть обновлен одним циклом шины памяти. В 64-битном режиме требуется привязка объектной ссылки к 8, поскольку ссылки на объекты являются 8-байтовыми указателями. Проблема в том, что смещение 44 выравнивается только до 4, а не до 8.

Не проблема в неуправляемой версии структуры вообще, элемент ExeFile на самом деле является массивом WCHAR []. Для этого требуется только выравнивание по 2, поэтому нет необходимости набирать блок, чтобы получить элемент на 48.

Вы должны отказаться от LayoutKind.Explicit и использовать LayoutKind.Sequential вместо этого. Легкий peasy, также может заставить вас чувствовать себя хорошо, что ваш код по-прежнему работает правильно в 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); 
+0

Я читал что-то о выравнивании, но я не знал об атомарности. Но при использовании последовательной компоновки поле ExeFile также выравнивается с 4. Не является ли это нарушением? – Cubinator73

+1

Теперь * управляемый * макет структуры больше не совпадает с * неуправляемым * макетом. Управляемая структура имеет член на 48, неуправляемый на уровне 44. Структура больше не * blittable *, но в любом случае она не была в первую очередь из-за строки. –

 Смежные вопросы

  • Нет связанных вопросов^_^