6

So I thought the window resize event would come through winproc, I might be mistaken, looking to get notified for a Console resize event.

I want to maximize the console buffer on resize, and once it's finished essentially shrink it back down to the window size, thus preventing an overflow error due to the buffer being smaller than the window.

David Torrey
  • 1,335
  • 3
  • 20
  • 43
  • Is this a solution for you: https://msdn.microsoft.com/en-us/library/system.console.setwindowsize(v=vs.110).aspx (maybe all of Console members: https://msdn.microsoft.com/en-us/library/System.Console(v=vs.110).aspx) – Thus Spoke Nomad Jul 17 '15 at 03:30
  • 1
    unfortunately the setwindowsize doesnt work because it will cause the buffer to be smaller than the window, causing an error (on window expand) – David Torrey Jul 17 '15 at 03:34
  • 1
    what i need is to get access to the resize when it first happens to adjust the buffer and prevent an error. I could technically do a really hacky version which anticipates the exception, sets the buffer to max, then checks to see if the buffer size and window size are different and fixes it, but i'm trying to avoid that – David Torrey Jul 17 '15 at 03:35
  • 1
    Maybe clues [here](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682079.aspx). You might have to use P/Invoke. – Blorgbeard Jul 17 '15 at 03:51
  • You've asked the wrong question. You don't care about window procs. You want to receive resize notification. Please edit to clarify. – David Heffernan Jul 17 '15 at 05:08
  • doesn't the resize notification come through winproc? – David Torrey Jul 17 '15 at 11:36
  • @DavidTorrey I'm having the exact same race condition on resize. It's worse on Windows 10 because resizing the window reflows the text and changes the buffer width as well as the window width. The [`WndProc` is in a separate process](http://stackoverflow.com/a/13438939/521757) and is not going to be hookable. Spy++ can't even hook Console window messages because of desktop security. baboulaf's answer is wasteful and inaccurate. I think the *right* way to do it must be the [Console Input Buffer](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682079.aspx) which Blorgbeard also found. – jnm2 Dec 10 '15 at 16:47

3 Answers3

10

Unfortunately, the answer is you can't hook the console's WndProc because it's in a separate, security-controlled process. Spy++ can hook other processes' WndProcs to read window messages, and Spy++ can't even read console window messages. Even if you could work around the security issue, C# cannot be used to hook another process.

I'm having the exact same race condition on resize. It's worse on Windows 10 because resizing the window reflows the text and changes the buffer width as well as the window width. Console.BufferWidth, Console.BufferHeight and family are non-atomic and will throw both managed and unmanaged errors if you use them while the window is being resized.

You can definitely find out about resizes after the fact by Reading Input Buffer Events but that won't solve your problem. You'll still have concurrency issues. Since that's not a hook, you can't make the resize wait for you to finish the Console class's non-atomic buffer/window size operations.

I think the only option to deal with the race condition is a retry loop, and that is what I will use.

while (true) try
{
    // Read and write the buffer size/location and window size/location and cursor position,
    // but be aware it will be rudely interrupted if the console is resized by the user.
}
catch (IOException) { }
catch (ArgumentOutOfRangeException) { }
Community
  • 1
  • 1
jnm2
  • 7,960
  • 5
  • 61
  • 99
0

1# You'll need to get a reference to the console window, there's various ways of doing that: https://support.microsoft.com/en-us/kb/124103

2# You'll need to hook your WndProc using setwindowshookex SetWindowsHookEx in C#

3# Handle the WM_SIZE message inside your WndProc https://msdn.microsoft.com/en-us/library/windows/desktop/ms632646(v=vs.85).aspx

Its important to note that in the SetWindowHookEx example, the guy called CallNextHookEx, thats because hooks are chained.

Another full example http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx where he gets keyboard messages, but you can do the same to catch the size events.

One more Capture all Windows Messages

Community
  • 1
  • 1
Alexandre Borela
  • 1,576
  • 13
  • 21
  • you should be able to get the console window's handle, at which point you can intercept messages manipulating it, I just don't have a ton of experience doing it – David Torrey Jul 17 '15 at 11:46
  • in fact, I've already gotten the handle and adjusted other aspects of the window, just can't figure out how to listen for the resize – David Torrey Jul 17 '15 at 11:47
  • Thats the point, what you are doing defeats the purpose of console applications, you can get the buffer size Console.BufferWidth and Console.BufferHeight, but console apps only deals with standard streams that can be chained and redirected, that's why you must never assume that your app is running in a console like cmd.exe. – Alexandre Borela Jul 17 '15 at 11:49
  • Why don't you write the application in Winforms or WPF (http://www.codeproject.com/Articles/247280/WPF-Command-Prompt)? – Alexandre Borela Jul 17 '15 at 11:51
  • I get what you're coming at, I still want to be able to detect if it's in fact a console program running in a window and be able to receive events accordingly – David Torrey Jul 17 '15 at 11:56
  • Alright, just a second, I did these kind of hacks in the past, but its not pretty. – Alexandre Borela Jul 17 '15 at 12:01
  • @AlexandreBorela This does not work. The console window [belongs to a different process](http://stackoverflow.com/a/13438939/521757) and C# is not able to hook it. Even Spy++ is not able to view the messages. Also, as the last link you posted says, C# is also not able to hook global windows messages. – jnm2 Dec 10 '15 at 16:38
  • Also it seems the .NET Framework already provides a reliable way to determine the console's window handle: `Process.GetCurrentProcess().MainWindowHandle`. Better than searching for windows by window title as Microsoft (!) suggests in your first link. @HansPassant, [what is this](https://support.microsoft.com/en-us/kb/124103)? – jnm2 Dec 10 '15 at 17:20
  • Console programs don't have windows so MainWindowHandle won't work, console programs only deals with standard streams (input, output and error), they can run in the background without any kind of command prompt and that's why those hacks are required and that's why I said it would be ugly. if you are not able to hook to the window and make changes to it, check if you have the necessary privileges, but anyway, try to avoid these hacks, console programs should only deal with standard streams. – Alexandre Borela Dec 10 '15 at 18:35
  • Also, the ideal solution is to create a command prompt from scratch and embed in your software if you really need these functions, e.g.: http://www.codeproject.com/Articles/9621/ShellControl-A-console-emulation-control – Alexandre Borela Dec 10 '15 at 18:39
-1

You can listen and capture the Resize event with a thread :

class Program
{
    private static bool _listnerOn;
    static void Main(string[] args)
    {
        Thread listner = new Thread(EventListnerWork);

        listner.Start();

        Console.WriteLine("Press a key to exit...");
        Console.ReadKey(true);

        _listnerOn = false;
        listner.Join();
    }

    static void ConsoleResizeEvent(int height, int width)
    {
        Console.WriteLine("Console Resize Event");
    }

    static void EventListnerWork()
    {
        Console.WriteLine("Listner is on");
        _listnerOn = true;
        int height = Console.WindowHeight;
        int width = Console.WindowWidth;
        while (_listnerOn)
        {
            if (height != Console.WindowHeight || width != Console.WindowWidth)
            {
                height = Console.WindowHeight;
                width = Console.WindowWidth;
                ConsoleResizeEvent(height,width);
            }

            Thread.Sleep(10); 
        }
        Console.WriteLine("Listner is off");

    }

}
baboulaf
  • 15
  • 2
  • 6
    Not a good solution. The busy loop will require a thread, eat up CPU and is prone to all kinds of race conditions. You should not be obtaining `Console.WindowHeight` and `Console.WindowWidth` in six different places; there is no guarantee that it won't have changed in between reads which makes the `if` logic faulty. You need p/invoke to read the height and width both at once and you need to write the logic atomically and even then you will randomly miss brief intermediate sizes or quick changes. – jnm2 Dec 10 '15 at 16:43