zespri's answerに基づいて、更新されたメソッドを書きました。
このコードは、16ビットよりも長いプロセスIDを処理できます。
また、1つのロジックバグが修正され、メモリとリークが処理されました。フェイルセーフを追加しました。
conhost.exeに複数の関連プロセスがある場合のためのメソッドを追加しました。これは、実行中のコンソールプログラムがあり、cmdを持っている場合に発生します。exeを親プロセスとしてだけでなく、関連するプロセスが子 - 親関係にさえもない他のいくつかのケースでも使用できます。
ありがとうございましたzespri元のコードは、そこから学ぶことがたくさんあります!方法更新の
詳細説明:
WinXPのためにSYSTEM_HANDLE_INFORMATIONのみ16ビット長のプロセスID-Sを返すためSYSTEM_EXTENDED_HANDLE_INFORMATIONを使用する方がよいです+。システムにハンドルが多量にロードされている場合、プロセスid-sは65kを超える値、たとえば8進数の8桁を持つ傾向があります。上記のコードが使用するシステムコールは、単にプロセスid-sの上位ビットをマスクします。プロセスハッカーのソースコードでSYSTEM_HANDLE_TABLE_ENTRY_INFO_EXとその使用法を見つけることができます。
void Main()
{
//System.Diagnostics.Process.EnterDebugMode(); //TODO: is this necessary?
int? ConsoleHost_PId = NativeMethods.GetConhostIdByProcessId(14412376);
ConsoleHost_PId.Dump();
int pid = 4484;
int? apid = NativeMethods.GetFirstConhostAssociatedProcessId(pid);
apid.Dump();
var apids = NativeMethods.GetConhostAssociatedProcessIds(pid);
apids.Dump();
}
public static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImportAttribute("kernel32.dll", SetLastError = true)]
public static extern uint GetProcessId([In]IntPtr process);
[DllImport("ntdll.dll")]
public static extern uint NtQueryObject(IntPtr objectHandle,
int objectInformationClass, IntPtr objectInformation, int objectInformationLength,
ref int returnLength);
[DllImport("ntdll.dll")]
public static extern uint NtQuerySystemInformation(int
systemInformationClass, IntPtr systemInformation, int systemInformationLength,
ref int returnLength);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
public enum ObjectInformationClass
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VmOperation = 0x00000008,
VmRead = 0x00000010,
VmWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_BASIC_INFORMATION
{
public int Attributes;
public int GrantedAccess;
public int HandleCount;
public int PointerCount;
public int PagedPoolUsage;
public int NonPagedPoolUsage;
public int Reserved1;
public int Reserved2;
public int Reserved3;
public int NameInformationLength;
public int TypeInformationLength;
public int SecurityDescriptorLength;
public System.Runtime.InteropServices.ComTypes.FILETIME CreateTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct OBJECT_TYPE_INFORMATION
{
public UNICODE_STRING Name;
public int ObjectCount;
public int HandleCount;
public int Reserved1;
public int Reserved2;
public int Reserved3;
public int Reserved4;
public int PeakObjectCount;
public int PeakHandleCount;
public int Reserved5;
public int Reserved6;
public int Reserved7;
public int Reserved8;
public int InvalidAttributes;
public GENERIC_MAPPING GenericMapping;
public int ValidAccess;
public byte Unknown;
public byte MaintainHandleDatabase;
public int PoolType;
public int PagedPoolUsage;
public int NonPagedPoolUsage;
}
[StructLayout(LayoutKind.Sequential)] //, Pack = 1)] //NB! no packing!
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct GENERIC_MAPPING
{
public int GenericRead;
public int GenericWrite;
public int GenericExecute;
public int GenericAll;
}
[StructLayout(LayoutKind.Sequential)] //, Pack = 1)] //NB! no packing!
public struct SYSTEM_HANDLE_INFORMATION
{
public ushort UniqueProcessId;
public ushort CreatorBackTraceIndex;
public byte ObjectTypeIndex;
public byte HandleAttributes; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
public ushort HandleValue;
public UIntPtr Object;
public uint GrantedAccess;
}
//adapted from ProcessExplorer ntexapi.h
[StructLayout(LayoutKind.Sequential)] //, Pack = 1)] //NB! no packing!
public struct SYSTEM_HANDLE_INFORMATION_EX
{
public UIntPtr Object;
public UIntPtr UniqueProcessId; //changed ulong to IntPtr
public UIntPtr HandleValue; //changed ulong to IntPtr
public uint GrantedAccess;
public ushort CreatorBackTraceIndex;
public ushort ObjectTypeIndex;
public uint HandleAttributes;
public uint Reserved;
}
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
public const int DUPLICATE_SAME_ACCESS = 0x2;
// ############################################################################
/// <summary>
/// Some console host processes have multiple associated processes!
/// </summary>
public static List<int> GetConhostAssociatedProcessIds(int pid)
{
List<int> result = new List<int>();
IntPtr currentProcess = GetCurrentProcess();
IntPtr processHandle = OpenProcess(ProcessAccessFlags.DupHandle, false, pid);
try
{
List<SYSTEM_HANDLE_INFORMATION_EX> lstHandles = GetHandles(pid);
foreach (SYSTEM_HANDLE_INFORMATION_EX handleInformation in lstHandles)
{
int? id = GetFileDetails(processHandle, handleInformation, currentProcess);
if (id.HasValue)
result.Add(id.Value);
}
return result;
}
finally
{
CloseHandle(processHandle);
}
}
public static int? GetFirstConhostAssociatedProcessId(int pid)
{
IntPtr currentProcess = GetCurrentProcess();
IntPtr processHandle = OpenProcess(ProcessAccessFlags.DupHandle, false, pid);
try
{
List<SYSTEM_HANDLE_INFORMATION_EX> lstHandles = GetHandles(pid);
foreach (SYSTEM_HANDLE_INFORMATION_EX handleInformation in lstHandles)
{
int? id = GetFileDetails(processHandle, handleInformation, currentProcess);
if (id.HasValue)
return id;
}
return null;
}
finally
{
CloseHandle(processHandle);
}
}
public static int? GetConhostIdByProcessId(int processId)
{
IntPtr currentProcess = GetCurrentProcess();
var processes = Process.GetProcessesByName("conhost");
try
{
foreach (Process process in processes) //TODO: check that this process is really system's console host
{
IntPtr processHandle = OpenProcess(ProcessAccessFlags.DupHandle, false, process.Id);
try
{
List<SYSTEM_HANDLE_INFORMATION_EX> lstHandles = GetHandles(process.Id);
foreach (SYSTEM_HANDLE_INFORMATION_EX handleInformation in lstHandles)
{
int? id = GetFileDetails(processHandle, handleInformation, currentProcess);
if (id == processId)
{
return process.Id;
}
}
}
finally
{
CloseHandle(processHandle);
}
} //foreach (Process process in Process.GetProcessesByName("conhost"))
return null;
}
finally
{
foreach (Process process in processes)
process.Dispose();
}
} //public static int? GetConhostIdByProcessId(int processId)
//TODO see this for possible hang under XP 32-bit:
//http://forum.sysinternals.com/handle-name-help-ntqueryobject_topic14435.html
//and https://stackoverflow.com/questions/16127948/hang-on-ntquerysysteminformation-in-winxpx32-but-works-fine-in-win7x64
private static int? GetFileDetails(IntPtr processHandle, SYSTEM_HANDLE_INFORMATION_EX systemHandleInformation,
IntPtr currentProcess)
{
IntPtr ipHandle;
OBJECT_BASIC_INFORMATION objBasic = new OBJECT_BASIC_INFORMATION();
OBJECT_TYPE_INFORMATION objObjectType = new OBJECT_TYPE_INFORMATION();
int nLength = 0;
if (Is64Bits())
{
if (!DuplicateHandle(processHandle, new IntPtr(unchecked((long)systemHandleInformation.HandleValue)), currentProcess,
out ipHandle, 0, false, DUPLICATE_SAME_ACCESS))
{
return null;
}
}
else
{
//failsafety
if ((systemHandleInformation.HandleValue.ToUInt64() >> 32) != 0)
return null;
if (!DuplicateHandle(processHandle, new IntPtr(unchecked((int)systemHandleInformation.HandleValue)), currentProcess,
out ipHandle, 0, false, DUPLICATE_SAME_ACCESS))
{
return null;
}
}
try
{
IntPtr ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
try
{
NtQueryObject(ipHandle, (int)ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
}
finally
{
Marshal.FreeHGlobal(ipBasic);
}
IntPtr ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
try
{
nLength = objBasic.TypeInformationLength;
while (NtQueryObject(ipHandle, (int)ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength) == STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = IntPtr.Zero; //zero the pointer before new alloc for case the alloc fails
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
//IntPtr ipTemp = Is64Bits() ? new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32) : objObjectType.Name.Buffer;
//string strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
string strObjectTypeName = Marshal.PtrToStringUni(objObjectType.Name.Buffer, objObjectType.Name.Length >> 1);
if (strObjectTypeName != "Process")
return null;
}
finally
{
Marshal.FreeHGlobal(ipObjectType);
}
return (int)GetProcessId(ipHandle);
}
finally
{
CloseHandle(ipHandle);
}
} //private static int? GetFileDetails(IntPtr processHandle, SYSTEM_HANDLE_INFORMATION systemHandleInformation, IntPtr currentProcess)
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const int CNST_SYSTEM_EXTENDED_HANDLE_INFORMATION = 64; //from ProcessHacker ntexapi.h
//http://hintdesk.com/c-get-all-handles-of-a-given-process-in-64-bits/
private static List<SYSTEM_HANDLE_INFORMATION_EX> GetHandles(int pid)
{
List<SYSTEM_HANDLE_INFORMATION_EX> lstHandles = new List<SYSTEM_HANDLE_INFORMATION_EX>();
int nHandleInfoSize = 0x10000;
IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize);
int nLength = 0;
IntPtr ipHandle;
if (IsWinXP) //from ProcessHacker. This works under Win XP+
{
try
{
//the structure array may get larger any number of times during our query
while (
(
NtQuerySystemInformation(CNST_SYSTEM_EXTENDED_HANDLE_INFORMATION, ipHandlePointer,
nHandleInfoSize, ref nLength)
)
== STATUS_INFO_LENGTH_MISMATCH
)
{
//TODO: stop loop if buffer size gets large
nHandleInfoSize = nLength;
Marshal.FreeHGlobal(ipHandlePointer);
ipHandlePointer = IntPtr.Zero; //zero the pointer before new alloc for case the alloc fails
ipHandlePointer = Marshal.AllocHGlobal(nLength);
}
long lHandleCount;
if (Is64Bits())
{
lHandleCount = Marshal.ReadInt64(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 16);
}
else
{
lHandleCount = Marshal.ReadInt32(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 8); //changed to 8, tested OK
}
SYSTEM_HANDLE_INFORMATION_EX shHandle_ex;
for (long lIndex = 0; lIndex < lHandleCount; lIndex++)
{
shHandle_ex = new SYSTEM_HANDLE_INFORMATION_EX();
if (Is64Bits())
{
shHandle_ex = (SYSTEM_HANDLE_INFORMATION_EX)Marshal.PtrToStructure(ipHandle, shHandle_ex.GetType());
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle_ex));
}
else
{
shHandle_ex = (SYSTEM_HANDLE_INFORMATION_EX)Marshal.PtrToStructure(ipHandle, shHandle_ex.GetType());
ipHandle = new IntPtr(ipHandle.ToInt32() + Marshal.SizeOf(shHandle_ex));
}
//failsafety
if (shHandle_ex.UniqueProcessId.ToUInt64() > (ulong)int.MaxValue) //TODO: start using ulong pids?
continue;
if ((int)shHandle_ex.UniqueProcessId.ToUInt32() != pid)
continue;
lstHandles.Add(shHandle_ex);
}
}
finally
{
Marshal.FreeHGlobal(ipHandlePointer);
}
return lstHandles;
}
else //if (IsWinXP)
{
try
{
//the structure array may get larger any number of times during our query
while (
(
NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ipHandlePointer,
nHandleInfoSize, ref nLength)
)
== STATUS_INFO_LENGTH_MISMATCH
)
{
//TODO: stop loop if buffer size gets large
nHandleInfoSize = nLength;
Marshal.FreeHGlobal(ipHandlePointer);
ipHandlePointer = IntPtr.Zero; //zero the pointer before new alloc for case the alloc fails
ipHandlePointer = Marshal.AllocHGlobal(nLength);
}
long lHandleCount;
if (Is64Bits())
{
lHandleCount = Marshal.ReadInt64(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8);
}
else
{
lHandleCount = Marshal.ReadInt32(ipHandlePointer);
ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4);
}
SYSTEM_HANDLE_INFORMATION shHandle;
for (long lIndex = 0; lIndex < lHandleCount; lIndex++)
{
shHandle = new SYSTEM_HANDLE_INFORMATION();
if (Is64Bits())
{
shHandle = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle) + 4);
}
else
{
shHandle = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
ipHandle = new IntPtr(ipHandle.ToInt32() + Marshal.SizeOf(shHandle));
}
if (shHandle.UniqueProcessId != pid)
continue;
SYSTEM_HANDLE_INFORMATION_EX shHandle_ex = new SYSTEM_HANDLE_INFORMATION_EX();
shHandle_ex.Object = shHandle.Object;
shHandle_ex.UniqueProcessId = new UIntPtr(shHandle.UniqueProcessId);
shHandle_ex.HandleValue = new UIntPtr(shHandle.HandleValue);
shHandle_ex.GrantedAccess = shHandle.GrantedAccess;
shHandle_ex.CreatorBackTraceIndex = shHandle.CreatorBackTraceIndex;
shHandle_ex.ObjectTypeIndex = shHandle.ObjectTypeIndex;
shHandle_ex.HandleAttributes = shHandle.HandleAttributes;
lstHandles.Add(shHandle_ex);
}
}
finally
{
Marshal.FreeHGlobal(ipHandlePointer);
}
return lstHandles;
} //if (IsWinXP)
} //private static List<SYSTEM_HANDLE_INFORMATION> GetHandles(int pid)
private static bool Is64Bits()
{
return Marshal.SizeOf(typeof(IntPtr)) == 8 ? true : false;
}
public static bool IsWinXP
{
get
{
return (
false
|| (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) //WinXP
|| Environment.OSVersion.Version.Major >= 6 //Vista or 7
);
}
}
}
UPDATE:
私は2014年6月27日に64ビットコードのための重要なバグ修正を追加しました。
UNICODE_STRING構造体のパッキングが間違っていて、コードがそれを幾分かの方法で補正しようとしました。それはWin7では明示されませんでしたが、Win8の下でうまくクラッシュしました。
Pack = 1の他の構造体のパッキングも正しくありませんでしたが、誤って計算されたレイアウトは変更されませんでした。
重要な変更箇所は以下の通りであった。言うことを約イム何の正確さの試合が、私はconhostを読むことができるものと確認
[StructLayout(LayoutKind.Sequential)] //, Pack = 1)] //NB! no packing!
public struct UNICODE_STRING
//IntPtr ipTemp = Is64Bits() ? new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32) : objObjectType.Name.Buffer;
//string strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
string strObjectTypeName = Marshal.PtrToStringUni(objObjectType.Name.Buffer, objObjectType.Name.Length >> 1);
ない100%がactualy窓7でコマンドプロンプトをホストしているそのための任意のProcess.Startで開始します()...私はプロセスが殺された後になぜそれが起きているのだろうと思っています...そしてなぜ地獄はそれがフォルダを削除するのを妨げるでしょうか?もしあなたが本当にあなたの問題であれば、私はハックを作ってプロセスを殺すことを示唆しています。あなたは確かにそれを試す前に他のことを試してみるべきです(タイプミスのために、私のベストをタイプする:P) –
それを追跡するには? –