Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 24.12.2011, 13:31   #1
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию Баг стандартного ввода? Или как мой бесстыжий код приводит к синему экрану смерти

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

Вопрос: как корректно подружить cin с новым буфером консоли?

Рассмотрим ниже представленный код:
(не вздумайте его запускать! Код может нанести вред вашему компьютеру)

Код:
int main()
{
    //создаю новый консольный буффер, какой мне нужен
    HANDLE FirstScreenBuffer= CreateConsoleScreenBuffer(  GENERIC_READ | GENERIC_WRITE, 
                                                    0,  NULL,  CONSOLE_TEXTMODE_BUFFER,  NULL  );

    //активирую его. Теперь консолька на него настроилась 
    SetConsoleActiveScreenBuffer(FirstScreenBuffer);

    
    cout<< "Введите данные"; //я не вижу этой надписи, потому что сменил буфер вывода
                             //а кауты по прежнему настроены на стандартный вывод
    int a; 
    cin>>a;   //в процессе ввода данных, я вижу голый экран.
              //Но когда нажимаю ентер, о чудо! Эта зараза сама почему то переключается на 
              //первичный буфер, и я вижу надпись "введите данные"
              //Что за самодеятельность??????????????

}
Но это - только половина беды. Настоящая беда заключается в том, что если попытаться дважды запустить данное приложение - оно виснет на какое то время, а потом вся ОС Windows крэшется.
Появляется Синий экран смерти. В частности, это так же означает, что все существующие процессы и потоки терминируются не успев завершиться. Тот же броузер, или студия не успевают сохранить свои настройки.
_Bers вне форума Ответить с цитированием
Старый 24.12.2011, 13:32   #2
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Теперь поиски лекарства:

Первое, что я сделал - это подружил каут с новым буфером:

Код:
void Redirect_Stdout(HANDLE hTarget); //перенаправление стандартных потоков вывода 
                                      //в новый буфер консоли

int main()
{
     HANDLE hOld = GetStdHandle(STD_OUTPUT_HANDLE);

    //создаю новый консольный буффер, какой мне нужен
    HANDLE FirstScreenBuffer= CreateConsoleScreenBuffer(  GENERIC_READ | GENERIC_WRITE, 
                                                    0,  NULL,  CONSOLE_TEXTMODE_BUFFER,  NULL  );

    //активирую его. Теперь консолька на него настроилась 
    SetConsoleActiveScreenBuffer(FirstScreenBuffer);

    //CloseHandle(hOld); //закроем за ненадобностью первичный буфер
                         //если убрать комментарий - получится синий экран смерти

    Redirect_Stdout(FirstScreenBuffer); //подружим каут с новым выводом

    
    cout<< "Введите данные"; //Теперь я вижу вывод каута

    int a; 
    cin>>a;   //в процессе ввода данных, я вижу свой новый такой красивый буфер.
              //ввижу приглашение вводить данные.

              //Но когда нажимаю ентер, о чудо! Эта зараза сама почему то переключается на 
              //первичный буфер, и я вижу голый экран, со скролингом
              //Что за самодеятельность??????????????

//По крайней мере он не ломает всю ОС Windows, 
//а только портит жизнь отдельно взятому программисту

}

void Redirect_Stdout(HANDLE hTarget)
{
     SetStdHandle(STD_OUTPUT_HANDLE, hTarget); 

     int hConHandle; long lStdHandle; FILE *fp;
          
     lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
     hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
     fp = _fdopen( hConHandle, "w" );
     *stdout = *fp;
     setvbuf( stdout, NULL, _IONBF, 0 );
}
Вопрос: Как сделать так, что бы после ввода данных, и нажатия enter, целевым оставался мой новый буфер, а не первичный?

Первое, что мне пришло в голову: перенаправить sdtin в соответствии с перенаправленным stdout

Код:
void Redirect_Stdout(HANDLE hTarget); //перенаправление стандартных потоков ввода-вывода 
                                      //в новый буфер консоли

void Redirect_Stdin(); //перенаправление стандартного потока ввода

int main()
{
    HANDLE hOld = GetStdHandle(STD_OUTPUT_HANDLE);

    //создаю новый консольный буффер, какой мне нужен
    HANDLE FirstScreenBuffer= CreateConsoleScreenBuffer(  GENERIC_READ | GENERIC_WRITE, 
                                                    0,  NULL,  CONSOLE_TEXTMODE_BUFFER,  NULL  );

    //активирую его. Теперь консолька на него настроилась 
    SetConsoleActiveScreenBuffer(FirstScreenBuffer);

    //CloseHandle(hOld); //закроем за ненадобностью первичный буфер
                         //если убрать комментарий - получится синий экран смерти

    Redirect_Stdout(FirstScreenBuffer); //подружим каут с новым буфером вывода
    Redirect_Stdin(); //попытка подружить син с новым буфером вывода

    
    cout<< "Введите данные"; //Теперь я вижу вывод каута

    int a; 
    cin>>a;   //в процессе ввода данных, я вижу свой новый такой красивый буфер.
              //ввижу приглашение вводить данные.

              //Но когда нажимаю ентер, о чудо! Приложение зависает. 
              //И через несколько чудных мгновений 
              //синий экран смерти меня очень любит
}

void Redirect_Stdin()
{
    int hConHandle; long lStdHandle; FILE *fp;
    
    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    fp = _fdopen( hConHandle, "r" );
    *stdin = *fp;
    setvbuf( stdin, NULL, _IONBF, 0 );
}

void Redirect_Stdout(HANDLE hTarget)
{
     SetStdHandle(STD_OUTPUT_HANDLE, hTarget); 

     int hConHandle; long lStdHandle; FILE *fp;
          
     lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
     hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
     fp = _fdopen( hConHandle, "w" );
     *stdout = *fp;
     setvbuf( stdout, NULL, _IONBF, 0 );
}
Вопрос: как корректно подружить cin с новым буфером консоли?

Последний раз редактировалось _Bers; 24.12.2011 в 13:34.
_Bers вне форума Ответить с цитированием
Старый 24.12.2011, 14:49   #3
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

В настоящий момент все пляски с бубнами вокруг cin оказались тщетны.

Создаётся устойчивое впечатление, что cin не дружит с WIN API системной консоли Windows.

Видимо, внутренняя реализация устройства, с которым изначально связан cin харкорно привязана к первичному буферу. И любые попытки от него отойти приводят ОС Windows к крэшу.

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

Итак, снова я выхожу на скользкую дорожку много-поточного программирования.

Рецепт лекарства не простой:

1. Создания безымяного пайпа, и перенаправление stdin в пайп. Теперь,
конец трубы "для записи" будет находится под управлением дочернего потока-Писателя, а конец трубы "для чтения" - под управлением перенаправленного stdin

2. Про стандартное устройство ввода на которое раньше был нацелен stdin можно будет забыть.

3. Дочерний поток-Писатель должен проверять состояние трубы - если на том конце кто-то очень хочет получить данные - активировать события ввода с клавиатуры посредством WIN API, и накапливать данные о нажатых кнопках, пока пользователь не нажмет enter.

4. После нажатия enter, дочерний поток сливает накопленные данные в трубу, и на том конце трубы cin разморозится, и выбросит в конечный контейнер-переменную полученные данные, и основной поток пойдёт дальше...

Тогда сразу возникают ряд проблем:
1. В период ввода данных с клавиатуры все остальные потоки (Читатель) должны замереть.
Решение проблемы - блокировка Читателя.

2. Как поток-Писатель узнает о том, что на том конце трубы хотят получить данные?
Как Писатель узнает о том, что к другому концу его трубы присосался cin и основной поток ожидает данные с клавиатуры?

Решение этой проблемы пока ещё не найдено....

Последний раз редактировалось _Bers; 24.12.2011 в 14:51.
_Bers вне форума Ответить с цитированием
Старый 24.12.2011, 15:04   #4
still_alive
Great Code Monkey
Форумчанин
 
Аватар для still_alive
 
Регистрация: 09.08.2007
Сообщений: 533
По умолчанию

Труба - как-то не звучит. Пайп или канал.

А какова собственно изначальная задача? Не этот кусочек, а глобально. Что ты хочешь сделать в итоге?
still_alive вне форума Ответить с цитированием
Старый 24.12.2011, 15:37   #5
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от still_alive Посмотреть сообщение
Труба - как-то не звучит. Пайп или канал.

А какова собственно изначальная задача? Не этот кусочек, а глобально. Что ты хочешь сделать в итоге?
Я сам - самоучка, с 3мя классами образования)
И с++ изучаю по книгам, статьям, и на примере решения не сложных, но реальных домашних проектиков.

Данный проект (который поначалу казался мне таким простым, ага) заключается в следующем:

Мне надоело это убогое черно-белое окошко, с крайне не удобным интерфейсом.

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

И что бы можно было формочки текстовые вставлять в консольку, и двигать их мышкой. Воот! А ещё хочу, что бы можно было легко и быстро вставлять в окошко консольки картинки (BITMAP)

И что бы, для инициализации библиотеки волшебной консоли достаточно было сделать: #include "WizardConsole.h"

А дальше все произойдёт автоматически - красивая консоль полностью подменит собой системную.

Требования к проекту:
1. Полная совместимость с системной консолью.

Это означает, что весь код, который работал в обычной консолью, должен без изменений работать и с моей консолью.

Это значит, что все cout, cin, printf, etc должны быть перехвачены, и перенаправлены в мою собственную консольку. Ввод-вывод осуществляется через мою консоль. Данные выводятся в моём окошке.

2. Расширяемость. Волшебная консоль не должна быть хардкорно связана с устройством вывода (окошком), и это устройство легко можно было бы заменять на другое.

Это означает, что я в любой момент смогу прицеплятся к устройству вывода, допустим созданного при помощи WIN API, или glut, или при помощи любого другого инструмента, умеющего создавать окна, и выводить графику.

В идеале, обновление продукта вообще должно происходить без перекомпиляции всей библиотеки. Но это только пожелание. В требованиях данный пункт отсуствует.

3. Удобство и практичность.

Дизайн кода:

console(новые координаты, цвет) << "ля ля ля"; //вывод по координатам, с заданным цветом

ну или:

console << eRED<<"ля ля ля"; //цвет можно поменять и вот так

ну или:

console(eHIDE_ACTIONS); //теперь манипуляции с консолью будут невидимы
{
//тут куча вывода
}
console(eVISIBLE_ACTIONS); //отобразить все скопом

То есть, что бы организовать анимацию, можно сначала подготовить невидимый кадр, а потом моментально его отобразить

... и многое другое...

//репозиторий проекта
http://code.google.com/p/studconsole/

Последний раз редактировалось _Bers; 24.12.2011 в 15:51.
_Bers вне форума Ответить с цитированием
Старый 24.12.2011, 15:40   #6
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Единственное, на чем я пока запарываюсь - это разобраться с stdin
Просто это мой первый опыт много-поточного программирования, и я конечно плаваю...

Все остальное - либо уже сделано, либо не представляет особой сложности.
_Bers вне форума Ответить с цитированием
Старый 24.12.2011, 20:39   #7
Сtrl
C++
Форумчанин
 
Аватар для Сtrl
 
Регистрация: 27.03.2011
Сообщений: 803
По умолчанию

Зачем извращаться и изменять std::cin, если можно сделать me::cin и внести минимальные изменения в клиентский код?
Ищете информацию по C++?
cplusplus.com
Сtrl вне форума Ответить с цитированием
Старый 24.12.2011, 20:59   #8
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от Сtrl Посмотреть сообщение
Зачем извращаться и изменять std::cin, если можно сделать me::cin и внести минимальные изменения в клиентский код?
Требования к проекту:
1. Полная совместимость с системной консолью.

зы:
Я не изменяю std::cin. Более того, в идеале я вообще не хочу трогать ни его, ни устройство из которого он читает.

Последний раз редактировалось _Bers; 24.12.2011 в 21:23.
_Bers вне форума Ответить с цитированием
Старый 25.12.2011, 02:39   #9
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

комментарии к требованиям:
1)лучше забыть о С++ std, создайте свои классы аналогичные этим(cout,cin,cerr).
можно даже отнаследоваться как нужно.
2)используйте мощь ООП.(хотя можно и без него, но почему бы и нет)

PS: не стоит добавлять излишней совместимости.
PPS: причина вылета может быть еще в том что cin один раз получил хендл на консоль, и больше его не меняет.
PPPS: немного подумав, решил что возвращается окошко по причине того что старый буфер то жив, и cin его возвращает обратно.
вы же не перенаправили stdin.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.

Последний раз редактировалось Пепел Феникса; 25.12.2011 в 02:48.
Пепел Феникса вне форума Ответить с цитированием
Старый 25.12.2011, 02:49   #10
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от Пепел Феникса Посмотреть сообщение
комментарии к требованиям:
1)лучше забыть о С++ std, создайте свои классы аналогичные этим(cout,cin,cerr).
можно даже отнаследоваться как нужно.
2)используйте мощь ООП.(хотя можно и без него, но почему бы и нет)

PS: не стоит добавлять излишней совместимости.
весь код, который работал с обычной консолью, должен без изменений работать и с моей консолью.
_Bers вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Баг или фича? (programmersforum) Сtrl О форуме и сайтах клуба 7 05.09.2011 21:20
Потоки. Использование стандартного ввода–вывода. Lazy_Bitch Общие вопросы C/C++ 1 05.05.2010 12:15
Перенаправление стандартного ввода/вывода в файл Роман Радер Общие вопросы C/C++ 1 09.01.2010 18:34
Как сделать запрет ввода букв в поле для ввода input zvezda_t PHP 1 27.12.2009 22:02
Баг Excel или я туплю? Raf_19 Microsoft Office Excel 5 06.08.2008 16:39