Как msys2 отображает вывод консоли из неконсольного приложения?

#c #windows #console #mingw #msys2

#c #Windows #консоль #mingw #msys2

Вопрос:

Если я скомпилирую этот исходный код:

 #include <stdio.h>

int main(int argc, char* args[]) {
  printf("Done!n");
  return 0;
}
  

с gcc hello.c -o hello -mwindows помощью msys2 с помощью mingw-w64-x86_64-toolchain toolchain, а затем запустите его изнутри msys2, я увижу:

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

В то же время, если я вызову этот же исполняемый файл из powershell (или cmd), я увижу:

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

Честно говоря, учитывая -mwindows , что говорится о создании исполняемого файла Windows в отличие от консольного, я не удивлен последним — я видел это много раз.

Но как msys2 удается отображать этот вывод?

Ответ №1:

Существует очень мало различий между консольным и графическим приложением в Windows, и код stdio во время выполнения C обычно не волнует, он заботится только о стандартных дескрипторах Win32.

Основное отличие заключается в том, как CreateProcess работает родительское приложение.

  • Консольное приложение подключается к стандартным дескрипторам Win32 родителей, если у родителя есть консоль. Если у родительского устройства нет консоли, для приложения создается новое окно консоли. Родительский элемент может передавать необязательные флаги CreateProcess , чтобы принудительно / запретить новую консоль.

  • Приложение с графическим интерфейсом не подключено к стандартным дескрипторам Win32, и новая консоль не создается.

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

Приложение терминала msys2, вероятно, не является настоящим консольным приложением и, вероятно, вызывает CreateProcess принудительные дескрипторы ( STARTF_USESTDHANDLES ). Эти дескрипторы, вероятно, являются дескрипторами каналов. Ваше приложение будет видеть эти дескрипторы как перенаправленные дескрипторы stdio и выполняться аналогично тому, как cmd.exe выступал yourconsoleapp.exe | otherconsoleapp.exe бы .

Комментарии:

1. Да, MSYS2 использует именованные каналы для стандартного ввода-вывода. Это обычная проблема для консольных приложений, которые используют полную буферизацию при записи в файл канала или диска, с обходными путями, такими как winpty.

2. Что касается CMD, интересно, что он временно изменяет свои собственные стандартные дескрипторы и полагается на наследование, а не на использование STARTUPINFO стандартных дескрипторов. Сначала он пытается вызвать CreateProcessW с bInheritHandles помощью as TRUE . Если это удается, дочерний элемент создается с копией значений стандартных дескрипторов родителей, независимо от того, унаследованы они или нет. Внутренний ConsoleHandle не наследуется для неконсольного приложения, но наследуемые стандартные дескрипторы ввода-вывода наследуются во всех случаях, поэтому мы можем сделать что-то вроде .hello.exe | more , учитывая «hello.exe » это неконсольная сборка операционной системы.

3. Если вместо этого мы запускаем ассоциацию file by filetype, CMD использует ShellExecuteExW , и в этом случае результат зависит от того, является ли связанный исполняемый файл консольным приложением. ShellExecuteExW не наследует дескрипторы, поэтому file.ext | more не будет работать, если «.EXT» связан с нашим неконсольным «hello.exe «. Если мы создадим «hello.exe «как консольное приложение, оно будет работать, потому NtCreateUserProcess что неявно дублирует (а не наследует) стандартные дескрипторы консольного приложения.