前書き
Windows XPから導入されたTool Help Functionsを利用してプロセス、スレッド、モジュール、ヒープリストのスナップショットを作成するクラスのコードです。
ソースコード
// Potisan.Windows.ToolHelp32.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; namespace Potisan.Windows.ToolHelp32 { public sealed class ToolHelp32SnapshotHandle : SafeHandle { [Flags] public enum Flags : uint { Inheritable = 0x80000000, SnapHeapList = 0x00000001, SnapProcess = 0x00000002, SnapThread = 0x00000004, SnapModule = 0x00000008, SnapModule32 = 0x00000010, SnapAll = SnapHeapList | SnapModule | SnapProcess | SnapThread, } private ToolHelp32SnapshotHandle() : base(IntPtr.Zero, true) { } public static ToolHelp32SnapshotHandle Snap(Flags flags, uint processId) { var handle = NativeMethods.CreateToolhelp32Snapshot((uint)flags, processId); if (handle.IsInvalid) throw new Win32Exception(); return handle; } public static ToolHelp32SnapshotHandle SnapAll(bool includesModule32, bool inheritable = false) => Snap(Flags.SnapAll | (includesModule32 ? Flags.SnapModule32 : 0), 0); public static ToolHelp32SnapshotHandle SnapProcess(bool inheritable = false) => Snap(Flags.SnapProcess | (inheritable ? Flags.Inheritable : 0), 0); public static ToolHelp32SnapshotHandle SnapThreads(uint processId, bool inheritable = false) => Snap(Flags.SnapThread | (inheritable ? Flags.Inheritable : 0), processId); public static ToolHelp32SnapshotHandle SnapHeapLists(uint processId, bool inheritable = false) => Snap(Flags.SnapHeapList | (inheritable ? Flags.Inheritable : 0), processId); public static ToolHelp32SnapshotHandle SnapModules(uint processId, bool includesModule32, bool inheritable = false) => Snap(Flags.SnapModule | (inheritable ? Flags.Inheritable : 0) | (includesModule32 ? Flags.SnapModule32 : 0), processId); public override bool IsInvalid => handle == default; protected override bool ReleaseHandle() => NativeMethods.CloseHandle(handle); private static class NativeMethods { [DllImport("Kernel32.dll", SetLastError = true)] public extern static ToolHelp32SnapshotHandle CreateToolhelp32Snapshot( uint flags, uint processId); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool CloseHandle(IntPtr handle); } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct ToolHelp32ProcessInfo { public uint Size; public uint UsageCount; public uint ProcessID; public UIntPtr DefaultHeapID; public uint ModuleID; public uint ThreadCount; public uint ParentProcessID; public int PriorityClassBase; public uint Flags; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string ExeFileName; } public sealed class ToolHelp32ProcessInfos : IEnumerable<ToolHelp32ProcessInfo> { private ToolHelp32SnapshotHandle handle; public ToolHelp32ProcessInfos(ToolHelp32SnapshotHandle handle) => this.handle = handle; public ToolHelp32SnapshotHandle SnapshotHandle => handle; public IEnumerator<ToolHelp32ProcessInfo> GetEnumerator() => new ToolHelp32ProcessInfoEnumerator(handle); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => new ToolHelp32ProcessInfoEnumerator(handle); } public sealed class ToolHelp32ProcessInfoEnumerator : IEnumerator<ToolHelp32ProcessInfo> { private ToolHelp32SnapshotHandle handle; private ToolHelp32ProcessInfo info; private ToolHelp32ProcessInfoEnumerator(ToolHelp32SnapshotHandle handle) { this.handle = handle; Reset(); } public ToolHelp32SnapshotHandle SnapshotHandle => handle; public ToolHelp32ProcessInfo Current => info; public void Dispose() { } object System.Collections.IEnumerator.Current => info; public bool MoveNext() { if (!NativeMethods.Process32Next(handle, ref info)) { if (Marshal.GetLastWin32Error() != ERROR_NO_MORE_FILES) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } return false; } return true; } public void Reset() { info.Size = (uint)Marshal.SizeOf<ToolHelp32ProcessInfo>(); if (!NativeMethods.Process32First(handle, ref info)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } private const int ERROR_NO_MORE_FILES = 18; private static class NativeMethods { [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool Process32First( ToolHelp32SnapshotHandle handle, ref ToolHelp32ProcessInfo info); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool Process32Next( ToolHelp32SnapshotHandle handle, ref ToolHelp32ProcessInfo info); } } }
ToolHelp32ProcessInfo.cs
using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; namespace Potisan.Windows.ToolHelp32 { public sealed class ToolHelp32SnapshotHandle : SafeHandle { [Flags] public enum Flags : uint { Inheritable = 0x80000000, SnapHeapList = 0x00000001, SnapProcess = 0x00000002, SnapThread = 0x00000004, SnapModule = 0x00000008, SnapModule32 = 0x00000010, SnapAll = SnapHeapList | SnapModule | SnapProcess | SnapThread, } private ToolHelp32SnapshotHandle() : base(IntPtr.Zero, true) { } public static ToolHelp32SnapshotHandle Snap(Flags flags, uint processId) { var handle = NativeMethods.CreateToolhelp32Snapshot((uint)flags, processId); if (handle.IsInvalid) throw new Win32Exception(); return handle; } public static ToolHelp32SnapshotHandle SnapAll(bool includesModule32, bool inheritable = false) => Snap(Flags.SnapAll | (includesModule32 ? Flags.SnapModule32 : 0), 0); public static ToolHelp32SnapshotHandle SnapProcess(bool inheritable = false) => Snap(Flags.SnapProcess | (inheritable ? Flags.Inheritable : 0), 0); public static ToolHelp32SnapshotHandle SnapThreads(uint processId, bool inheritable = false) => Snap(Flags.SnapThread | (inheritable ? Flags.Inheritable : 0), processId); public static ToolHelp32SnapshotHandle SnapHeapLists(uint processId, bool inheritable = false) => Snap(Flags.SnapHeapList | (inheritable ? Flags.Inheritable : 0), processId); public static ToolHelp32SnapshotHandle SnapModules(uint processId, bool includesModule32, bool inheritable = false) => Snap(Flags.SnapModule | (inheritable ? Flags.Inheritable : 0) | (includesModule32 ? Flags.SnapModule32 : 0), processId); public override bool IsInvalid => handle == default; protected override bool ReleaseHandle() => NativeMethods.CloseHandle(handle); private static class NativeMethods { [DllImport("Kernel32.dll", SetLastError = true)] public extern static ToolHelp32SnapshotHandle CreateToolhelp32Snapshot( uint flags, uint processId); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool CloseHandle(IntPtr handle); } } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] public struct ToolHelp32ProcessInfo { public uint Size; public uint UsageCount; public uint ProcessID; public UIntPtr DefaultHeapID; public uint ModuleID; public uint ThreadCount; public uint ParentProcessID; public int PriorityClassBase; public uint Flags; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string ExeFileName; } public sealed class ToolHelp32ProcessInfos : IEnumerable<ToolHelp32ProcessInfo> { private ToolHelp32SnapshotHandle handle; public ToolHelp32ProcessInfos(ToolHelp32SnapshotHandle handle) => this.handle = handle; public ToolHelp32SnapshotHandle SnapshotHandle => handle; public IEnumerator<ToolHelp32ProcessInfo> GetEnumerator() => new ToolHelp32ProcessInfoEnumerator(handle); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => new ToolHelp32ProcessInfoEnumerator(handle); } public sealed class ToolHelp32ProcessInfoEnumerator : IEnumerator<ToolHelp32ProcessInfo> { private ToolHelp32SnapshotHandle handle; private ToolHelp32ProcessInfo info; internal ToolHelp32ProcessInfoEnumerator(ToolHelp32SnapshotHandle handle) { this.handle = handle; Reset(); } public ToolHelp32SnapshotHandle SnapshotHandle => handle; public ToolHelp32ProcessInfo Current => info; public void Dispose() { } object System.Collections.IEnumerator.Current => info; public bool MoveNext() { if (!NativeMethods.Process32Next(handle, ref info)) { if (Marshal.GetLastWin32Error() != ERROR_NO_MORE_FILES) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } return false; } return true; } public void Reset() { info.Size = (uint)Marshal.SizeOf<ToolHelp32ProcessInfo>(); if (!NativeMethods.Process32First(handle, ref info)) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } private const int ERROR_NO_MORE_FILES = 18; private static class NativeMethods { [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool Process32First( ToolHelp32SnapshotHandle handle, ref ToolHelp32ProcessInfo info); [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] public extern static bool Process32Next( ToolHelp32SnapshotHandle handle, ref ToolHelp32ProcessInfo info); } } }
サンプルコード
プロセスIDの列挙
// Program.cs using System; using Potisan.Text.Format; using Potisan.Windows.ToolHelp32; var formatter = new FieldArrayFormatter(); using (var snapshot = ToolHelp32SnapshotHandle.SnapProcess()) { foreach (var processInfo in new ToolHelp32ProcessInfos(snapshot)) { Console.WriteLine(formatter.Format(null, processInfo, null)); } }
// Potisan.Text.FieldArrayFormatter.cs using System; using System.Text; namespace Potisan.Text.Format { public sealed class FieldArrayFormatter : IFormatProvider, ICustomFormatter { public object GetFormat(Type formatType) => (formatType == typeof(ICustomFormatter)) ? this : null; public string Format(string format, object arg, IFormatProvider formatProvider) { var props = arg.GetType().GetFields(); var builder = new StringBuilder(); builder.Append("{"); foreach (var prop in props) { builder.Append(prop.Name); builder.Append(": "); builder.Append(prop.GetValue(arg).ToString()); builder.Append(", "); } builder.Remove(builder.Length - ", ".Length, ", ".Length); builder.Append("}"); return builder.ToString(); } } }
2021/3/10:この記事は別のブログで投稿した記事を移動したものです。