Как я могу определить, если "Нажмите любую клавишу, чтобы продолжить ..." будет отображаться? - PullRequest
10 голосов
/ 18 сентября 2011

При запуске консольного приложения в Visual Studio, в зависимости от ваших настроек, после выхода из программы оно добавит приглашение:

Нажмите любую клавишу для продолжения.,.

Я нашел, как определить, работаю ли я под отладчиком (используйте Debugger.IsAttached), но это бесполезно.Нажмите CTRL-F5 до Запуск без отладки устанавливает этот флаг на false, но по-прежнему отображает приглашение .

Я хочу обнаружитьэто потому, что я хотел бы показать свое собственное сообщение и ждать нажатия клавиш, но не удваивать проверки нажатия клавиш .

Я не хочу портить мои общие настройки Visual Studio.Если я смогу отключить его для этого проекта в способом, который можно проверить в системе контроля версий , это также сработает.

Какой механизм используется для добавления этого приглашения и как я могу обнаружитьэто?

Или как отключить его для каждого проекта и проверить это изменение в управлении исходным кодом?

Ответы [ 7 ]

7 голосов
/ 28 февраля 2013

Добавьте следующий код в консольное приложение:

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.EXEVisual Studio 2010 ваше приложение запускается со следующей командной строкой, когда Запуск без отладки :

"% comspec%" / c "" имя файла вашего приложения«& пауза»

Таким образом, невозможно достичь цели полностью управляемыми способами ;потому что это было НЕ в домене приложения.

Здесь мы используем управляемый способ WMI для перечисления процессов и инкапсулируем неуправляемые WINAPI s для завершения ProcessThread s, потому что ProcessThread не должен обычно прерываться;он предоставляется как что-то только для чтения.

Как упоминалось выше, приложение было порождено определенной командной строкой; будет иметь один поток, создающий сигнатуру одного процесса, поэтому мы использовали метод Single(), чтобы получить этот поток и завершить его.

Когда мы запускаем приложение в существующей командной строке, это тот же сценарий, что и Запуск без отладки .Более того, когда Start Debugging , процесс приложения создается devenv.exe.В нем много потоков, мы знаем это и не будем прерывать ни одного потока, просто приглашаем и ждем нажатия клавиши.Эта ситуация аналогична запуску приложения двойным щелчком мыши или из контекстного меню .Таким образом, процесс приложения создается системной оболочкой, обычно Explorer.exe, и также имеет много потоков.

Фактически, если мы можем успешно прервать поток, это означает, что у нас есть разрешения на уничтожение родительского процесса.Но нам НЕ нужно.Нам просто нужно прервать единственный поток, процесс автоматически завершается системой, когда у него больше нет потоков.Убить родительский процесс, указав, что вызывающим процессом является %comspec%, - это еще один способ сделать то же самое, но это опасная процедура.Поскольку процесс, порождающий приложение, может иметь другие потоки, у которых есть любое количество потоков, создайте соответствующий процесс %comspec%.Вы можете убить критическую работу процесса небрежно или просто усложнить проверку безопасности процесса.Поэтому я рассматриваю , когда один поток создает один процесс в качестве сигнатуры нашего родительского процесса, который можно безопасно уничтожать / прерывать.

WMI современно, некоторые из WINAPI могут устареть в будущем.Но настоящая причина этой композиции в ее простоте.Старый Tool Help Library такой сложный, как способы конвертации ProcessThread в System.Threading.Thread.С помощью LINQ и методов расширения мы можем сделать код более простым и более семантическим.

2 голосов
/ 01 марта 2013

Вот фрагмент кода, который должен это сделать:

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 /349246/kak-poluchit-roditelskii-protsess-v-net-upravlyaemym-sposobom
    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;
}
2 голосов
/ 18 сентября 2011

Похоже, что это приглашение предоставляется командой pause. Эта команда автоматически добавляется Visual Studio.

Когда вы запускаете свой проект за пределами Visual Studio, нет никаких оснований для «обнаружения» этой команды. Вы можете смело предполагать, что он не будет добавлен в вашу программу. Это означает, что вы можете пойти дальше и добавить любое приглашение, подобное:

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

См. Этот вопрос.

1 голос
/ 18 сентября 2011

Это сообщение не имеет ничего общего с вашей программой. Вы можете добавить в свою программу все, что захотите, и она будет работать так же, как если бы вы запускали ее из командной строки.

Visual Studio отображает его с целью показать, что его выполнение завершено, поэтому вы знаете, что оно завершилось правильно. Если вы хотите пропустить его, вы можете попытаться «запустить без отладки» (или что-то в этом роде; оно чуть ниже «запустить с отладчиком»).

0 голосов
/ 19 февраля 2019

Не поможет обнаружить, но добавит ту же подсказку при запуске с подключенным отладчиком (F5), если вы добавите это в конец Main:

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

Это будет на практикесделать так же, как Ctrl + F5 с & pause

0 голосов
/ 03 марта 2013

Это вывод команды «пауза», которая добавляется Visual Studio. Если вы видите это, программа заканчивается. В связи с этим возникает вопрос, может ли приложение обнаружить, что оно само покончено. Я думаю, что это не логично.

0 голосов
/ 02 марта 2013

Эта подсказка выдается, когда команда система («пауза»)

используется.

Даже я сталкивался с этой проблемой. Вы можете использовать функцию _getch () в conio.h для ожидания нажатия клавиши.

Таким образом, вы можете использовать следующий код:

      cout<<"your message here"
      _getch()

Это подождет нажатия клавиши и отобразит ваш собственный запрос.

...