CreateProcess, вызывающий cmd.exe, вкл. аргументы без показа (мигающего) окна?

Сага продолжается...

Я искал в Интернете, я искал в StackOverflow, я нашел много надежд, дающих ответы/решения, но почему-то все они потерпели неудачу (вверх) у меня (включая те, которые связаны с ShellExecute(Ex) ).

Как скрыть (мигающее) окно CMD (включая аргументы) с помощью CreateProcess??

В основном я хочу вызвать/выполнить набор условных/собственных команд cmd.exe (т.е. FOR /F и ||), а также внешнюю команду FIND(STR).exe. И это, без показа (мигающего) окна CMD.

Но даже скрыть такую ​​простую вещь, как "cmd.exe /C ECHO ...flashing window is bad...", кажется невозможным.

Код, который я пробовал (включая множество вариантов, связанных с флагами dwFlags и wShowWindow

#include <windows.h>

int main()


{

    char cmdline[] = "cmd.exe /c ECHO ...flashing window is bad...";

    PROCESS_INFORMATION pi;
    STARTUPINFO si;

//  memset(&si,0,sizeof(STARTUPINFO));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);

//  si.dwFlags = STARTF_USESTDHANDLES;
//  si.dwFlags = CREATE_NO_WINDOW;
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;
//  si.wShowWindow = CREATE_NO_WINDOW;

    CreateProcess(NULL, (LPSTR) cmdline, NULL, NULL, 0, 0, NULL, NULL, &si, &pi);

    WaitForSingleObject(pi.hProcess, INFINITE);

    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

//  ExitProcess;

    return 0;

}

Я не хочу полагаться на внешние программы, такие как .vbs (Windows Scripting Host) или трюки с ярлыками, а просто на автономный скомпилированный .exe.

Это (действительно) слишком много, чтобы просить, или я делаю это (совершенно) неправильно?

Спасибо...


person script'n'code    schedule 04.10.2017    source источник
comment
Остановите спам тегов. Прочитайте описания тегов, прежде чем просто случайным образом добавить те, которые кажутся вам знакомыми. Два из тех, которые вы добавили, не имеют ничего общего с вашим вопросом здесь. Теги здесь имеют конкретное определение и назначение.   -  person Ken White    schedule 04.10.2017
comment
Точка входа main по умолчанию создает консольное приложение. Вы переопределяете значения по умолчанию компоновщика?   -  person Eryk Sun    schedule 04.10.2017
comment
Кен Уайт: Возможно, теги были слишком общими. Спасибо за совет. - eryksun: Зачем ты меня стыдишь? ;-) Я просмотрел настройки/параметры своего компоновщика и заметил, что для параметра не создавать окно консоли (-mwindows) установлено значение нет (т.е. отключено). После его включения окно больше не появляется/отображается o_O примечание: я раньше играл с нарушающей настройкой компоновщика, но в то время это как-то не работало... назовите это загадкой, но я все еще хочу выяснить, почему это работает сейчас ни с того ни с сего. Не возражаете, если я буду рассматривать ваш ответ как обходной путь, а не как решение?   -  person script'n'code    schedule 04.10.2017


Ответы (2)


Обновление: кажется, вы также путаете флаги CreateProcess (его аргумент dwCreationFlags) с элементом структуры STARTUPINFO. Это разные флаги, CREATE_NO_WINDOW не должно быть в STARTUPINFO.

Вы должны передать флаг CREATE_NO_WINDOW, тогда окно консоли не будет отображаться. Первоначально я ответил, что вам нужно перенаправить стандартные дескрипторы, что неверно (но все же настоятельно рекомендуется).

Установите STARTF_USESTDHANDLES и заполните соответствующие дескрипторы. Если вас интересует вывод процесса, создайте каналы, иначе вы можете просто открыть nul проход, который.

person rkapl    schedule 04.10.2017
comment
Перенаправление стандартных дескрипторов отличается от отображения окна консоли. Внутри действительно есть 4 стандартных дескриптора ввода/вывода -- ConsoleHandle, StandardInput, StandardOutput и StandardError. Дескрипторы консольного ввода/экранного буфера бесполезны, если они не подключены к консоли, т. е. внутренний ConsoleHandle. Дескриптор консоли также неявно используется для консольных функций, которые не принимают дескриптор буфера консоли, например GetConsoleCP. Функции AllocConsole и AttachConsole потенциально устанавливают все 4 дескриптора. - person Eryk Sun; 04.10.2017
comment
Если вызов CreateProcess не указывает флаг создания DETACHED_PROCESS, то консольное приложение автоматически присоединяется к своей родительской консоли или создает новую консоль, если у родителя ее нет или указан флаг создания CREATE_NEW_CONSOLE. Если ни один из последних не используется, то CREATE_NO_WINDOW можно использовать для создания новой консоли без окна. Если создается новая консоль с окном, окно можно сделать скрытым с помощью SW_HIDE в информации о запуске. - person Eryk Sun; 04.10.2017
comment
Спасибо, что указали на ошибки (я новичок в этом, думаю, мне не нужно в этом признаваться ;-)). О перенаправлении stdin/stdout: я предполагаю, что вы имеете в виду способ перенаправления C++, а не способ перенаправления CMD? Способ C++ перенаправления на null мне неизвестен (я видел только примеры перенаправления вывода в файл). Многое из этого до сих пор для меня загадка, но я учусь этому через неудачи, глядя на код людей и не боясь задавать вопросы... Я посмотрю, что я могу сделать, но это все еще звучит немного абстрактно для меня ;-) - person script'n'code; 04.10.2017
comment
Да, я имел в виду перенаправление C++. nul — это специальный файл/устройство, предоставляемое Windows. - person rkapl; 04.10.2017
comment
О чем я думал ;-) .. Возможно, я ошибаюсь, но в случае, если IDE предлагает возможность скрыть (не показывать) окно консоли при выполнении, использует ли компилятор аналогичный/сопоставимый метод/способ скрытия окно (внутри), как при использовании возможностей скрытия ShellExecute(Ex) и CreateProcess? (хотя последний дает больше элементов управления/ручек) - person script'n'code; 04.10.2017
comment
@eryksun Нет четвертого дескриптора (то, что вы называете ConsoleHandle). Сами стандартные дескрипторы являются дескрипторами буфера консоли. GetConsoleCP использует дескриптор StandardInput. Я пытался выследить эти ручки и выяснить, что они ведут себя странным образом. Здесь есть отличная запись: github.com/rprichard/win32-console-docs - person rkapl; 05.10.2017
comment
GetConsoleCP не использует дескриптор StandardInput. Используйте отладчик и проверьте параметры процесса, ?? @$peb->ProcessParameters. ConsoleHandle — это внутренняя, недокументированная деталь реализации. Установка допустимого дескриптора для консоли — это то, что означает присоединение к консоли. - person Eryk Sun; 05.10.2017
comment
До Windows 8 ConsoleHandle — это порт LPC, совместно используемый хост-процессом консоли, conhost.exe и присоединенным клиентским процессом. Дескрипторы буфера ввода/экрана создаются и управляются консолью и помечаются путем установки двух младших битов (например, 3, 7, 11), что позволяет API направлять запросы через специальные функции на основе LPC вместо использования обычного ядра I/. О функции. Кроме того, CreateFile предназначен для маршрутизации открытия con, conin$ и conout$ в функцию OpenConsoleW на основе LPC. - person Eryk Sun; 05.10.2017
comment
В Windows 8+ консоль использует устройство \Device\ConDrv. con, conin$ и conout$ — это объектные символические ссылки на виртуальные файлы на этом устройстве с именами соответственно Console, CurrentIn и CurrentOut. Начальные стандартные дескрипторы — это файлы на устройстве с именем Input and Output, а ConsoleHandle — это файл с именем Connect. При подключении используются другие файлы с именами Server и Reference. Дескрипторы консольного ввода/буфера экрана и дескриптор соединения являются дескрипторами ядра, используемыми со стандартными системными вызовами ввода/вывода NT NtReadFile, NtWriteFile и NtDeviceIoControlFile. - person Eryk Sun; 05.10.2017
comment
Вот пошаговый обзор инициализации консоли при запуске, когда консольное приложение создает новую консоль, используя более новый Реализация устройства ConDrv. - person Eryk Sun; 05.10.2017
comment
@eryksun Да, для связи с ConDrv/csrss используется дескриптор. Однако попробуйте установить дескриптор ввода на INVALID_HANDLE_VALUE. Ваш GetConsoleCP изменится на ноль. pastebin.com/9rJAHibZ . Я также думаю, что дескриптор консоли открывается только один раз в начале, независимо от того, сколько консолей вы открываете, но я могу ошибаться. - person rkapl; 05.10.2017
comment
Извини. Это не шутка, а моя глупость. Это означает, что вы правы. - person rkapl; 05.10.2017
comment
Функции, которые обращаются к глобальным данным в присоединенном экземпляре conhost.exe (например, GetConsoleCP, GetConsoleTitle и GetConsoleWindow), не связаны с буфером ввода/экрана, поэтому API неявно использует ConsoleHandle. Кроме того, консольный API в Window 7 всегда неявно использует ConsoleHandle, поскольку дескрипторы буфера ввода/экрана управляются conhost.exe, а внутри система направляет все вызовы консоли (включая консоль CreateFile, ReadFile или WriteFile) через соединение порта LPC на conhost.exe. В Windows 8+ с устройством ConDrv сложнее. - person Eryk Sun; 05.10.2017
comment
В Windows 8+ CreateFile, ReadFile и WriteFile используют обычный путь ввода-вывода к системным вызовам NtCreateFile, NtReadFile и NtWriteFile. Другие консольные API используют NtDeviceIoControlFile либо с дескриптором буфера ввода/экрана, либо файлом Connect ConsoleHandle. То, как это реализовано, файлы ввода и вывода по умолчанию в ConDrv работают с любой подключенной консолью. Дескриптор Connect специфичен для подключения к конкретному экземпляру conhost.exe. Если вы отсоединитесь через FreeConsole, этот файл будет закрыт. - person Eryk Sun; 05.10.2017
comment
...Если вы открываете con (Console), conin$ (CurrentIn) или conout$ (CurrentOut), эти дескрипторы работают с этой конкретной консолью и только с этой консолью. Они продолжают работать, если вы отсоедините их через FreeConsole, а затем снова подключите через AttachConsole. - person Eryk Sun; 05.10.2017

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

String[] scriptArray = shellScript.toString().split(" ");

ProcessBuilder builder = new ProcessBuilder(scriptArray);
File outputFile = new File("/logs/AgentOutputLog.txt");
File errorFile = new File("/logs/AgentErrorLog.txt");
builder.redirectOutput(outputFile);
builder.redirectError(errorFile);
Process process = builder.start();
int errCode = process.waitFor();

//errCode = 0 means online
if(errCode == 0){
    success = true;
    break;
//errCode = 1 means offline
} else if (errCode == 1){
    success = false;
    break;
}
person Steven    schedule 04.10.2017
comment
Спасибо за подсказку/совет. Я должен признать, что я довольно новичок в этом (С++), поэтому я, скорее всего, не смогу собрать все кусочки головоломки вместе. Но я очень ценю помощь, которую вы предлагаете! Если мне удастся разобраться в этом, я сообщу вам о своих выводах. Просто существует так много способов/методов для достижения чего-то, что может сделать вещи немного сложными/сложными (каждый метод имеет свои плюсы/минусы). Я обязательно рассмотрю предложенный вами фрагмент кода, но вы должны понимать, что всегда может быть что-то лучше (более простое/минималистичное) за углом ;-) - person script'n'code; 04.10.2017