Skip to content

Updating rktio_console.c and windows-rand.rkt for better Win32 support - #5577

Open
ndykman wants to merge 18 commits into
racket:masterfrom
ndykman:rktio-console-update
Open

ndykman wants to merge 18 commits into
racket:masterfrom
ndykman:rktio-console-update

Conversation

@ndykman

@ndykman ndykman commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • Bugfix
  • Feature
  • tests included
  • documentation

Description of change

The current logic that creates a Win32 console (needed for some GUI applications), creates a window that needed to be closed manually when the program exited. In old versions of Windows, this could be the best way to handle console redirection in a GUI application. However, Windows 2000 introduced the AllocConsole and FreeConsole APIs making management of the console much simpler. The new code for creating a console uses those functions. It also sets up the console input and output to better handle virtual terminal instructions, making it work like a modern Linux terminal. It also hides the console window, reducing clutter in the task bar and confusion when the closing the console window also closes the main window.

Alongside this, support for handling CRTL+C and CRTL-BREAK was added. CRTL-BREAK is handled as a SIGTERM event.

As part of overall Win32 improved support, windows-rand.rkt was updated to use the modern cryptology provider for generating random bytes.

@mflatt

mflatt commented Sep 11, 2026

Copy link
Copy Markdown
Member

I think this change doesn't do what's intended in the current implementation. The current intent is that when you're running a GUI application that doesn't have a console window initially, output to stderr or stdout isn't simply lost. Instead, a console window is created as needed to show the output. This is important when something goes wrong starting up a GUI program: Without sending error output somewhere, it can be difficult to know what has gone wrong.

As a minimal example, try putting #lang racket "hi" in hi.rkt, and then run it with GRacket: racket\lib\gracket hi.rkt. A console should appear to show the output. Since GRacket.exe is a Windows application (as opposed to a console application), its output will not go to the command prompt where GRacket.exe is started.

The unusual callbacks on the create console window are to prevent the Racket process from being killed by closing the console window, which is helpful if the application keeps running after writing output.

@ndykman

ndykman commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Okay, I see the disconnect here. That behavior (showing a console window) is not what modern GUI applications do. The assumption is that you are not launching a GUI application from a console window.

In the case of macOS, stdout and stderr are redirected to the system log, unless the open command is used and then it is redirected to the stdout and stderr of the Terminal app. It is not expected to able to pipe stdin into a GUI application.

In Windows, one can open a console window to allow for stdin/stdout/stderr to be redirected as needed, but it is not shown. Again, the assumption is that error handling or other output is handled by using logs and so on.

In the case of Linux, it's all over the map in where stdout or stderr is redirected (if it is at all) when a GUI application is not launched from a pty session. Again, the major GUI frameworks assume that all stdio is not available.

Also, in your example, a variation of it doesn't work as expected. Now, my code doesn't fix it either, but it points to a problem.

D:\racket\GRacket.exe C:\temp\test.rkt > C:\temp\output.txt

This shows a console window about an error writing to stream port (the pipe is being closed). In this case, I think opening a console window broke the existing redirects.

Yes, this is a change and I know the desire to keep things as is, but it is how GUI applications should work and the current version leads to more confusion to the average programmer and user right now.

@mflatt

mflatt commented Sep 12, 2026 •

Copy link
Copy Markdown
Member

I don't think we're on the same page, yet.

"GRacket.exe" identifies itself as a GUI program, and so Windows treats it that way — so far, so good. And just to be clear, "GRacket.exe" here is a proxy for the output of raco exe --gui, not just the "Gracket.exe" executable. The output of raco exe --gui is similarly tagged as a GUI executable (i.e., the subsystem byte in the PE header says "windows" instead of "console").

On Unixen like Mac OS and Linux, it's not the GUI programs are always started with a particular redirected output. Running in a shell always supplies the shell's input and output to the started program, whether it has associated data to indicate that it's a GUI program or not. That's a property of the shell, but practically all shells behave that way. True, Finder on Mac OS and similar on Linux do different things when they start programs, but there's an established and simple way to see a program's output if you need to: run it in a shell. (FWIW, you have the open command backwards: it's a way of running a GUI program from a shell, but in the Finder way.)

On Windows, cmd.exe starts console and GUI programs differently. It starts a GUI programs with no handles — unless there's a file redirection, in which case a file handle is provided to the program. (In other words, your example with "GRacket.exe" and redirection does work in Command Prompt.) Of course, Explorer also starts a GUI program that way, and never with redirection. For PowerShell, I can't really tell what it's doing with redirection to a file; I think it's setting up some intermediary, and maybe there's something to be fixed in that case. PowerShell meanwhile provides the full generality of Start-Process with -RedirectStandardInput and similar options, but that doesn't seem like something you'd want to type out to make stderr output visible.

There are two key points to the Windows difference:

  • There's not a simple way to direct to/from the console itself in the case of using Command Prompt (cmd.exe) or PowerShell to start a program. (Well, there's the old trick of supplying both "X.exe" and "X.com". Racket users can do that if they want by using raco exe twice, but let's not go there.)

  • Unlike a Finder-like program on Unixen directing a program's output to a file or log, when Windows starts a GUI program without handles, the program can tell that it was started without output going anywhere. Since the program can detect that situation, it can make its own place.

So, that's what's happening with "GRacket.exe" currently. It is a GUI program that should not normally write to stdout and stderr. But when it does, rktio can be helpful by noticing there's no destination for output and create a console dynamically. Historically, that was important for letting students see error messages to diagnose problems, because there wasn't an easy way to see errors otherwise. I think it remains useful for Racket programmers in general.

Edit: I forgot one of the big use cases, which is seeing errors to diagnose an installation problem when DrRacket fails to start.

@ndykman

ndykman commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the clarification on how open works on macOS. I should note that open also has flags to redirect stdout and stderr to files. This is similar to Start-Process in PowerShell as you noted.

My perspective here is that of a DrRacket user on Windows. When I use the tool as an end user/programmer, I don't want to chase down a console window that was dynamically opened because stdio was used somewhere. And when it is a window that can't be closed until you close the associated DrRacket instance, that's extra insult to injury. I don't want to then chase down which DrRacket window that I have to close if I have multiple copies of the DrRacket tool open only to find I guessed the wrong one and boom, I have to restart them both.

As an end user, I don't care about the error details. If the application has to crash, then have it crash as politely as possible. Don't leave stray processes around. Don't make me chase down why a file is still locked by what process because I have a console window lurking around that I didn't notice.

Yes, I know this is a Windows thing, but it is still a thing. I think this is a reasonable expectation for most users to have.

Giving newer developers the impression that stdio is always there and can be easily captured will set them up for problems if they develop GUI applications on macOS, iOS, Android, Windows and even Linux. Modern GUI applications use logging frameworks for very good reasons. Just printing to stdout and stderr and hoping some tool picks it up isn't really workable, even if it is standard practice on server-side programs.

I do understand the need for additional diagnostics for internal developers. But, redirecting stderr and stdout is quite doable on macOS and Windows. Yes, PowerShell is quirky as heck compared to cmd.exe, but it's okay for power users to understand how redirection works in PowerShell. If you need to see stdout and stderr, this works a treat in Powershell (even if 2> does not).

.\DrRacket.exe 2>&1 | Tee-Object -FilePath output.txt

This will open the application, pipe stdout and stderr to the terminal window as well as the file and it colors stderr output in red by default. Also, one can use start in cmd.exe in a similar manner.

In the end, I think the best solution is completely do away with dynamically allocating a console at all in GUI applications on Windows. Redirecting stdout and stderr to a tee works even better for diagnostics anyway.

@mflatt

mflatt commented Sep 13, 2026

Copy link
Copy Markdown
Member

I agree that a non-closable window opened by DrRacket would be really annoying, and that shouldn't happen in DrRacket. So far, I have viewed as a bug that I'd want to know about and fix. But if it happens routinely on Windows, then something better is indeed needed.

As long as other Racket users on Windows are on — I suggest double-checking on Discourse — I'm ok with console creation being turned off by default.

In that case, I'd still like an environment variable to turn on the current behavior, perhaps PLT_GUI_STDIO_CONSOLE. Just moments ago, seeing an error message when I tried to start GRacket.exe helped me immediately understand that I had mangled my installation and saved me minutes of confusion. (Had the error not appeared, I would have eventually started redirecting I/O, but probably not my first step.) It's fine for my purposes if I can configure my Windows environment once for that behavior.

@rfindler

Copy link
Copy Markdown
Member

My $0.02 as a drracket developer and only occasional user of windows: sometimes there are platform-specific bugs on windows that (because a racket-level exception is being raised) produce output on stderr. There are two reasons why I would like that to pop up for the user of DrRacket: it makes for much clearer bug reports when a user includes whatever message is in there (end users won't see it if it is a log file somewhere) and I think I would be unlikely to remember to turn on an environment variable to get that output myself while working on new things or debugging old things under windows.

I do agree that when this window pops up it should be regarded as a bug but I think that that's already the case, both by experienced and novice developers alike, as it looks quite different than normal interactions with drracket look.

I certainly agree with @mflatt that the console popping up should not be routine, but should be viewed as a bug to be fixed. The additinoal point I want to make here is that the console popping up is not the bug per se and it contains information that helps developers find and fix the actual bug.

@ndykman

ndykman commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor Author

I do agree that it makes sense to enable the current behavior if an environment variable (or switch) is set and that opt-in is the right choice. I did put it out there on Discord for feedback. If opt-out is more popular, then there it is.

I know that supporting Windows is a right old pain. And that programmers will be running on macOS and Linux anyway. Honestly, you can make a case that Windows is just a boat-anchor overall here and I wouldn't argue that much, even with the market share being what it is.

I just also want to make clear that I don't care about jaded programmers that won't run any tool without perfect dark mode support. [1] I try to think about somebody with access to a computer that wants to learn more about programming without being buried in the nightmare that current web-based tools present. Making it a bit easier to use on Windows helps there.

What started this for me is that I was getting a console window when using the package manager and some other cases and it wasn't printing out anything. And yes, you couldn't just close it (because that would kill both processes) until you exited DrRacket. That behavior seems to have disappeared.

I just accidently found a way to show the issues around this on my machine. Open up DrRacket and with no files and select "File->Search in Files". This will do one of two things:

Crash the entire process right away.
Pop up a "DrRacket Internal Error" modal dialogue and a console window. The error is about a contract violation (trying to
take car of an empty list). Both have the same information.

Is this a recoverable error? Seems so. I can dismiss the dialog and move on. But the console window is still there and I need to close it or files will still be locked.

If it isn't recoverable, then just save what you can and close. DrRacket does the right thing here. When you restart, it notes that it closed unexpectedly and there are drafts of files that can be recovered and allows users to make a decision on what to do.

In general, when it comes to other GUI programs in Racket, the right thing now is "do as we say, not as we do" and that can lead to complications.

[1] This was an actual YouTube comment to one of my videos I made on Racket. I have next to no subs and views and still somebody came in to say, in essence, "lol, the dark mode sucks".

@rfindler

Copy link
Copy Markdown
Member

I know that supporting Windows is a right old pain. And that programmers will be running on macOS and Linux anyway. Honestly, you can make a case that Windows is just a boat-anchor overall here and I wouldn't argue that much, even with the market share being what it is.

Just for the record, I believe we should support Windows as best we can! And I'm grateful for all your efforts here. Thank you!

What started this for me is that I was getting a console window when using the package manager and some other cases and it wasn't printing out anything. And yes, you couldn't just close it (because that would kill both processes) until you exited DrRacket. That behavior seems to have disappeared.

Great! One bug fixed 😄.

I see the internal error dialog box is intended to clarify that what you're seeing isn't the end user's fault but it doesn't catch all possible problems. I still think the console window as indicative of a bug but coming from a lower layer of the system

[1] This was an actual YouTube comment to one of my videos I made on Racket. I have next to no subs and views and still somebody came in to say, in essence, "lol, the dark mode sucks".

Geez. People can really be jerks sometimes! That said, there is definitely room for improvement in dark mode under windows. I think we don't know how to make the OS-drawn controls (like buttons) turn into dark mode, and the detection if the OS is set in dark mode might not work so well.

I just accidently found a way to show the issues around this on my machine. Open up DrRacket and with no files and select "File->Search in Files".

Thanks! I am not seeing that on my machine. Could you perhaps share the content of the console window and I'll look into it? Also, if you could clarify the exact steps and which version you're using that might also help me out.

@ndykman

ndykman commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

Just for the record, I believe we should support Windows as best we can! And I'm grateful for all your efforts here. Thank you!

You are welcome. I don't want to be the person that is "fix all these things" and not help fixing them when I can.

I just accidently found a way to show the issues around this on my machine. Open up DrRacket and with no files and select "File->Search in Files".

Thanks! I am not seeing that on my machine. Could you perhaps share the content of the console window and I'll look into it? Also, if you could clarify the exact steps and which version you're using that might also help me out.

It was the release version of DrRacket (9.3) on Windows 112 25H2.

The steps were:

Open DrRacket
Select File->Search in Files...

I say was because a reinstall fixed the problem, and given I am mixing development and release versions, this is probably a bug that is nigh impossible to replicate and very, very unlikely to appear again.

@ndykman

ndykman commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

Following on, I got two responses in Discord that did find it annoying that a console window would appear in their GUI apps on Windows. In one case, it was when printing (not an error). That developer redirected stdout and stderr to nowhere to prevent this from happening.

I really think the best thing overall is hide the console window on Windows unless a environment variable is set or a function is called to force a console window to be shown.

@rfindler

Copy link
Copy Markdown
Member

What if there was a way that individual apps could opt-in to the ugly black window appearing? Then I could just turn that on in DrRacket?

@otherjoel

Copy link
Copy Markdown
Contributor

What if there was a way that individual apps could opt-in to the ugly black window appearing? Then I could just turn that on in DrRacket?

I think this makes the most sense. For my own Windows apps I'd prefer to have the ugly black window for betas and turn it off for production builds. Arguably I can already do this, it's just opt-out rather than opt-in.

In the case of DrRacket in particular: from what I understand, it only shows a console window on Windows when something has gone sideways within DrRacket itself (or within a plugin).1 I lightly favor keeping that situation because it surfaces problems that should be fixed rather than allowing them to persist indefinitely. It's already the case that Windows is sort of a red-headed stepchild in Racket (and among modern OSs generally) so it's all the more important to surface errors that might be happening on Windows that don't occur on the other platforms.

I like the sound of all the other changes described in the original PR description, though I have no idea whether/how I'd be able to perceive those improvements in my work.

Footnotes

  1. I can't recall that this has ever actually happened to me in DrRacket on Windows. Output to stdout or stderr from my own apps running from inside DrRacket appears in the REPL where I would expect it, and does not open a console. ↩

@ndykman

ndykman commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

What if there was a way that individual apps could opt-in to the ugly black window appearing? Then I could just turn that on in DrRacket?

That would be the function of the environment variable. Maybe it could also be a switch when running a program. It could also turn on additional debugging functionalities that would further help trace errors.

@mflatt

mflatt commented Sep 27, 2026

Copy link
Copy Markdown
Member

After thinking about this more, here's what I recommend:

  • Change Racket GUI mode to create a stdio console only until racket/gui is instantiated. That way, a console is likely to appear for startup problems (where I remain convinced that it's valuable) and not appear after a GUI program is running (where it has been trouble for users).
  • Add an environment that continues stdio console creation beyond racket/gui instantiation. That's useful for debugging and for detecting unintended stdio output during development.

I lean against an application-specific choice instead of an environment variable that applies to all Racket applications, at least for now, because the application-specific choice would be more stuff.

@rfindler

Copy link
Copy Markdown
Member

Trying to think back on problems I have debugged, I think I would agree that drracket, once it is started up, tends to be able to display "internal error" dialogs successfully for racket-level bugs. Also, I certainly could set the current-error-port and current-output-port to somewhere that doesn't swallow the output during drracket's startup so there is an application-level option in some sense that's available regardless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants