Send ctrl-C to a Windows shell

I’m running a Shell in Interactive mode (2) to launch redis-server. When I want to stop it on the Mac, I Write ChrB( 3 ) and then wait. That’s the same as hitting ctrl-C in a terminal and it works perfectly.

Not so in Windows. I can’t figure out how to tell the server to stop and the only thing I’ve been able to do so far is to force it to quit by starting another shell and issuing taskkill /pid the_pid /f, which doesn’t give it an opportunity to cleanup and is not what I want.

So what’s the trick here?

I think Shell.Close does what you want on all platforms.

It does not. Close terminates immediately which is a good substitute for Kill, but not what I want.

How about the redis-cli shutdown command?
Can’t you just write “shutdown” or “stop” in your shell?
I think those are both redis commands to stop.

Yes, I was thinking of that. The problem is, I have to discover the port as it may be in a custom config, but I guess that’s the way to do it.

Thanks.

It turns out I can’t do that because there may be a password of which I am unaware, so I’m back to my original question. How can I send a running shell a ctrl-C or otherwise signal it to end?

Also, Shell.Close doesn’t kill the process on Windows either, I just discovered.

Kem - this feels like it might be relevant?

https://stackoverflow.com/questions/283128/how-do-i-send-ctrlc-to-a-process-in-c

Particularly this line in some sample code on the page:

proc.StandardInput.WriteLine("\\x3");

Comment from the code snippet’s author: [quote](To clarify: \x3 is the hex escape sequence for the hex character 3, which is ctrl+c. It’s not just a magic number. :wink: )[/quote]

I appreciate your finding that, but no luck. I tried it with Write, WriteLine, and with Canonical set to True too.

This doesn’t seem like it should be this hard…

Seems like I might need input from an engineer. @William Yu , @Joe Ranieri , @Travis Hill , @Greg O’Lone ?

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.

Still hoping for a response from an engineer…

sigh I miss the Pro channel.

I guess I’ll file this as Feedback and move on.

FYI:

<https://xojo.com/issue/52363>

[quote=390102:@Kem Tekinay]FYI:

<https://xojo.com/issue/52363>[/quote]
@Kem Tekinay – I posted a sample project on that case that works on my machine. Please try it on your machine and compare it to your code.

I will, but your comment says “I just tried this on my mac…” Did you mean you tried it on your Mac through a VM running Windows? Or that you missed that this was a Windows-specific problem?

I just looked, and it looks like the latter. I’ll update the case.

@Kem Tekinay – Try Shell.Execute(chrb(3)) instead of Shell.Write(chrb(3)) on Windows. That’s working for me.

Unfortunately that only looks like it works, but ends up closing the shell without terminating the app.

As Kem indicates, that results in the Shell being terminated - not what we’re looking for. I’m trying Kem’s option above.

@William Yu - can you chime in on this?