Java 为什么要启动 conhost.exe?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1313195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is conhost.exe being launched?
提问by ripper234
I'm launching a Java process ("java.exe") from .Net. using Process.Start(). In addition to the Java process, another process called conhost.exeis launched somehow. I am redirecting the output from the Java process to the .Net process.
我正在从 .Net 启动一个 Java 进程(“java.exe”)。使用Process.Start()。除了 Java 进程之外,还有一个名为conhost.exe 的进程以某种方式启动。我正在将 Java 进程的输出重定向到 .Net 进程。
- Why is conhost.exe even launched?
- How do I track it from .Net? I want to track this specific instance, and since I'm not creating it directly (but rather the Java.exe process), I don't have it's PID.
- 为什么 conhost.exe 甚至启动?
- 我如何从 .Net 跟踪它?我想跟踪这个特定的实例,因为我不是直接创建它(而是 Java.exe 进程),所以我没有它的 PID。
采纳答案by Michael
In earlier versions of Windows, console windows were hosted in CSRSS, which is a highly privileged, trusted, system critical process. On Win7, it appears that console windows are now hosted in conhost.exe, which has less rights. This was probably done for security & reliability reasons - a security issue in the console system won't compromise the entire box, and a crash in the console code won't blue screen the system.
在早期版本的 Windows 中,控制台窗口托管在 CSRSS 中,这是一个具有高特权、受信任的系统关键进程。在 Win7 上,控制台窗口现在似乎托管在权限较少的 conhost.exe 中。这样做可能是出于安全和可靠性原因——控制台系统中的安全问题不会危及整个机器,控制台代码中的崩溃也不会使系统蓝屏。
回答by Breakthrough
To be blatant, I don't know anything about Java, so I can't help you with #1. I can help with #2, though.
坦率地说,我对 Java 一无所知,所以我无法帮助您解决 #1。不过,我可以帮助解决 #2。
To track it with .NET, you can use System.Diagnostics.
要使用 .NET 跟踪它,您可以使用 System.Diagnostics。
First, you have to get each of the processes by the name "conhost.exe", launch Java, then get all the processes again, and compare.
首先,您必须通过名称“conhost.exe”获取每个进程,启动 Java,然后再次获取所有进程并进行比较。
To get the specific instances, use the process ID:
要获取特定实例,请使用进程 ID:
foreach (Process singleProcess in Process.GetProcessesByName("conhost"))
{
//Store the following in some kind of array
somePidArray[yourindex] = singleProcess.Id;
}
Then when you want to kill the processes, run the exact same loop, and if the process ID was not stored in the initial loop, then call singleProcess.Kill(); on it. Then you will have kept all the initial conhost.exe processes alive, and only kill the ones created between the time you launch Java in your program and the time your Java process exits.
然后当你想杀死进程时,运行完全相同的循环,如果进程 ID 没有存储在初始循环中,则调用 singleProcess.Kill(); 在上面。然后,您将使所有初始 conhost.exe 进程保持活动状态,并且只终止在您在程序中启动 Java 和 Java 进程退出之间创建的那些进程。
回答by Igal Serban
Update:I guessthat you can find the reasoning on the oldnewthing. It was probably added to restore some functionality (like drag and drop) that was removed from Windows Vista due to security reasons.
更新:我想你可以在 oldnewthing 上找到原因。添加它可能是为了恢复由于安全原因从 Windows Vista 中删除的某些功能(如拖放)。
Before Update:conhost seems to launch on any cmd.exe opening. It's probably some new, undocumented thing on windows 7.
更新之前:conhost 似乎在任何 cmd.exe 打开时启动。这可能是 Windows 7 上的一些新的、未记录的东西。
回答by Filip Navara
It is a process that hosts the console window. It was introduced in Windows 7 (iirc), in older versions the functionality was executed in the context of the csrss.exe process.
它是一个承载控制台窗口的进程。它是在 Windows 7 (iirc) 中引入的,在旧版本中,该功能是在 csrss.exe 进程的上下文中执行的。
回答by erickson
This raises a related question: do you want a console window for the Java application spawned by the .NET app? If not, you can execute the javaw
command instead of java
. I haven't experimented with it on Vista, but it may eliminate the conhost.exe
process.
这引发了一个相关的问题:您是否想要一个由 .NET 应用程序生成的 Java 应用程序的控制台窗口?如果没有,您可以执行javaw
命令而不是java
. 我还没有在 Vista 上试验过它,但它可能会消除这个conhost.exe
过程。
回答by The How-To Geek
I just wrote up an article attempting to explain the purpose of the process. It's geared towards regular people, but there's lots of screenshots to illustrate.
我刚刚写了一篇文章试图解释这个过程的目的。它面向普通人,但有很多截图可以说明。
What is conhost.exe and Why Is It Running?
The bottom line is that conhost.exe sits between the CSRSS process and cmd.exe, so you can use drag & drop again.
最重要的是 conhost.exe 位于 CSRSS 进程和 cmd.exe 之间,因此您可以再次使用拖放。
回答by Andrew Savinykh
Sorry, for necroing such an old thread, but I thought that the question is interesting and worth an answer.
抱歉,我把这么旧的线程弄死了,但我认为这个问题很有趣,值得回答。
Why is conhost.exe even launched?As explained in other posts this is now a default way to host console applications. Further details can be found in the article linked in another answer here: What is conhost.exe and Why Is It Running?
为什么 conhost.exe 甚至启动?正如其他帖子中所述,这现在是托管控制台应用程序的默认方式。可以在此处另一个答案中链接的文章中找到更多详细信息:什么是 conhost.exe 以及它为什么在运行?
How do I track it from .Net? I want to track this specific instance, and since I'm not creating it directly (but rather the Java.exe process), I don't have it's PID.
我如何从 .Net 跟踪它?我想跟踪这个特定的实例,因为我不是直接创建它(而是 Java.exe 进程),所以我没有它的 PID。
As other has noted there should be little reason to "track" the conhost process. Having said that, there is a way to obtain conhost process id from your java.exe process Id. All you have to do is enumerate all the process handles that every conhost process in the system has, and if one of these handles point to a process with the same Id as your jawa.exe, this will be the conhost.exe handle you are after. Covert it to Process Id and you get the PID for conhost.exe
正如其他人所指出的,几乎没有理由“跟踪”conhost 进程。话虽如此,有一种方法可以从您的 java.exe 进程 ID 中获取 conhost 进程 ID。你所要做的就是枚举系统中每个conhost进程拥有的所有进程句柄,如果这些句柄之一指向一个与你的jawa.exe具有相同ID的进程,这将是你所在的conhost.exe句柄后。将其转换为进程 ID,您将获得 conhost.exe 的 PID
So this is theory. How to achieve this in practice? There an excellent articlethat shows some code that is doing something very similar. I have modified this code a bit to suit our task at hand. In the end you Utility.GetConhostIdByProcessId
static function and pass the PID of your java.exe to it, and it will return you the PID of relevant conhost.exe A test call to this method can be found in the Main function in the example below.
所以这是理论。如何在实践中实现这一目标?有一篇优秀的文章展示了一些正在做非常相似的事情的代码。我对这段代码做了一些修改以适应我们手头的任务。最后你Utility.GetConhostIdByProcessId
静态函数并将你的 java.exe 的 PID 传递给它,它会返回你相关 conhost.exe 的 PID 可以在下面示例的 Main 函数中找到对此方法的测试调用。
And now the code:
现在的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace SO1313195
{
class Program
{
static void Main()
{
const int processId = 6980;
int? result = Utility.GetConhostIdByProcessId(processId);
if (result.HasValue)
{
Console.WriteLine("Process {0} has conhost {1}", processId, result.Value);
}
else
{
Console.WriteLine("Unable to find conhost for process {0}", processId);
}
Console.ReadLine();
}
}
public class Win32Api
{
[DllImportAttribute("kernel32.dll", EntryPoint = "GetProcessId")]
public static extern uint GetProcessId([In]IntPtr process);
[DllImport("ntdll.dll")]
public static extern int 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", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(byte[] destination, IntPtr source, uint length);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
ushort hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll")]
public static extern IntPtr GetCurrentProcess();
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)]
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)]
public struct SYSTEM_HANDLE_INFORMATION
{
public int ProcessID;
public byte ObjectTypeNumber;
public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
public ushort Handle;
public int Object_Pointer;
public UInt32 GrantedAccess;
}
public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
public const int DUPLICATE_SAME_ACCESS = 0x2;
}
class Utility
{
public static int? GetConhostIdByProcessId(int processId)
{
foreach (Process process in Process.GetProcessesByName("conhost"))
{
IntPtr processHwnd = Win32Api.OpenProcess(Win32Api.ProcessAccessFlags.DupHandle, false, process.Id);
List<Win32Api.SYSTEM_HANDLE_INFORMATION> lstHandles = GetHandles(process);
foreach (Win32Api.SYSTEM_HANDLE_INFORMATION handle in lstHandles)
{
int? id = GetFileDetails(processHwnd, handle);
if (id == processId)
{
return process.Id;
}
}
}
return null;
}
private static int? GetFileDetails(IntPtr processHwnd, Win32Api.SYSTEM_HANDLE_INFORMATION systemHandleInformation)
{
IntPtr ipHandle;
Win32Api.OBJECT_BASIC_INFORMATION objBasic = new Win32Api.OBJECT_BASIC_INFORMATION();
Win32Api.OBJECT_TYPE_INFORMATION objObjectType = new Win32Api.OBJECT_TYPE_INFORMATION();
int nLength = 0;
if (!Win32Api.DuplicateHandle(processHwnd, systemHandleInformation.Handle, Win32Api.GetCurrentProcess(), out ipHandle, 0, false, Win32Api.DUPLICATE_SAME_ACCESS)) return null;
IntPtr ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
Win32Api.NtQueryObject(ipHandle, (int)Win32Api.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
objBasic = (Win32Api.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
Marshal.FreeHGlobal(ipBasic);
IntPtr ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
nLength = objBasic.TypeInformationLength;
while ((uint)(Win32Api.NtQueryObject(ipHandle, (int)Win32Api.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32Api.STATUS_INFO_LENGTH_MISMATCH)
{
Marshal.FreeHGlobal(ipObjectType);
ipObjectType = Marshal.AllocHGlobal(nLength);
}
objObjectType = (Win32Api.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);
Marshal.FreeHGlobal(ipObjectType);
if (strObjectTypeName != "Process") return null;
return (int)Win32Api.GetProcessId(ipHandle);
}
private static List<Win32Api.SYSTEM_HANDLE_INFORMATION> GetHandles(Process process)
{
const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004;
int nHandleInfoSize = 0x10000;
IntPtr ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize);
int nLength = 0;
IntPtr ipHandle;
while ((Win32Api.NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ipHandlePointer, nHandleInfoSize, ref nLength)) == STATUS_INFO_LENGTH_MISMATCH)
{
nHandleInfoSize = nLength;
Marshal.FreeHGlobal(ipHandlePointer);
ipHandlePointer = Marshal.AllocHGlobal(nLength);
}
byte[] baTemp = new byte[nLength];
Win32Api.CopyMemory(baTemp, ipHandlePointer, (uint)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);
}
Win32Api.SYSTEM_HANDLE_INFORMATION shHandle;
List<Win32Api.SYSTEM_HANDLE_INFORMATION> lstHandles = new List<Win32Api.SYSTEM_HANDLE_INFORMATION>();
for (long lIndex = 0; lIndex < lHandleCount; lIndex++)
{
shHandle = new Win32Api.SYSTEM_HANDLE_INFORMATION();
if (Is64Bits())
{
shHandle = (Win32Api.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle) + 8);
}
else
{
ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle));
shHandle = (Win32Api.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
}
if (shHandle.ProcessID != process.Id) continue;
lstHandles.Add(shHandle);
}
return lstHandles;
}
static bool Is64Bits()
{
return Marshal.SizeOf(typeof(IntPtr)) == 8 ? true : false;
}
}
}
Note, that I only tested this code on x64 Windows 7 with both x86 and x64 compilation option. I compiled it with VS2010 for .NET 4. This code is less than readable and I can't guaranty that it will work on all relevant platforms and architectures. However it works here (tm) and is useful for this esoteric task.
请注意,我仅在 x64 Windows 7 上使用 x86 和 x64 编译选项测试了此代码。我用 VS2010 for .NET 4 编译它。这段代码可读性差,我不能保证它可以在所有相关平台和体系结构上运行。然而,它在这里工作(tm)并且对于这个深奥的任务很有用。
回答by R. L. Watkins
When one launches a process using 'Process.Start()' one has the option of creating the process directly, or of launching 'cmd.exe' and letting 'cmd.exe' handle the details. The 'UseShellExecute' flag controls this. If you elect to leave the details to 'cmd.exe', which is common in situations where you want to invoke a fileand let the shell run the appropriate program to handle it, e.g. by "running" a '.txt' file, then on Win7 this will actually run 'cmd', which itself runs 'conhost'. If, on the other hand, you don't use 'ShellExecute' then 'Start()' won't run 'cmd' and you won't indirectly launch 'conhost'.
当使用“Process.Start()”启动进程时,可以选择直接创建进程,或启动“cmd.exe”并让“cmd.exe”处理细节。'UseShellExecute' 标志控制这一点。如果您选择将详细信息留给 'cmd.exe',这在您想要调用文件并让 shell 运行适当的程序来处理它的情况下很常见,例如通过“运行”一个 '.txt' 文件,然后在 Win7 上这将实际运行“cmd”,它本身运行“conhost”。另一方面,如果您不使用“ShellExecute”,则“Start()”将不会运行“cmd”,您也不会间接启动“conhost”。
回答by Roland Pihlakas
Based on zespri's answerI wrote updated methods.
This code is able to handle process id-s that are longer than 16-bit.
Also one logic bug was fixed and some memory and handle leaks. Added some failsafety.
I added a method for case when conhost.exe has multiple associated processes. This can happen when there is a console program running and has cmd.exe as its parent process, but also some other cases, where the associated processes are not even in child-parent relationship.
Thanks a lot for zesprifor the original code, there is lot to learn from it!
根据zespri的回答,我编写了更新的方法。
此代码能够处理超过 16 位的进程 ID。
还修复了一个逻辑错误以及一些内存和句柄泄漏。添加了一些故障安全。
当 conhost.exe 有多个关联进程时,我添加了一个方法。当有一个控制台程序正在运行并且将 cmd.exe 作为其父进程时,可能会发生这种情况,但在其他一些情况下,关联的进程甚至没有子父关系。
非常感谢zespri的原始代码,从中可以学到很多东西!
More explanation for the method updates:
For WinXP+ it is better to use SYSTEM_EXTENDED_HANDLE_INFORMATION since SYSTEM_HANDLE_INFORMATION returns only 16-bit long process id-s. If system is heavily loaded with handles then process id-s tend to start having values above 65k, for example 8 decimal digits. The system call that the above code uses would simply mask off the high bits of process id-s. You can find SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX and its usage in Process Hacker's source code.
方法更新的更多解释:
对于 WinXP+,最好使用 SYSTEM_EXTENDED_HANDLE_INFORMATION,因为 SYSTEM_HANDLE_INFORMATION 只返回 16 位长的进程 id-s。如果系统的句柄负载很重,那么进程 id-s 的值往往会超过 65k,例如 8 位十进制数字。上述代码使用的系统调用将简单地屏蔽进程 id-s 的高位。您可以在 Process Hacker 的源代码中找到 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:
更新:
I added important bugfixes for 64-bit code on 27.06.2014.
The packing of the UNICODE_STRING struct was wrong and the code tried to compensate for that in some tricky manner. Although it did not manifest in Win7, it crashed nicely under Win8.
The other structs' packing with Pack=1 was incorrect too, but accidentally did not change their computed layout.
The important changed parts were:
我在 2014 年 6 月 27 日为 64 位代码添加了重要的错误修正。
UNICODE_STRING 结构的打包是错误的,代码试图以某种棘手的方式弥补这一点。虽然它在Win7下没有表现出来,但在Win8下它崩溃得很好。
其他结构的 Pack=1 打包也是不正确的,但意外地没有改变它们的计算布局。
重要的变化部分是:
[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);