I found a solution, but I’m not marking it as an answer just yet as I’d still like to hear from an engineer.
Starting with this post, I came up with the following code based on declares:
#if TargetWindows then
declare function AttachConsole lib kWindowsLib (dbProcessId As UInt32) As Boolean
declare function GenerateConsoleCtrlEvent lib kWindowsLib (dwCtrlEvent as Int32, dwProcessGroupId As UInt32) As Boolean
if AttachConsole( PID.Val ) then
call SetConsoleCtrlHandler( nil, true )
call GenerateConsoleCtrlEvent( 0, 0 )
DoWindowsTeardown = true
end if
#else
static ctrlC as string = ChrB( 3 ).DefineEncoding( nil )
ServerShell.Write ctrlC
#endif
When the shell is being torn down, it executes this:
#if TargetWindows then
if DoWindowsTeardown then
declare function FreeConsole lib kWindowsLib () As Boolean
call SetConsoleCtrlHandler( nil, false )
call FreeConsole()
DoWindowsTeardown = false
end if
#endif
The SetConsoleCtrlHandler
is defined as an external method like this:
Private Function SetConsoleCtrlHandler(handler As Ptr, add As Boolean) as Boolean
And kWindowsLib = "kernel32.dll"
Finally, PID.Val
is the actual PID of my running process, not the one reported by Shell.PID
. Fortunately, redis-server reports that back to me when it starts.
I have no idea if I’m doing the right thing but it certainly works the way I want it. My server is signaled to stop just as if I had pressed ctrl-C in the Terminal and cleans itself up.