I've had some time to think some more about this issue. Here is additional information.
IMHO, the key problem with the current implementation is that it seems to assume the console control handler code is being called as a callback (which is what I believe happens in Windows XP), when actually the console control handler code (in Windows Vista and later) is being called from another thread being started by Windows (see note 1, below). This control handler code then triggers Perl's signal handling code, from the other thread. The signal handling code in the Perl program will be running at the same time the Perl program is. I'd be very surprised if Perl were designed to do that!
In my mind an equivalent scenario would be if I had a Perl program which ran an XSUB that (in C code) started a thread which then tried to call back into the Perl interpreter. Yes, because all the data structures are in the same process, my new thread could access and change things, sure, but due to synchronization and other thread safety issues I'd bet that my new thread would reek havoc upon the Perl interpreter and return results from my thread might be affected by the program still running in the Perl interpreter.
The other problem is that since Windows Vista, the OS (not Perl) will terminate the process upon the return from the console control handler—regardless of the return value (see note 2, below, and from what I've read elsewhere, this process will eventually be terminated by the OS even if the control handler never returns, see note 3, below). This could abort any type of recovery a Perl signal handler might attempt.
Here's what I propose as a possible solution. There is code in win32.c to convert a WM_USER_KILL message into a C runtime signal. Instead of having the console control handler code call back into Perl, have it post (or maybe in the case of SIGHUP, send -- so the thread could wait for a response) a message to the main thread of the console (possibly get the the console window handle via a GetWindowConsole-type function?). This would allow the existing code in win32c to recreate a SIGHUP (and also SIGINT and SIGBREAK, because they too would be affected by the threading issue) signal in the main thread, the thread running the Perl interpreter. Although slightly less efficient on Windows XP, this technique should operate on any Windows OS. Another advantage of this technique is that it would allow the use of a message spy type utility to determine whether or not the signal-simulation messages were actually reaching the console window.
Regarding the process termination issue, I'm less certain. Originally I suggested a rather simplistic "sleep" type solution prior to returning from the console control handler, but I have to admit, that idea stinks. I read here that the timeout on Windows 7 is 10 seconds, but what about Windows Vista? Windows 8? Windows 9, 10, 11? I don't know. Maybe the answer is: just go ahead and let the OS kill the process at that point. I do believe that somehow, the console control handler should be kept from returning until the Perl $SIG handler has a chance to run. How could that happen? Could the idea about the SendMessage for the SIGHUP work? You know better than I on that.
Anyway, here are my reference notes on this topic: