Набор цветов ANSI для графического представления прерывается в середине пакета и работает после его продолжения

У меня есть пакет, в котором есть подраздел, который перебирает строки файла для запуска EXE-файлов, а затем пакет сортирует EXE-файлы на основе их кодов выхода.

По какой-то причине ANSI SGR, кажется, ломает или повторяет буквальный текст после установки графического представления предыдущего вместо его повторного рендеринга.

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

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

Содержимое test_map.log не должно иметь большого значения, поскольку на самом деле вы используете только Блокнот и отправляете ему некоторые аргументы. Вот что у меня настроено:

C:\temp\qt_selftest.exe
C:\temp\sub_test.exe
C:\temp\cmd_module_test.exe
C:\temp\failing_qt_test.exe
C:\temp\passing_qt_test.exe
C:\temp\random_qt_test.exe
C:\temp\fail_module.exe
C:\temp\pass_module.exe

И как вы можете видеть на скриншоте, линии трактуются буквально. В самом пакете, из которого я это вытащил, он снова работает... но в этом блоке и только в этом блоке он сломан.

введите здесь описание изображения

Любая идея, где я могу все испортить?

Я не могу поделиться кодом напрямую из-за преобразования последовательностей ESC, поэтому вот суть: >https://gist.github.com/the-nose-knows/1bebce2719e020188c6307cff736f951

Если вам нужно повторно добавить их перед [, используйте альтернативный код 027, как alt 0 2 7


person kayleeFrye_onDeck    schedule 03.07.2017    source источник
comment
cmd отключает режим виртуального терминала консоли, когда он запускает внешнюю программу, и восстанавливает режим VT, когда он восстанавливает контроль в качестве процесса переднего плана - если это вам поможет. Трудно сказать больше без примера кода, воспроизводящего проблему.   -  person Eryk Sun    schedule 04.07.2017
comment
Тот неловкий момент, когда вы думали, что у вас есть все, прежде чем задать вопрос xD Спасибо, @eryksun!   -  person kayleeFrye_onDeck    schedule 04.07.2017
comment
Что интересно в вашем комментарии, так это то, что инструмент проверки, который я заменил с помощью Блокнота, использует bool process_created = CreateProcess(obj.process_name.c_str(), parameters, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, p_startup_info, p_proc_info);, и в последнее время я не делал много обновлений для этого инструмента, но я мог вызвать что-то, что изменило его поведение в пакетных файлах. Возможно, тот факт, что я теперь всегда заполняю cmd-params хотя бы процессом, который нужно создать, сделал его внешним.   -  person kayleeFrye_onDeck    schedule 04.07.2017
comment
Это вывод партии из сути. Некоторые из цветовых кодов могут не совпадать 1:1, но это потому, что я переделал изолированную тестовую партию.   -  person kayleeFrye_onDeck    schedule 04.07.2017
comment
Это ошибка в cmd.exe. При запуске он сохраняет исходный режим вывода консоли, в котором отключен режим виртуального терминала. Он восстанавливает этот режим при запуске внешней программы. После выхода программа сбрасывает свой собственный режим, в котором включен режим виртуального терминала. Ошибка заключается в том, что в операторе цикла for, как только он восстанавливает режим запуска консоли для выполнения notepad.exe, он никогда не сбрасывается в свой собственный режим до выхода из цикла. По-видимому, это зависит исключительно от цикла оценки пакетного сценария верхнего уровня, который сбрасывает режим консоли между операторами.   -  person Eryk Sun    schedule 04.07.2017
comment
В качестве обходного пути вы можете запустить cmd.exe из простой консольной программы, которая включает режим VT. Затем, когда cmd восстанавливает исходный режим для запуска внешней программы, режим VT по-прежнему будет включен.   -  person Eryk Sun    schedule 04.07.2017
comment
Я был бы рад принять комбинацию ваших последних двух комментариев в качестве фактического ответа. Какой досадный косметический баг! :(   -  person kayleeFrye_onDeck    schedule 04.07.2017


Ответы (1)


Как сказал Эриксон, это можно решить, включив режим виртуального терминала. Если вас только интересует цвет PowerShell, вы можете добавить переключатель /A к вызову CMD.exe, в противном случае вам понадобится небольшой процесс, который обрабатывает этот вид, как прокладку, но один это также гарантирует, что VTM включен. Это не полностью плохо, так как этот уровень абстракции может пригодиться для будущих вариантов использования и ошибок.

Единственной «странной» частью этого фрагмента будет использование rel-path. Этот фрагмент представляет собой прокладку процесса из подкаталога для запуска пакетной прокладки из одного каталога.

Важными частями кода являются заголовки, которые необходимо включить, и включение VTM для StdOut после получения дескриптора консоли.

#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <Windows.h>

#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#endif

bool uses_whitespace(std::string test_string)
{
    size_t path_white_space_query = test_string.find(' ', 0);

    if (path_white_space_query != std::string::npos)
    {
        return true;
    }

    return false;
}

int main(int argc, char* argv[])
{
    std::string this_app_path = std::string(argv[0]);

    auto it = this_app_path.find_last_of("\\", std::string::npos);

    std::string path(this_app_path, 0, it);

    // Just forwarding the args that were sent to this shim to a batch in a known location,
    // making sure whitespace arguments keep their quotes when forwarded.

    // CMD.exe will need proper quote-handling, or the call will get mangled.
    std::string str = "C:\\Windows\\System32\\cmd.exe /C \"\"" + path + "\\..\\app_shim.bat\"";
    std::vector<std::string> args;
    std::copy(argv + 1, argv + argc, std::back_inserter(args));

    for (auto const& arg : args)
    {
        if (uses_whitespace(arg))
        {
            str += (" \"" + arg + "\"");
        }
        else
        {
            str += (" " + arg);
        }
    }

    // end-of-CMD-call final wrapping quote
    str += "\"";

    HANDLE stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    DWORD mode = 0;
    GetConsoleMode(stdOutHandle, &mode);
    SetConsoleMode(stdOutHandle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);

    int exit_code = system(str.c_str());

    CloseHandle(stdOutHandle);

    return exit_code;
}
person kayleeFrye_onDeck    schedule 02.08.2017
comment
Это должно вызвать GetConsoleMode и ИЛИ флаг VT в текущий режим для установки через SetConsoleMode. Кроме того, вам не нужно делать это как для StandardOutput, так и для StandardError; это будет редкая ситуация, в которой это дескрипторы для отдельных экранных буферов консоли. - person Eryk Sun; 20.08.2017
comment
Хм, это отличная мысль о STDERR; спасибо, что упомянули об этом. Ни один из моих пакетных эхо-сигналов не попадет в STDERR, так что на самом деле это не имеет отношения к моему варианту использования, поскольку ни одно из приложений или сценариев, о которых я знаю, не использует окраску ANSI через STDERR (если это вообще возможно, непосредственно), что я знаю о xD Чем больше я узнаю, тем больше я знаю, что я не знаю, но учиться интересно! ^_^ Спасибо, @eryksun. - person kayleeFrye_onDeck; 21.08.2017
comment
@eryksun отражает ли последнее редактирование то, что вы имели в виду в своем комментарии? Думаю, я вас понял, и вроде работает нормально, но просто удостоверяюсь. - person kayleeFrye_onDeck; 22.08.2017
comment
Да, это то, что я имел в виду. - person Eryk Sun; 23.08.2017