2017-01-23 58 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
在一般情况

下一个 - 在控制台输出涉及两个过程 - 您的应用程序(客户)和conhost.exe服务器)(由你的过程中产生的)(这是从Vista开始,在xp - 您的应用程序和+ csrss.exe

您的过程中的每个控制台功能(包括文本输出)都会对conhost.exe(通过内部ConsoleCallServer)进行远程(同步)调用。为理解为什么这种行为需要在当前实现调试conhost.exe

conhost.exe有3个主题:

  1. ConsoleIoThread线程 - 它与您的控制台 应用
  2. ConsoleInputThread沟通,这是控制台UI线程,自旋 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)

当你再次在服务器端调用printfConsoleIoThread(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

And ...有什么解决方案?只要等待一段时间,希望它不会太快?这看起来像一个非常糟糕的黑客攻击。 – tambre

+0

@tambre - 我没有看到很好的解决方案。这里我只描述什么/为什么会发生 – RbMm

+0

不是真的答案是吗?不知道为什么它被接受为答案,即使它甚至没有提出问题的解决方案,只是为什么问题首先发生。 – tambre

相关问题