Resize Event for Console
Asked Answered
M

3

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.

Millhon answered 17/7, 2015 at 3:26 Comment(7)
Is this a solution for you: msdn.microsoft.com/en-us/library/… (maybe all of Console members: msdn.microsoft.com/en-us/library/System.Console(v=vs.110).aspx)Debauch
unfortunately the setwindowsize doesnt work because it will cause the buffer to be smaller than the window, causing an error (on window expand)Millhon
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 thatMillhon
Maybe clues here. You might have to use P/Invoke.Languishment
You've asked the wrong question. You don't care about window procs. You want to receive resize notification. Please edit to clarify.Chronometry
doesn't the resize notification come through winproc?Millhon
@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 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 which Blorgbeard also found.Shien
S
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) { }
Shien answered 10/12, 2015 at 17:42 Comment(0)
M
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

Me answered 17/7, 2015 at 11:45 Comment(10)
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 itMillhon
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 resizeMillhon
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.Me
Why don't you write the application in Winforms or WPF (codeproject.com/Articles/247280/WPF-Command-Prompt)?Me
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 accordinglyMillhon
Alright, just a second, I did these kind of hacks in the past, but its not pretty.Me
@AlexandreBorela This does not work. The console window belongs to a different process 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.Shien
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?Shien
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.Me
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.: codeproject.com/Articles/9621/…Me
C
-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");

    }

}
Carpetbag answered 19/8, 2015 at 17:35 Comment(1)
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.Shien

© 2022 - 2024 — McMap. All rights reserved.