Option to display stderr in a different color #13032

Closed
opened 2026-01-31 03:31:51 +00:00 by claunia · 10 comments
Owner

Originally created by @stevenwdv on GitHub (Mar 14, 2021).

Description of the new feature/enhancement

I'm not sure if this has been requested before (I couldn't find it), but I think it would be nice to have the option in the settings to display error output (stderr) in a different color/style than normal output (stdout). For example, this would make it possible to automatically print make the text in std::cerr << "This is an error\n"; red.

I'm not sure how difficult this would be to realize, because normally GetStdHandle(STD_OUTPUT_HANDLE) and GetStdHandle(STD_ERROR_HANDLE) point to the same device. I'm also not sure how this would impact ANSI escape codes being sent to either stream affecting the other or not.

Additionally, this may impact the ability to invoke SetConsoleTextAttribute for the two handles separately, like e.g. this person tried to do.

Originally created by @stevenwdv on GitHub (Mar 14, 2021). <!-- 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨 I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING: 1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement. 2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement. 3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number). 4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement. 5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement. All good? Then proceed! --> # Description of the new feature/enhancement <!-- A clear and concise description of what the problem is that the new feature would solve. Describe why and how a user would use this new functionality (if applicable). --> I'm not sure if this has been requested before (I couldn't find it), but I think it would be nice to have the option in the settings to display error output (stderr) in a different color/style than normal output (stdout). For example, this would make it possible to automatically print make the text in `std::cerr << "This is an error\n";` red. I'm not sure how difficult this would be to realize, because normally `GetStdHandle(STD_OUTPUT_HANDLE)` and `GetStdHandle(STD_ERROR_HANDLE)` point to the same device. I'm also not sure how this would impact ANSI escape codes being sent to either stream affecting the other or not. Additionally, this may impact the ability to invoke `SetConsoleTextAttribute` for the two handles separately, like e.g. [this person tried to do](https://stackoverflow.com/q/4920661). <!--# Proposed technical implementation details (optional) <!-- A clear and concise description of what you want to happen. -->
Author
Owner

@skyline75489 commented on GitHub (Mar 14, 2021):

Don't mean to be rude or anything. But I think even on Linux, the output in stderr has the exact same color as stdout. This feels a problem that should be solved on the application side, rather than the terminal side.

@skyline75489 commented on GitHub (Mar 14, 2021): Don't mean to be rude or anything. But I think even on Linux, the output in stderr has the exact same color as stdout. This feels a problem that should be solved on the application side, rather than the terminal side.
Author
Owner

@stevenwdv commented on GitHub (Mar 15, 2021):

@skyline75489 Well it wouldn't be a real feature request if it was already implemented, someone has to be the first to do it, and if the goal is to just do what Unix does, than why not just use that instead of Windows 😉 But if it's really not something you're interested in than I guess I'll have to find some other way to do this.
The problem is that, as far as I know, at the moment it is hard for applications to do this, as the two handles are identical so changing color on one also changes color on the other. Hence, every print statement would need to start with setting the color.
I think it would be useful to be able to style error output differently, also for existing applications that give normal output and errors the same color, making errors not stand out.

By the way, I didn't see that this repo contains both the classic and the new terminal. I created this request mostly for the new one.

@stevenwdv commented on GitHub (Mar 15, 2021): @skyline75489 Well it wouldn't be a real feature request if it was already implemented, someone has to be the first to do it, and if the goal is to just do what Unix does, than why not just use that instead of Windows 😉 But if it's really not something you're interested in than I guess I'll have to find some other way to do this. The problem is that, as far as I know, at the moment it is hard for applications to do this, as the two handles are identical so changing color on one also changes color on the other. Hence, every print statement would need to start with setting the color. I think it would be useful to be able to style error output differently, also for **existing applications** that give normal output and errors the same color, making errors not stand out. By the way, I didn't see that this repo contains both the classic and the new terminal. I created this request mostly for the new one.
Author
Owner

@zadjii-msft commented on GitHub (Mar 15, 2021):

Yea, I think at the end of the day, this isn't going to be possible. The terminal and the vintage console both use the same underlying APIs, that's what lets legacy apps work decently well in the Terminal. When the console subsystem hooks up the console to a client application, it is literally hooking up the same pointer to both the client's stderr and stdout handles. So when the client app is using different handles to write to the console, they're both coming in to the exact same place.

I suppose (very hypothetically) we could have the console return a non-pointer for those values, and then look up if it was supposed to be the stdout handle or the stderr one. Gah no, that won't work. Again, the subsystem only ever returns a single value for a single console. The way the Console API is designed, there's just no way for the console to be able to differentiate between API calls made on stdout vs stderr.

So I'm gonna close this as "cool idea, but technically impossible". Sorry about that!

@zadjii-msft commented on GitHub (Mar 15, 2021): Yea, I think at the end of the day, this isn't going to be possible. The terminal and the vintage console both use the same underlying APIs, that's what lets legacy apps work decently well in the Terminal. When the console subsystem hooks up the console to a client application, it is literally hooking up the same pointer to both the client's stderr and stdout handles. So when the client app is using different handles to write to the console, they're both coming in to the exact same place. ~~I suppose (_very hypothetically_) we could have the console return a non-pointer for those values, and then look up if it was supposed to be the stdout handle or the stderr one.~~ Gah no, that won't work. Again, the subsystem only ever returns a single value for a single console. The way the Console API is designed, there's just no way for the console to be able to differentiate between API calls made on stdout vs stderr. So I'm gonna close this as "cool idea, but technically impossible". Sorry about that!
Author
Owner

@stevenwdv commented on GitHub (Mar 15, 2021):

@zadjii-msft Okay that's a pity but no worries! I'm curious: why do both STD_OUTPUT_HANDLE and STD_ERROR_HANDLE exist then? And are there cases where they do have different values? (Or alternatively: where can I find an answer to that question?)

@stevenwdv commented on GitHub (Mar 15, 2021): @zadjii-msft Okay that's a pity but no worries! I'm curious: why do both `STD_OUTPUT_HANDLE` and `STD_ERROR_HANDLE` exist then? And are there cases where they do have different values? (Or alternatively: where can I find an answer to that question?)
Author
Owner

@zadjii-msft commented on GitHub (Mar 15, 2021):

You know, that's a question that probably exceeds my knowledge. I believe shells are capable of redirecting the out and err of a client app separately, so you can separate out text that's written to one or the other. I'm pretty sure that in this case, the shell is passing in a different handle to the client process, so the shell actually can differentiate between the two streams.

Again, we're at the edge of my expertise here. I'm sure if I'm wrong on the internet, someone will correct me 😆

@zadjii-msft commented on GitHub (Mar 15, 2021): You know, that's a question that probably exceeds my knowledge. I believe shells are capable of redirecting the out and err of a client app separately, so you can separate out text that's written to one or the other. I'm pretty sure that in this case, the shell is passing in a different handle to the client process, so the shell actually _can_ differentiate between the two streams. Again, we're at the edge of my expertise here. I'm sure if I'm wrong on the internet, someone will correct me 😆
Author
Owner

@DHowett commented on GitHub (Mar 15, 2021):

No, that's quite correct. It's also possible to redirect only one of them -- to have output go to the screen but log errors to a file, or the other way around (which is what you typically want when you redirect the output of a tool to a file).

It is possible for the console to differentiate output and error streams, but it would require some fairly invasive changes in kernel32, as kernel32 is responsible for setting up the console client handles. This would also put it in the "will take 6-18 months to get released" category, as well as the "would require more resources for normal applications, on the order of one more open file handle for every application ever run" category.

Cool idea, but can be easily handled with a separate utility that hooks up stdout/stderr the way it pleases before launching another application.

PowerShell, for example, already does something with stderr. Perhaps it would be worth investigating it on their end?

@DHowett commented on GitHub (Mar 15, 2021): No, that's quite correct. It's also possible to redirect only one of them -- to have output go to the screen but log errors to a file, _or the other way around_ (which is what you typically want when you redirect the output of a tool to a file). It _is_ possible for the console to differentiate output and error streams, but it would require some fairly invasive changes in kernel32, as kernel32 is responsible for setting up the console client handles. This would also put it in the "will take 6-18 months to get released" category, as well as the "would require more resources for normal applications, on the order of _one more open file handle for every application ever run_" category. Cool idea, but can be easily handled with a separate utility that hooks up stdout/stderr the way it pleases before launching another application. PowerShell, for example, already does _something_ with stderr. Perhaps it would be worth investigating it on their end?
Author
Owner

@stevenwdv commented on GitHub (Mar 15, 2021):

@DHowett Wait, so actually how is it then already possible that one of the streams can be redirected (.\app 2> nul)? Also, when executing SetConsoleTextAttribute for the redirected handle it has no effect. Indeed, the handle values seem to actually differ which I thought was not the case, so I'm confused. And how exactly is kernel32 involved in this? Doesn't the terminal pass handles itself via STARTUPINFO? (Or do you mean when you start a console app without opening a terminal first?)

PowerShell, for example, already does something with stderr.

What do you mean by this?

I hope I'm not keeping you busy too many questions... 😬

@stevenwdv commented on GitHub (Mar 15, 2021): @DHowett Wait, so actually how is it then already possible that one of the streams can be redirected (`.\app 2> nul`)? Also, when executing `SetConsoleTextAttribute` for the redirected handle it has no effect. Indeed, the handle values seem to actually differ which I thought was not the case, so I'm confused. And how exactly is kernel32 involved in this? Doesn't the terminal pass handles itself via `STARTUPINFO`? (Or do you mean when you start a console app without opening a terminal first?) > PowerShell, for example, already does _something_ with stderr. What do you mean by this? I hope I'm not keeping you busy too many questions... 😬
Author
Owner

@DHowett commented on GitHub (Mar 15, 2021):

Nah, it's no problem.

So, every process has three handle slots (input, output, error). During kernel32's initialization³, our library (conclnt ("console client")) checks the subsystem of the current process's image and does a couple things if it's SUBSYSTEM_CONSOLE. The function that handles that is called ConsoleInitialize, and it:

  • Checks residency for the input, output and error handle slots
    • If all three of these are filled (by either explicit startup info fields or inheritance), ConsoleInitialize returns.
    • If any one of those slots is empty, it continues.
  • Checks whether it's been spawned with a "Console Reference" handle¹
    • If it hasn't been spawned with a console reference handle, it opens a connection to the console driver (condrv) and launches a console host (conhost) to service that connection.
  • It derives only two handles from the console driver connection: Input and Output.
  • It hooks up input->input, output->output and error->output. These slots are only filled if they were otherwise empty².

³ It is convenient, then, that kernel32 is loaded into almost every process.
¹ Terminal spawns processes with a console reference handle, not stdin/out/err.
² This is why redirection works. When you spawn app 2>nul, your handle table is one of the following two options:

In        Out        Error
inherit | inherit  | \\.\GLOBALROOT\Device\Null
<empty> | <empty>  | \\.\GLOBALROOT\Device\Null
@DHowett commented on GitHub (Mar 15, 2021): Nah, it's no problem. So, every process has three handle slots (input, output, error). During kernel32's initialization³, our library (conclnt ("console client")) checks the subsystem of the current process's image and does a couple things if it's `SUBSYSTEM_CONSOLE`. The function that handles that is called `ConsoleInitialize`, and it: * Checks residency for the input, output and error handle slots * If all three of these are filled (by either explicit startup info fields or inheritance), `ConsoleInitialize` returns. * If _any one_ of those slots is empty, it continues. * Checks whether it's been spawned with a "Console Reference" handle¹ * If it hasn't been spawned with a console reference handle, it opens a connection to the console driver (condrv) and launches a console host (conhost) to service that connection. * It derives _only two handles_ from the console driver connection: `Input` and `Output`. * It hooks up input->input, output->output _and error->output_. These slots are only filled if they were otherwise empty². ³ It is convenient, then, that kernel32 is loaded into almost every process. ¹ Terminal spawns processes with a console reference handle, not stdin/out/err. ² This is why redirection works. When you spawn `app 2>nul`, your handle table is one of the following two options: ``` In Out Error inherit | inherit | \\.\GLOBALROOT\Device\Null <empty> | <empty> | \\.\GLOBALROOT\Device\Null ```
Author
Owner

@DHowett commented on GitHub (Mar 15, 2021):

(And when you've redirected the error handle, it is no longer a handle derived from the console connection. Console APIs will not work against it, because it isn't serviced by the console driver.)

@DHowett commented on GitHub (Mar 15, 2021): (And when you've redirected the error handle, it is no longer a handle derived from the console connection. Console APIs will not work against it, because it isn't serviced by the console driver.)
Author
Owner

@stevenwdv commented on GitHub (Mar 16, 2021):

@DHowett Thanks so much, that was really insightful!

@stevenwdv commented on GitHub (Mar 16, 2021): @DHowett Thanks so much, that was really insightful!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#13032