Окно администратора DOS/CMD в Windows 2012 R2 не принимает клавишу ESC, ожидаемую в консольном приложении C#

Вот фрагмент кода C# из консольного приложения, которое работает в моей локальной среде Windows 8 и VS .NET 2015. Это также работает, когда я запускаю консольное приложение в окне администратора DOS/CMD на моем локальном компьютере с Windows 8.

Консольное приложение (окно DOS/CMD, открытое от имени администратора) не принимает клавишу ESC при использовании удаленного рабочего стола Windows для подключения к серверу Windows 2012 R2.

do
{
    while (!Console.KeyAvailable)
    {
         // Do nothing while waiting for input
    }
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

Я знаю, что в Windows 2012 R2 есть специальные клавиши для специальных команд при использовании удаленного рабочего стола, однако эта клавиша ESC не работает с удаленным рабочим столом в Windows 2012 R2 в окне DOS/CMD.

Я знаю, что могу использовать другую клавишу (или другие комбинации), но я хочу знать, почему клавиша ESC не «принимается» окном DOS/консоли в этом сценарии.

[РЕДАКТИРОВАТЬ]: Хорошо, здесь мне нужно быть более конкретным.

Я действительно пытаюсь запустить EXE как консольное приложение. Код, который я разместил, работал в Windows 2008, но подождите, это еще не все! Прежде чем выводить результаты в командную строку CMD/DOS, я должен сначала подключиться к существующему родительскому окну CMD. Я использую код, найденный по следующему URL-адресу, либо для новой AllocConsole(), либо для AttachConsole(). http://www.jankowskimichal.pl/en/2011/12/wpf-hybrid-application-with-parameters/

Как в Windows Server 2008, так и в Windows Server 2012 он постоянно записывает все, что я закодировал с помощью Console.Write....

Однако в Windows Server 2012 этот код больше не принимает мой ввод с помощью ReadKey() или код, изначально опубликованный выше.

Фрагмент кода (bool "show" = true присоединяется к консоли, false отсоединяется). Опять же, работает для вывода во всех случаях, но не принимает мой ввод в Console.ReadKey().

//Declarations area
[DllImport("kernel32.dll",
        EntryPoint = "AllocConsole",
        SetLastError = true,
        CharSet = CharSet.Auto,
        CallingConvention = CallingConvention.StdCall)]
    private static extern bool AllocConsole();

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FreeConsole();

    [DllImport("kernel32", SetLastError = true)]
    private static extern bool AttachConsole(int dwProcessId);

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);

    private enum ConsoleCtrlEvent
    {
        CTRL_C = 0,
        CTRL_BREAK = 1,
        CTRL_CLOSE = 2,
        CTRL_LOGOFF = 5,
        CTRL_SHUTDOWN = 6
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GenerateConsoleCtrlEvent(ConsoleCtrlEvent sigevent, int dwProcessGroupId);

    [DllImport("User32.Dll", EntryPoint = "PostMessageA")]
    private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

[СНИП]

//Caller:
//Attach or create a console window to display information to the user
DoConsoleWindow(true);
//Determine if user has administrator privileges
if (UserHasAdminPrivileges(args))
{
    //Write information to the console
    HandleCommandArgs(args);
}
DoConsoleWindow(false);

[СНИП]

private static void DoConsoleWindow(bool show)
    {
        if (show == true)
        {
            ptr = GetForegroundWindow();
            int u;

            GetWindowThreadProcessId(ptr, out u);

            process = Process.GetProcessById(u);

            if (process.ProcessName == "cmd")    //Is the uppermost window a cmd process?
            {
                AttachConsole(process.Id);
                attachedExisting = true;
            }
            else
            {
                //no console AND we're in console mode ... create a new console.
                AllocConsole();
            }
        }
        else
        {
            try
            {
                //Must pause for 2 seconds to allow display of data to catch up?
                Thread.Sleep(2000);

                //Send the {ENTER} Key to the console. 
                PostMessage(ptr, WM_KEYDOWN, VK_RETURN, 0);

                FreeConsole();

                if (process != null)
                {
                    if (attachedExisting != true)
                    {
                        process.Close();
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Log(TraceEventType.Error,
                    string.Format("{0} failed handling console close", serviceName),
                    string.Format("{0} failed handling console close: {1}",
                        serviceName, ex.ToString()),
                        serviceLogContext);
            }
        }
    }

person Troy Munford    schedule 19.01.2016    source источник
comment
Хм, нет, это не может быть проблемой программирования. Попробуйте serverfault.com   -  person Hans Passant    schedule 20.01.2016
comment
@HansPassant: Возможно, это так, если на удаленном сервере ограничены процессоры. У него есть горячее ожидание, которое загружает один ЦП на 100%. Я предполагаю, что это может привести к тому, что удаленные события будут пропущены, по крайней мере, время от времени.   -  person Eric J.    schedule 20.01.2016
comment
Нет, вы просто догадываетесь. Единственное, что вы можете видеть.   -  person Hans Passant    schedule 20.01.2016


Ответы (1)


Давайте посмотрим на ваш код

do
{
    while (!Console.KeyAvailable)
    {
         // Do nothing while waiting for input
    }
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

Часть

while (!Console.KeyAvailable)

это горячая петля. Одно ядро ​​ЦП будет работать почти на 100 % при опросе KeyAvailable. На моей 8-ядерной машине код по-прежнему отвечает и завершается при нажатии ConsoleKey.Escape. Однако это излишне неэффективно, и если у вас есть только одно или ограниченное количество ядер ЦП, это может привести к тому, что событие нажатия клавиши будет пропущено.

Переписать на более эффективный

do
{
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

и посмотреть, исчезнет ли проблема.

Я запустил ваш исходный код и мой на сервере Windows 2012 R2, доступ к которому осуществляется через удаленный рабочий стол. Оба варианта работали. На этом сервере у меня есть несколько доступных ядер ЦП, и одно из них достигло 100% с вашим кодом.

person Eric J.    schedule 19.01.2016
comment
Это не решает проблему на сервере Windows 2012 2. После еще нескольких тестов он выглядит как простой Console.ReadKey(); тоже не работает! На этом сервере используется .NET Framework 4.6.1. - person Troy Munford; 20.01.2016