2015-11-10 24 views
5

oświadczyłem funkcję Process32FirstW i strukturę PROCESSENTRY32W takiego:`PROCESSENTRY32W` w języku 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; 
} 

Dzwoniąc Process32FirstW (z procesu 64-bitowej), zawsze uzyskać TypeLoadException mówiąc

Typ ProcessEntry mogłem” t być załadowany, ponieważ pole obiektu przy przesunięciu 44 jest wyrównane źle lub jest zachodzące na siebie przez inne pole, które nie jest polem obiektu.

Próbowałem też za pomocą char[] zamiast string dla ProcessEntry.ExeFile i korzystania Pack=4 i Pack=8 w strukturze na StructLayoutAttribute. I zawsze ustawione ProcessEntry.Size do 568 i kopiowane dane korekcyjne z programu C++ (64-bitowy 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 

nie mogę dowiedzieć się, co jest nie tak, więc jak deklarowaćPROCESSENTRY32Ww języku C# dla aplikacji 64-bitowej? Czy muszę używać C++/CLI, czy po prostu robię coś nie tak?


EDIT: Running ten kod jako program 64-bitowy działa perfekcyjnie dla mnie

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

Twój struct nie pokrywa się z MSDN w [PROCESSENTRY32] (https://msdn.microsoft.com/en-us/library/windows/desktop/ ms684839 (v = vs.85) .aspx) wszędzie blisko. –

+0

+ scott-chamberlain Dlaczego? dwSize ma rozmiar 4 bajtów, podobnie jak pole Size. Oba znajdują się w punkcie zerowym 0. PID ma również 4 bajty, takie jak moje i w offsecie 8. Macierzysty PID ma również rozmiar 4 bajtów i wydaje się być przesunięty 32 zgodnie z próbką C++. Użyłem 260 szerokich znaków w odsunięciu 44. Gdzie moja struktura różni się od tej w MSDN? – Cubinator73

+0

Ignorujesz fakt, że 'ULONG_PTR' ma inną wielkość w systemach 32- i 64-bitowych, napisał pełną odpowiedź. –

Odpowiedz

4

PROCESSENTRY32 jest w pełni zdefiniowana jako

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; 

zignorować ULONG_PTR th32DefaultHeapID;, że członek jest 4 bajty na systemach 32-bitowych i 8 bajtów na systemach 64 bitowych, co oznacza, że ​​Twój FieldOffsetAttribute dla ParentProcessID i ExeFile będzie miał inny offsetu w zależności od tego, czy korzystasz z 32 bitów, czy 64 bitów. Patrząc na matematykę, wydaje się, że zakładasz, że zawsze będzie to 8 bajtów.

Najprostszym sposobem obejścia tego problemu jest wyraźne zdefiniowanie przesunięć i użycie IntPtr, aby dynamicznie obliczyć poprawne przesunięcie.

[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

Mimo że działa (dziękuję :), wciąż muszę zapytać, jak mogę zignorować th32DefaultHeapID podczas korzystania z danych przesunięcia z programu 64-bitowego? – Cubinator73

+0

Zobacz odpowiedź Hansa, naprawdę nie można z powodu zarządzanego modelu pamięci. –

0

Spróbuj ustawić wyrównanie Pack=8 and Charset.Unicode.

Pole początkowe szExeFile ma wartość 40, a nie 44.
Zobacz indywidualny rozmiar każdego członka.

+1

IMO, ta odpowiedź powinna być albo komentarzem, albo bardziej rozwinięta. –

+0

Próbowałem już 'Pack = 8' i już ustawiłem' CharSet = Unicode' (wszystkie wymienione w powyższym pytaniu). Ustawiam także zestaw znaków do importowania funkcji, nawet jeśli nie ma żadnych parametrów łańcucha. – Cubinator73

3

Tak, to nie działa. Podczas korzystania z LayoutKind.Explicit, struktura struktury zarządzana struct będzie tą, którą określisz. Oprócz niezarządzanej wersji struktury. W tym konkretnym przypadku narusza to jednak model pamięci .NET. Co oznacza, że ​​odwołania do obiektów, takie jak ProcessEntry.ExeFile, są zawsze atomowe.

Atomowość można osiągnąć tylko wtedy, gdy zmienna jest prawidłowo wyrównana. Więc może być aktualizowany za pomocą jednego cyklu szyny pamięci. W trybie 64-bitowym, który wymaga odniesienia obiektu do 8, ponieważ odwołania do obiektów są wskaźnikami 8-bajtowymi. Problem polega na tym, że przesunięcie 44 jest wyrównane tylko do 4, a nie do 8.

W ogóle nie jest problemem w niezarządzanej wersji struktury, członek ExeFile jest w rzeczywistości tablicą WCHAR []. Który wymaga tylko wyrównania do 2, więc nie trzeba pad, aby uzyskać członka na 48.

Musisz zrezygnować z LayoutKind.Explicit i użyć zamiast niego LayoutKind.Sequential. Łatwy w obsłudze, również może sprawić, że poczujesz się dobrze, że twój kod nadal działa poprawnie w trybie 32-bitowym.

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

i czek nie boli:

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

Przeczytałem coś o wyrównaniu, ale nie wiedziałem o atomowości. Ale przy użyciu układu sekwencyjnego pole ExeFile również zostanie wyrównane do 4. Czy to też nie jest naruszenie? – Cubinator73

+1

Teraz * zarządzany * układ struktury nie pasuje już do układu * niezarządzanego *. Zarządzana struktura ma 48-osobową strukturę, niezarządzaną w 44. Struktura nie jest już * blittable *, ale i tak nie była na pierwszym miejscu ze względu na łańcuch. –