2017-01-23 63 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

相關問題