Как вывести на стандартный ввод из приложения Windows?

Это фрагмент моего кода:

AttachConsole(-1);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
freopen("CONIN$", "r", stdin); //this doesn't seem to do anything
int n = 0;
cin >> n;
cout << n + 1;
FreeConsole();
WNDCLASSA MainWindow = { 0 };
MainWindow.hbrBackground = (HBRUSH) COLOR_WINDOW;
MainWindow.hCursor = LoadCursor(NULL, IDC_ARROW);
MainWindow.hInstance = hInst;
//here the window gets created etc.

Очевидно, что использование консоли здесь является базовым, но этого должно хватить для ответа на этот вопрос. Проблема в том, что не работают такие функции, как cin или scanf. cout, cerr, clog, printf работают нормально, но функции ввода ничего не делают. Как я могу заставить работать stdin (желательно с cin)?

Вы пробовали std::cin.clear() после freopen() потоковой передачи?

user10543939 24.10.2018 20:50

Почему вы не используете Unicode

Edward Karak 24.10.2018 20:53

@J. Добавление cin.clear() после freopen("CONIN$", "r", stdin) не работает.

DarkAtom 24.10.2018 20:56

@stackptr Мне не нужен Unicode, и я не понимаю, почему это актуально для этого вопроса

DarkAtom 24.10.2018 20:57

Вы можете попробовать другой метод, чем freopen(), описанный в статье это.

user10543939 24.10.2018 21:05

Интересная идея, но визуальная студия не позволяет мне изменить значение stdin (на самом деле это #defined)

DarkAtom 24.10.2018 21:19

Вы не проверяете возвращаемые значения (большой NO-NO). Отметьте gist.github.com/Xsoda/3120099.

CristiFati 24.10.2018 21:30

В статье предлагается не менять stdin, а указывать на него.

user10543939 24.10.2018 21:33

Вы включаете <stdio.h> или <cstdio>? Согласно этому: stackoverflow.com/questions/5257509/… что вы делаете должен работа. Как предложила CristiFati, проверьте возврат на freopen. Я бы посоветовал после перенаправления stdin сначала протестировать его с помощью (например) fgets(buf,sizeof(buf),stdin), чтобы убедиться, что stdin был перенаправлен правильно. Затем вы можете перейти к тестированию с помощью cin. Этот двухэтапный подход поможет изолировать, если freopen не работает или требуется что-то дополнительное, чтобы заставить cin работать (например, cin.clear()), как предлагали другие.

Craig Estey 24.10.2018 21:35

@CristiFati в статье указатель на новый ФАЙЛ не разыменовывался, поэтому запутался. Он все время говорил мне, что я пытался назначить FILE * для FILE. Я все еще пытаюсь реализовать это

DarkAtom 24.10.2018 21:41

@Craig Estey, вот что я включил: windows.h, io.h, conio.h, stdlib.h, stdio.h, iostream

DarkAtom 24.10.2018 21:45

@DarkAtom «Я все еще пытаюсь реализовать это», функция в моем ответе использует метод, описанный в статье.

user10543939 24.10.2018 21:47

Я не уверен, имеет ли это значение, но я бы использовал cstdio, так как это способ идиоматический. cstdio [вероятно] будет включать stdio.h, но сделает это таким образом, чтобы cin мог работать. Если бы вы использовали Только, используя stdin и нет, cin, включая stdio.h, было бы / должно быть хорошо. Их смешивание требует особой осторожности (например, вам нужно «сообщить» cin, что вы это делаете, чтобы он использовал буферизацию FILE вместо своей собственной - зависит от реализации). Итак, каковы результаты теста fgets?

Craig Estey 24.10.2018 21:56

@CraigEstey <cstdio> не делал никаких забавных вещей до включения <stdio.h> (Visual Studio).

user10543939 24.10.2018 21:59

@J. Доу заменил stdio.h и stdlib.h на cstdio и cstdlib соответственно. Никаких изменений в поведении. Я считаю, что cin не имеет ничего общего с cstdio, но с iostream, с той лишь разницей, что один находится в пространстве имен std, а другой - нет.

DarkAtom 24.10.2018 22:09

@DarkAtom Вам следует лучше следить за тем, на кого вы ссылаетесь в своих комментариях. (Второй раз не тот человек)

user10543939 24.10.2018 22:10

Я на самом деле имел в виду CristiFati и вас

DarkAtom 24.10.2018 22:15

Используйте AllocConsole, если родительский процесс с окном консоли отсутствует.

Barmak Shemirani 25.10.2018 01:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
19
574
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваша программа /SUBSYSTEM:WINDOWS будет отсоединена от консоли сразу после ее запуска, и командный процессор cmd.exe снова ждет ввода пользователя. Итак, stdin уже используется, прежде чем ваша программа сможет попытаться выполнить какую-либо операцию ввода.

На самом деле,

#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>

#include <iostream>

int APIENTRY WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    AttachConsole(-1);

    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    freopen("CONIN$", "r", stdin);

    std::cout.clear();
    std::cin.clear();

    std::cout << "Hello!\n";
    int i;
    std::cin >> i;
    std::cout << i << '\n';
    std::cin.get();
    std::cin.get();
}

работает должным образом при запуске из командной строки с помощью start /wait foobar.exe. (foobar.exe должен быть построен как x64, чтобы работать таким образом в x64 Windows. Попытка использовать исполняемый файл x86 дает забавные сообщения об ошибках.)

См. Как мне написать программу, которую можно запускать как консоль, так и приложение с графическим интерфейсом? для обсуждения темы.

он говорит, что _O_TEXT не определен

DarkAtom 24.10.2018 21:52
_O_TEXT определен в <fcntl.h>
user10543939 24.10.2018 21:53

Я вызвал это из WinMain и проверил, ложно ли возвращаемое значение. Если это так, отобразите окно сообщения Windows, в противном случае - нет. При запуске без консоли он отображает это (очевидно), но при запуске с помощью консоли он этого не отображает, но ни stdin, ни stdout не работают.

DarkAtom 24.10.2018 22:06

Вы проверяете возвращаемое значение вашего AttachConsole(-1);? Что вообще за родительский процесс?

user10543939 24.10.2018 22:08

да, я проверил, он возвращает TRUE только тогда, когда действительно есть вызывающая консоль

DarkAtom 24.10.2018 22:12

Вы хотите сказать, что ваш родитель - cmd.exe?

user10543939 24.10.2018 22:14

Да, я использую Windows, поэтому я использую cmd.exe, потому что других вариантов нет (я не думаю, что использование PowerShell что-то изменит)

DarkAtom 24.10.2018 22:16

Как только программа с SUBSYSTEM:WINDOWS запускается с консоли, она отключается от нее. Командный процессор не будет ждать завершения программы, а сразу же отобразит новое приглашение и будет ждать ввода данных пользователем. Как это могло работать, если бы ваша программа могла украсть stdin консоли ??

user10543939 24.10.2018 22:42

Метод freopen() действительно работает, если вы запускаете исполняемый файл с cmd.exe с помощью start /wait foobar.exe. Но на W10 x64 только с исполняемыми файлами x64. Вы получите забавные сообщения об ошибках, если попробуете с x86 exes.

user10543939 24.10.2018 22:52

Хорошо, это полезно знать, но какова тогда цель AttachConsole? Разве нельзя повторно подключить программу к cmd?

DarkAtom 24.10.2018 22:59

Цель AttachConsole() - подключиться к консоли родительского процесса, который либо имеет консоль, потому что это исполняемый файл SUBSYSTEM:CONSOLE, либо тот, который использовал AllocConsole().

user10543939 24.10.2018 23:04

См. blogs.msdn.microsoft.com/oldnewthing/20090101-00 для обсуждения темы.

user10543939 24.10.2018 23:05

Хорошо, использование AllocConsole и метода freopen работает нормально. По какой-то причине подключение к существующим консолям является проблемой, а создание консоли - нет.

DarkAtom 24.10.2018 23:06

Хорошо, я нашел дальний план, но, на мой взгляд, он довольно глупый. При подключении к консоли и последующем отключении cmd.exe, связанного с этой консолью, с помощью system("taskkill /f /im cmd.exe"); окно консоли останется открытым и будет свободно доступным для приложения. После того, как приложение завершит работу с консолью, оно может запустить cmd.exe с помощью system("start cmd.exe");. Единственным недостатком этого является то, что он изменяет PID cmd.exe и убивает все окна cmd (хотя я считаю, что это можно обойти, убив только процесс с родительским pid)

DarkAtom 24.10.2018 23:25

Я бы просто использовал консольное приложение и использовал бы консоль «легально» или FreeConsole() в противном случае.

user10543939 24.10.2018 23:31

Другие вопросы по теме