6

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

(64ビットプロセスで)呼び出すときProcess32FirstW、私はいつもTypeLoadExceptionことわざを受け取ります

ProcessEntryオフセット 44 のオブジェクト フィールドが正しく配置されていないか、オブジェクト フィールドではない別のフィールドと重なっているため、型を読み込むことができませんでした。

また、 forchar[]の代わりに使用して、構造体の でand を使用してみました。私は常に568 に設定し、C++ プログラム (64 ビット ビルド) からオフセット データをコピーしました。stringProcessEntry.ExeFilePack=4Pack=8StructLayoutAttributeProcessEntry.Size

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

何がうまくいかないのかわからないので、64 ビット アプリケーションの C# で宣言する方法を教えてください。 C++/CLI を使用する必要がありますか、それとも単にここで何か間違ったことをしているのですか?PROCESSENTRY32W


編集:このコードを 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);
4

3 に答える 3

5

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 バイトです。つまり、 32 ビットと 64 ビットのどちらを実行しているかによって、 FieldOffsetAttributeforParentProcessIDExeFileのオフセットが異なります。数学を見ると、常に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; 
 }; 
于 2015-11-10T15:07:51.677 に答える
4

はい、これは機能しません。を使用すると、マネージドLayoutKind.Explicit構造体の構造レイアウトは指定したものになります。構造体の管理されていないバージョンと同様。ただし、この特定のケースでは、.NET メモリ モデルに違反しています。これは、ProcessEntry.ExeFile などのオブジェクト参照が常にアトミックであることを示しています。

原子性は、変数が適切に配置されている場合にのみ達成できます。したがって、1 つのメモリ バス サイクルで更新できます。64 ビット モードでは、オブジェクト参照は 8 バイト ポインターであるため、オブジェクト参照を 8 にアラインする必要があります。問題は、44 のオフセットが 8 ではなく 4 にのみ位置合わせされることです。

アンマネージ バージョンの構造体ではまったく問題ありません。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);
于 2015-11-10T15:19:35.453 に答える
0

配置Pack=8 および Charset.Unicode を設定してみてください。

開始フィールド szExeFile は 44 ではなく 40
です。各メンバーの個々のサイズを参照してください。

于 2015-11-10T14:59:26.783 に答える