如何检测是否“按任意键继续"..."会显示? [英] How can I detect if "Press any key to continue . . ." will be displayed?

查看:16
本文介绍了如何检测是否“按任意键继续"..."会显示?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<块引用>

按任意键继续...

我找到了如何检测我是否在调试器下运行(使用 Debugger.IsAttached),但这没有帮助.按 CTRL-F5Start without Debugging 将此标志设置为 false,但 仍然显示提示.p>

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

我不想弄乱我的常规 Visual Studio 设置.如果我可以以一种可以签入源代码管理的方式为该项目禁用它,那也可以.

使用什么机制来附加这个提示,我如何检测它?

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

解决方案

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

公共静态类扩展{[DllImport("kernel32.dll")]静态外部 IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);[DllImport("kernel32.dll")]static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);公共静态进程GetParentProcess(此进程x){返回 (从它在 (new ManagementObjectSearcher("root\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()其中 (uint)it["ProcessId"]==x.Id选择 Process.GetProcessById((int)(uint)it["ParentProcessId"])).第一的();}公共静态 IEnumerable<Process>GetChildProcesses(此进程 x){返回 (从它在 (new ManagementObjectSearcher("root\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()其中 (uint)it["ParentProcessId"]==x.Id选择 Process.GetProcessById((int)(uint)it["ProcessId"]));}公共静态无效中止(此 ProcessThread x){TerminateThread(OpenThread(1, false, (uint)x.Id), 1);}}

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

类程序{静态无效主要(字符串[] args){//...(您的代码可能会放在这里)尝试 {Process.GetCurrentProcess().GetParentProcess().Threads.Cast().Single().Abort();}捕捉(无效操作异常){}Console.Write("按 ONLY 键继续...");Console.ReadKey(true);}}

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

<块引用>

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

所以不可能以完全管理的方式实现目标;因为它在应用程序域下NOT.

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

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

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

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

WMI 是现代的,某些 WINAPI 将来可能会被弃用.但这种组合的真正原因是它的简单性.旧的Tool Help Library 就像将ProcessThread 转换成System.Threading.Thread 的方法一样复杂.使用 LINQ 和扩展方法,我们可以使代码更简单、更语义化.

When running a console application in Visual Studio, depending on your settings, it will add a prompt after the program exits:

Press any key to continue . . .

I have found how to detect if I am running under the debugger(use Debugger.IsAttached), but it isn't helpful. Press CTRL-F5 to Start Without Debugging sets this flag to false, yet still shows the prompt.

I want to detect this because I'd like to display my own message and wait for a keypress, but not double up keypress checks.

I don't want to muck with my general Visual Studio settings. If I can disable it for this project in a way that can be checked into source control, that would also work.

What mechanism is used to append this prompt, and how do I detect it?

Or how do I disable it per-project, and check this change into source control?

解决方案

Add the following code to the console application:

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

And then modify your code like this:

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

So, everything we are expecting is done now. I consider this as a workaround solution. It works under Windows XP SP3 and I guess that it would work with newer Windows operating systems. Under Visual Studio, applications are always a spawned process. In older Visual C++ 6.0, it spawned by the IDE by calling VCSPAWN.EXE; in Visual Studio 2010, your application runs with following command line when Start Without Debugging:

"%comspec%" /c ""your application filename" & pause"

So it is impossible to reach the goal in fully managed ways; because it was NOT under the application domain.

Here we use the managed way of WMI to enumerate the processes, and encapsulate the unmanaged WINAPIs to terminate the ProcessThreads, because the ProcessThread is not supposed to be normally aborted; it's provided like something for read-only.

As mentioned above, the application was spawned with the particular command line; it would have a single thread creates a single process signature, so we used the Single() method to retrieve that thread and terminate it.

When we start the application under an existing command prompt, it is just the same scenario of Start Without Debugging. Moreover, when Start Debugging, the application process is created by devenv.exe. It has a lot of threads, we known that and won't abort any thread, just prompt and wait for a key press. This situation is similar to starting application with double-clicking or from context menu. This way, the application process is created by the system shell, usually Explorer.exe and it also has a lots of threads.

In fact, if we can successfully abort the thread it implies that we have the permissions to kill the parent process. But we do NOT need to. We just need to abort the only thread, the process terminates automatically by system when it has no more threads. Killing the parent process by identifying that the calling process is %comspec%, is another way to do the same thing, but it's a dangerous procedure. Because the process spawning the application might have other threads which have any number of threads create a process matches %comspec%. You may kill a critical work of process with carelessness or just growing the complexity of checking whether the process is safe to kill. So I consider a single thread creates a single process as a signature of our parent process which is safe to kill/abort.

WMI is modern, some of WINAPIs might become deprecated in the future. But the real reason of this composition is for its simplicity. The old Tool Help Library is such complicated like the ways to convert ProcessThread to System.Threading.Thread. With LINQ and extension methods, we can make the code simpler and more semantical.

这篇关于如何检测是否“按任意键继续"..."会显示?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆