如何检测“是否按任意键继续。 。 。“会显示吗?

问题描述 投票:10回答:7

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

按任意键继续 。 。 。

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

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

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

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

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

visual-studio ide console-application visual-studio-debugging
7个回答
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);
    }
}

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

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

所以,我们所期待的一切都已经完成了。我认为这是一种解决方案。它在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是现代的,一些WINAPIs可能会在未来被弃用。但这种构成的真正原因在于它的简单性。旧的Tool Help Library就像将ProcessThread转换为System.Threading.Thread一样复杂。使用LINQ和扩展方法,我们可以使代码更简单,更具语义。


2
投票

听起来像这个提示是由pause命令提供的。 Visual Studio自动添加此命令。

在Visual Studio外部运行项目时,没有理由“检测”此命令。您可以放心地假设它不会添加到您的程序中。这意味着你可以继续添加你想要的任何提示,类似于:

Console.WriteLine("Press any key...");
Console.Read();

See this question.


2
投票

这是一段应该做的代码:

class Program
{
    static void Main(string[] args)
    {
        // do your stuff

        if (!WasStartedWithPause())
        {
            Console.WriteLine("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

public static bool WasStartedWithPause()
{
    // Here, I reuse my answer at http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way
    Process parentProcess = ParentProcessUtilities.GetParentProcess();

    // are we started by cmd.exe ?
    if (string.Compare(parentProcess.MainModule.ModuleName, "cmd.exe", StringComparison.OrdinalIgnoreCase) != 0)
        return false;

    // get cmd.exe command line
    string cmdLine = GetProcessCommandLine(parentProcess);

    // was it started with a pause?
    return cmdLine != null & cmdLine.EndsWith("& pause\"");
}

public static string GetProcessCommandLine(Process process)
{
    if (process == null)
        throw new ArgumentNullException("process");

    // use WMI to query command line
    ManagementObjectCollection moc = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + process.Id).Get();
    foreach (ManagementObject mo in moc)
    {
        return (string)mo.Properties["CommandLine"].Value;
    }
    return null;
}

1
投票

该消息与您的程序无关。您可以向程序添加任何您喜欢的程序,如果您从命令提示符运行它,它将以与它相同的方式执行。

Visual Studio显示它是为了向您显示其执行已完成运行,因此您知道它已正确结束。如果你想跳过它,你可以尝试“无需调试运行”(或类似的东西;它就在“使用调试器运行”下面)。


1
投票

这是由visual studio添加的“pause”命令的输出。如果你看到这个,程序就结束了。这就是一个问题,例如应用程序可能检测到它自身已经结束。我认为这不符合逻辑。


0
投票

命令系统(“暂停”)时给出此提示

用来。

即使我遇到了这个问题。您可以使用conio.h下的函数_getch()来等待按键。

所以你可以使用以下代码:

      cout<<"your message here"
      _getch()

这将等待按键并显示您自己的提示。


0
投票

无法检测,但是如果你将它添加到Main的末尾,当你运行调试器时会添加相同的提示(F5):

if (Debugger.IsAttached)
{
    Console.Write("Press any key to continue . . . ");
    Console.ReadKey();
}

它实际上与Ctrl + F5一样与& pause相同

© www.soinside.com 2019 - 2024. All rights reserved.