2017-01-23 10 views
0

Я пишу консольное приложение с использованием WinAPI, и я заметил странное поведение
SetCurrentConsoleFontEx.Использование нескольких типов шрифтов в консольном приложении с помощью WinAPI

Приложение использует два типа шрифтов:

// small font 
CONSOLE_FONT_INFOEX font0; 
font0.cbSize = sizeof(CONSOLE_FONT_INFOEX); 
font0.nFont = 0; 
font0.dwFontSize = { 8, 16 }; 
font0.FontFamily = FF_DONTCARE; 
font0.FontWeight = FW_NORMAL; 
wcscpy_s(font0.FaceName, L"Consolas"); 

// large font 
CONSOLE_FONT_INFOEX font1; 
font1.cbSize = sizeof(CONSOLE_FONT_INFOEX); 
font1.nFont = 1; 
font1.dwFontSize = { 16, 32 }; 
font1.FontFamily = FF_DONTCARE; 
font1.FontWeight = FW_BOLD; 
wcscpy_s(font1.FaceName, L"Consolas"); 

SetCurrentConsoleFontEx(outHnd, FALSE, &font1) 
printf("This text is big!\n"); 

SetCurrentConsoleFontEx(outHnd, FALSE, &font0); 
printf("This text is small!\n"); 

После запуска приложения как текстовые строки выглядят одинаково (как font0).
Но если добавить Sleep(100) между printf("This text is big!\n")
и SetCurrentConsoleFontEx(outHnd, FALSE, &font0),
программа будет работать правильно (первый текст больше, чем 2).
Это также работает, когда я использую цикл задержки:

int i = 0; 
while (i < 100000000) 
    i++; 

Почему это происходит и как изменить шрифты без дополнительных функций задержки/петель?

+0

Каковы возвращение значения вызовов 'SetCurrentConsoleFontEx' в примере? – tambre

+0

@tambre Они всегда возвращаются 1. – dsonyy

+0

да, этот эффект присутствует. может подтвердить – RbMm

ответ

1

в общей ситуации следующий - в консоли вывода участвуют два процесса - приложение (Client) и conhost.exe (сервера) (созданный технологом) (это начинается с Vista, в xp - ваше приложение и + csrss.exe)

любая функция консоли, включая вывод текста в вашем процессе, делает удаленный (синхронный) вызов conhost.exe (через внутренний ConsoleCallServer). для понять, почему такое поведение нужно отладить conhost.exe

в текущей реализации conhost.exe есть 3 темы:

  1. ConsoleIoThread нить - это связь с вашей консолью приложения
  2. ConsoleInputThread это поток пользовательского интерфейса для консоли, спина в GetMessage loop
  3. Microsoft::Console::Render::RenderThread::_ThreadProc - это нить ожидание на каком-либо мероприятии (пусть назовите его m_hEvent). это событие установлено ConsoleIoThread, когда ваше приложение запрашивает некоторые действия, например текст вывод или изменение шрифта. выполнить это действие и начать снова ждать события (m_hEvent)

давайте посмотрим, что происходит, когда вы звоните SetCurrentConsoleFontEx на стороне сервера: ConsoleIoThread (1) пробуждать и делать дальше:

SrvSetConsoleCurrentFont 
    SCREEN_INFORMATION::UpdateFont(FontInfo*) 
    SCREEN_INFORMATION::RefreshFontWithRender 
     Microsoft::Console::Render::Renderer::TriggerFontChange(int, FontInfo*) 
     Microsoft::Console::Render::GdiEngine::UpdateFont(FontInfo*) 
     SetEvent(m_hEvent) 

кратко : создать/выбрать для контекста устройства новый шрифт и уведомить нить рендеринга (3) на SetEvent(m_hEvent)

при вызове printf на стороне сервера ConsoleIoThread (1) пробуждать и делать дальше:

SrvWriteConsole 
    DoSrvWriteConsole 
    WriteCharsLegacy 
     Microsoft::Console::Render::Renderer::TriggerRedraw(SMALL_RECT*) 
     Microsoft::Console::Render::GdiEngine::Invalidate(SMALL_RECT*) 
     SetEvent(m_hEvent) 

после этого вынести нить (3) пробуждать и визуализации текста с использованием текущего шрифта.

то, что будет, если вы делаете следующие вызовы слишком быстро:

SetCurrentConsoleFontEx(outHnd, FALSE, &font1) 
printf("This text is big!\n"); 
SetCurrentConsoleFontEx(outHnd, FALSE, &font0); 
printf("This text is small!\n"); 

ConsoleIoThread (1) называют UpdateFont(FontInfo*)два времени доRenderThread (3) является пробуждать! в результате второго вызова UpdateFont(FontInfo*) перезаписать первый вызов.

, но если вы делаете следующий:

SetCurrentConsoleFontEx(outHnd, FALSE, &font1) 
printf("This text is big!\n"); 
Sleep(1000);// or any unknown delay 
SetCurrentConsoleFontEx(outHnd, FALSE, &font0); 
printf("This text is small!\n"); 

RenderThread (3) является пробуждать в первый раз, когда вы будете ждать вSleep и не делают с font1, а затем уже делают с font0

+0

И ... Какое решение? Подождите некоторое время и надейтесь, что это не слишком быстро? Это похоже на * очень плохой взлом. – tambre

+0

@tambre - я не вижу хорошее решение. здесь я только описываю, что/почему происходит – RbMm

+0

На самом деле это не ответ? Не уверен, почему это принято в качестве ответа, даже если оно даже не предлагает решения вопроса, а именно, почему проблема возникает в первую очередь. – tambre