如何检测"按任意键继续...".会显示吗?

Mer*_*ham 10 ide console-application visual-studio visual-studio-debugging

在Visual Studio中运行控制台应用程序时,根据您的设置,它将在程序退出后添加提示:

按任意键继续 ...

我已经找到了如何检测我是否在调试器(使用Debugger.IsAttached)下运行,但它没有帮助.按CTRL-F5不开始调试设置该标志false,但仍显示提示.

我想检测这个,因为我想显示我自己的消息并等待按键,但不要加倍按键检查.

我不想破坏我的一般Visual Studio设置.如果我可以以可以检查到源代码管理的方式为此项目禁用它,那也可以.

使用什么机制来附加此提示,以及如何检测它?

或者如何为每个项目禁用它,并将此更改检查为源代码管理?

Ken*_*Kin 7

将以下代码添加到控制台应用程序:

public static class Extensions {
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);

    [DllImport("kernel32.dll")]
    static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);

    public static Process GetParentProcess(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ParentProcessId"])
            ).First();
    }

    public static IEnumerable<Process> GetChildProcesses(this Process x) {
        return (
            from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
            where (uint)it["ParentProcessId"]==x.Id
            select Process.GetProcessById((int)(uint)it["ProcessId"])
            );
    }

    public static void Abort(this ProcessThread x) {
        TerminateThread(OpenThread(1, false, (uint)x.Id), 1);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样修改你的代码:

class Program {
    static void Main(String[] args) {
        // ... (your code might goes here)

        try {
            Process.GetCurrentProcess().GetParentProcess().Threads.Cast<ProcessThread>().Single().Abort();
        }
        catch(InvalidOperationException) {
        }

        Console.Write("Press ONLY key to continue . . . ");
        Console.ReadKey(true);
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,我们所期待的一切都已经完成了.我认为这是一种解决方案.它可以工作Windows XP SP3,我猜它可以用于较新的Windows操作系统.在Visual Studio下,应用程序始终是一个衍生过程.在较旧的Visual C++ 6.0中,它由IDE通过调用生成VCSPAWN.EXE; 在Visual Studio 2010中,应用程序在启动时不使用调试时使用以下命令行运行:

"%comspec%"/ c""您的应用程序文件名"&pause"

因此,无法以完全管理的方式实现目标; 因为它不在应用程序域下.

这里我们使用托管的方式WMI来枚举进程,并封装非托管的WINAPIs以终止ProcessThreads,因为ProcessThread它不应该被正常中止; 它提供了像只读的东西.

如上所述,应用程序是使用特定的命令行生成的; 它会有一个线程创建一个进程签名,所以我们使用该Single()方法来检索该线程并终止它.

当我们在现有命令提示符下启动应用程序时,它与Start Without Debugging相同.而且,当启动调试时,应用程序进程由创建devenv.exe.它有很多线程,我们知道并且不会中止任何线程,只是提示并等待按键.这种情况类似于双击启动应用程序或从上下文菜单启动应用程序.这样,应用程序进程通常由系统shell创建Explorer.exe,它也有很多线程.

实际上,如果我们可以成功中止线程,则意味着我们有权杀死父进程.但是,我们不要需要.我们只需要中止唯一的线程,当系统没有更多线程时,进程会自动终止.通过识别调用进程来杀死父进程%comspec%是另一种做同样事情的方法,但这是一个危险的过程.因为生成应用程序的进程可能具有其他线程,这些线程具有任意数量的线程,所以创建进程匹配%comspec%.你可能会粗心地杀死一个重要的过程工作,或者只是增加检查过程是否可以安全杀死的复杂性.所以我认为单个线程创建一个进程作为我们父进程的签名,可以安全地终止/中止.

WMI是现代的,有些WINAPI可能会在未来被弃用.但这种构成的真正原因在于它的简单性.老Tool Help Library就是这样复杂的像转换的方式ProcessThreadSystem.Threading.Thread.使用LINQ和扩展方法,我们可以使代码更简单,更具语义.