2

Is there any way to detect whether a console app is running with Windows 10's new features enabled?

This MSDN page shows that HKEY_CURRENT_USER\Console\ForceV2, HKEY_CURRENT_USER\Console\LineWrap and HKEY_CURRENT_USER\Console\{name}\LineWrap control it, but besides that being less robust to parse, it may not be correct. If the user switches to or from legacy mode, the change won't take effect until the console relaunches.

If I develop the app, I can do the check at startup. There could have been a race condition though, which renders the registry check useless for any practical use. I am curious what the solution would be for third party console windows.

jnm2
  • 7,960
  • 5
  • 61
  • 99
  • Why do you need to know?? These are conveniences to the user, they should not change the code you write. The api has not changed at all. – Hans Passant Dec 10 '15 at 19:54
  • @Hans that would be the theory, but it's not true. `Console.BufferHeight` in C# causes issues while the user is resizing the window because when the W10 features are enabled, the user is able to asynchronously change the window width which reflows the text and changes the buffer width and causes a race condition between the rect get, modify and set in `Console.BufferHeight`. Not to mention a few other issues. I'm getting both managed and unmanaged exceptions that should not be happening because buffer, window, and cursor operations are not atomic and interactive resizing really hammers it. – jnm2 Dec 10 '15 at 20:07
  • The basic thing that is going on is in my app is that the last line of the output acts as a sort of status bar. It gets overwritten with new messages. This is not an unusual thing to do. On W10 the number of lines it takes up changes when the window is resized and [there is no safe way to use the API](http://stackoverflow.com/a/34208406/521757) anymore to deal with window or buffer or cursor. Pre-W10, resizing the window blocked console API operations so the race condition problem did not exist. – jnm2 Dec 10 '15 at 20:13
  • Also: execute `Console.BufferHeight = 20` or some number after the console window width has been resized to fewer than 14 characters per line. Fun, right? – jnm2 Dec 10 '15 at 20:15
  • So if you can detect this then these problems are suddenly not problems anymore?? Very hard to believe but then just plain assume it is turned on then. Heck of an XY question. – Hans Passant Dec 10 '15 at 20:19
  • I can take a different code path. I'm asking the question because I'm curious, and sooner or later someone else will be, and it matters any time you're breathing near innocuous looking `Console` members. My example problem is only one example of why this is an interesting question; even solving my example problem will still leave me needing to know this. – jnm2 Dec 10 '15 at 20:24

1 Answers1

2

There seems to be no API for that, though I'd expect one to surface in some later SDK (maybe additional hyper-extended flags in GetConsoleMode).

Meanwhile, the following is a quick hack which attempts to detect the resize-wider capability of the new console, based on checking the ptMaxTrackSize.X value returned by GetMinMaxInfo.

The legacy console doesn't allow resizing the window wider than the screen buffer width, while the new one does. On the assumptions that (a) the console is running at full buffer width i.e. has no horizontal scrollbar, and (b) it's not already stretched to the full/max screen width, it's fairly straightforward to check whether the window allows itself to be resized wider (new console) or not (legacy console). Should be noted that assumption (a) could technically be avoided by manually converting the buffer width from characters to pixels, rather than relying on GetWindowRect, but assumption (b) is pretty much unavoidable.

This is the code (disclaimer: quick-and-dirty proof-of concept, no error checking etc).

int main()
{
    // largest possible console size for given font and desktop
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD cd = GetLargestConsoleWindowSize(hOut);
    SHORT nScrMaxXch = cd.X, 
        nScrMaxYch = cd.Y;

    // current and max console sizes for given screen buffer
    CONSOLE_SCREEN_BUFFER_INFOEX csbix = { sizeof(csbix) };
    GetConsoleScreenBufferInfoEx(hOut, &csbix);
    SHORT nWndXch = csbix.srWindow.Right - csbix.srWindow.Left + 1,
        nWndYch = csbix.srWindow.Bottom - csbix.srWindow.Top + 1;
    SHORT nWndMaxXch = csbix.dwMaximumWindowSize.X,
        nWndMaxYch = csbix.dwMaximumWindowSize.Y;

    wprintf(L"chars:   wnd-size %d %d, max-wnd-size %d %d, largest-size %d %d\n",
        nWndXch, nWndYch, nWndMaxXch, nWndMaxYch, nScrMaxXch, nScrMaxYch);

    // current window size
    HWND hWnd = GetConsoleWindow();
    RECT rc; GetWindowRect(hWnd, &rc);
    LONG nWndXpx = rc.right - rc.left,
        nWndYpx = rc.bottom - rc.top;

    // max window tracking size
    MINMAXINFO mmi = { 0 };
    SendMessage(hWnd, WM_GETMINMAXINFO, 0, (LPARAM)&mmi);
    LONG nWndMaxXpx = mmi.ptMaxTrackSize.x,
        nWndMaxYpx = mmi.ptMaxTrackSize.y;

    wprintf(L"pixels:  wnd-size %lu %lu, max-tracking-size %lu %lu\n",
        nWndXpx, nWndYpx, nWndMaxXpx, nWndMaxYpx);

    if (nWndXch == nWndMaxXch   // full buffer width, no h-scrollbar 
    && nWndXch < nScrMaxXch     // not already stretched to full screen width
    && nWndMaxXpx > nWndXpx)    // allowed to resized wider
        wprintf(L"\n...most likely a Win10 console with ForceV2 enabled\n");

    return 0;
}

This is the output when run in a legacy console.

chars:   wnd-size 80 25, max-wnd-size 80 71, largest-size 240 71
pixels:  wnd-size 677 443, max-tracking-size 677 1179

This is the output when run in the new console.

chars:   wnd-size 80 25, max-wnd-size 80 71, largest-size 239 71
pixels:  wnd-size 677 443, max-tracking-size 1936 1186

...most likely a Win10 console with ForceV2 enabled
dxiv
  • 16,984
  • 2
  • 27
  • 49