{"id":12283,"date":"2010-11-15T07:00:00","date_gmt":"2010-11-15T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2010\/11\/15\/the-program-running-in-a-console-decides-what-appears-in-that-console\/"},"modified":"2010-11-15T07:00:00","modified_gmt":"2010-11-15T07:00:00","slug":"the-program-running-in-a-console-decides-what-appears-in-that-console","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20101115-00\/?p=12283","title":{"rendered":"The program running in a console decides what appears in that console"},"content":{"rendered":"<p>\nJames Risto asks,\n&#8220;<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/pages\/407234.aspx#2374944\">Is there a way to change the behavior of the CMD.EXE window?\nI would like to add a status line<\/a>.&#8221;\n<\/p>\n<p>\nThe use of the phrase &#8220;the CMD.EXE window&#8221; is ambiguous.\nJames could be referring to the console itself, or he could be\nreferring to the CMD.EXE progarm.\n<\/p>\n<p>\nThe program running in a console decides what appears in the console.\nIf you want to devote a line of text to a status bar, then feel free\nto code one up.\nBut if you didn&#8217;t write the program that&#8217;s running,\nthen you&#8217;re at the mercy of whatever that program decided to display.\n<\/p>\n<p>\nJust to show that it can be done, here&#8217;s a totally useless console\nprogram that contains a status bar.\n<\/p>\n<pre>\n#define UNICODE\n#define _UNICODE\n#include &lt;windows.h&gt;\n#include &lt;strsafe.h&gt; \/\/ for StringCchPrintf\nvoid DrawStatusBar(HANDLE hScreen)\n{\n CONSOLE_SCREEN_BUFFER_INFO sbi;\n if (!GetConsoleScreenBufferInfo(hScreen, &amp;sbi)) return;\n TCHAR szBuf[80];\n StringCchPrintf(szBuf, 80, TEXT(\"Pos = %3d, %3d\"),\n                 sbi.dwCursorPosition.X,\n                 sbi.dwCursorPosition.Y);\n DWORD dwWritten;\n COORD coDest = { 0, sbi.srWindow.Bottom };\n WriteConsoleOutputCharacter(hScreen, szBuf, lstrlen(szBuf),\n    coDest, &amp;dwWritten);\n}\n<\/pre>\n<p>\nOur lame-o status bar consists of the current cursor position.\nNotice that the console subsystem does not follow the GDI convention\nof\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/02\/18\/75652.aspx\">\nendpoint-exclusive rectangles<\/a>.\n<\/p>\n<pre>\nint __cdecl wmain(int argc, WCHAR **argv)\n{\n HANDLE hConin = CreateFile(TEXT(\"CONIN$\"),\n                            GENERIC_READ | GENERIC_WRITE,\n                            FILE_SHARE_READ | FILE_SHARE_WRITE,\n                            NULL, OPEN_EXISTING, 0, NULL);\n if (hConin == INVALID_HANDLE_VALUE) return 1;\n HANDLE hConout = CreateFile(TEXT(\"CONOUT$\"),\n                       GENERIC_READ | GENERIC_WRITE,\n                       FILE_SHARE_READ | FILE_SHARE_WRITE,\n                       NULL, OPEN_EXISTING, 0, NULL);\n if (hConout == INVALID_HANDLE_VALUE) return 1;\n<\/pre>\n<p>\nWe start by getting the handles to the current console.\nSince we are a fullscreen program, we don&#8217;t rely on stdin and stdout.\n(How do you position the cursor on a redirected output stream?)\n<\/p>\n<pre>\n HANDLE hScreen = CreateConsoleScreenBuffer(\n                       GENERIC_READ | GENERIC_WRITE,\n                       0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);\n if (!hScreen) return 1;\n SetConsoleActiveScreenBuffer(hScreen);\n<\/pre>\n<p>\nWe create a new screen buffer and switch to it, so that our\nwork doesn&#8217;t disturb what was previously on the screen.\n<\/p>\n<pre>\n DWORD dwInMode;\n GetConsoleMode(hConin, &amp;dwInMode);\n<\/pre>\n<p>\nWe start by retrieving the original console input mode\nbefore we start fiddling with it,\nso we can restore the mode when our program is finished.\n<\/p>\n<pre>\n SetConsoleCtrlHandler(NULL, TRUE);\n SetConsoleMode(hConin, ENABLE_MOUSE_INPUT |\n                        ENABLE_EXTENDED_FLAGS);\n<\/pre>\n<p>\nWe set our console control handler to <code>NULL<\/code>\n(which means &#8220;don&#8217;t terminate on Ctrl+C&#8221;)\nand enable mouse input on the console because we&#8217;re going to\nbe tracking the mouse position in our status bar.\n<\/p>\n<pre>\n CONSOLE_SCREEN_BUFFER_INFO sbi;\n if (!GetConsoleScreenBufferInfo(hConout, &amp;sbi)) return 1;\n COORD coDest = { 0, sbi.srWindow.Bottom - sbi.srWindow.Top };\n DWORD dw;\n FillConsoleOutputAttribute(hScreen,\n     BACKGROUND_BLUE |\n     FOREGROUND_BLUE | FOREGROUND_RED |\n     FOREGROUND_GREEN | FOREGROUND_INTENSITY,\n     sbi.srWindow.Right - sbi.srWindow.Left + 1,\n     coDest, &amp;dw);\n<\/pre>\n<p>\nWe retrieve the screen buffer dimensions and draw a blue status\nbar at the bottom of the screen.\nNotice that the endpoint-inclusive rectangles employed by the\nconsole subsystem result in what look like off-by-one errors.\nThe bottom line of the screen is <code>Bottom - Top<\/code>,\nwhich in an endpoint-exclusive world would be the height of the\nscreen, but since the rectangle is endpoint-inclusive,\nthis is actually the height of the screen <i>minus&nbsp;1<\/i>,\nwhich puts us at the bottom line of the screen.\nSimilarly <code>Right - Left<\/code> is the width of the screen\n<i>minus&nbsp;1<\/i>, so we have to add one back to get the width.\n<\/p>\n<pre>\n DrawStatusBar(hScreen);\n<\/pre>\n<p>\nDraw our initial status bar.\n<\/p>\n<pre>\n INPUT_RECORD ir;\n BOOL fContinue = TRUE;\n while (fContinue &amp;&amp; ReadConsoleInput(hConin, &amp;ir, 1, &amp;dw)) {\n  switch (ir.EventType) {\n  case MOUSE_EVENT:\n   if (ir.Event.MouseEvent.dwEventFlags &amp; MOUSE_MOVED) {\n     SetConsoleCursorPosition(hScreen,\n         ir.Event.MouseEvent.dwMousePosition);\n     DrawStatusBar(hScreen);\n   }\n   break;\n  case KEY_EVENT:\n   if (ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) {\n    fContinue = FALSE;\n   }\n   break;\n  }\n }\n<\/pre>\n<p>\nThis is the console version of a &#8220;message loop&#8221;:\nWe read input events from the console and respond to them.\nIf the mouse moves, we move the cursor to the mouse position and\nupdate the status bar.\nIf the user hits the Escape key, we exit the program.\n<\/p>\n<pre>\n SetConsoleMode(hConin, dwInMode);\n SetConsoleActiveScreenBuffer(hConout);\n return 0;\n}\n<\/pre>\n<p>\nAnd when the program ends, we clean up: Restore the original\ninput mode and restore the original screen buffer.\n<\/p>\n<p>\nIf you run this program, you&#8217;ll see a happy little status bar\nat the bottom whose contents continuously reflect the cursor\nposition, which you can move by just waving the mouse around.\n<\/p>\n<p>\nIf you want a status bar in your console program,\ngo ahead and draw it yourself.\nOf course, since it&#8217;s a console program, your status bar\nis going to look console-y since all you have to work with\nare rectangular character cells.\nMaybe you can make use of those fancy line-drawing characters.\nParty like it&#8217;s 1989!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>James Risto asks, &#8220;Is there a way to change the behavior of the CMD.EXE window? I would like to add a status line.&#8221; The use of the phrase &#8220;the CMD.EXE window&#8221; is ambiguous. James could be referring to the console itself, or he could be referring to the CMD.EXE progarm. The program running in a [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[26],"class_list":["post-12283","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>James Risto asks, &#8220;Is there a way to change the behavior of the CMD.EXE window? I would like to add a status line.&#8221; The use of the phrase &#8220;the CMD.EXE window&#8221; is ambiguous. James could be referring to the console itself, or he could be referring to the CMD.EXE progarm. The program running in a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12283","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=12283"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/12283\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=12283"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=12283"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=12283"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}