Gracefully terminate a process on Windows

taskkill
windows send sigint to process
reason: this process can only be terminated forcefully (with /f option).
terminateprocess
batch file kill process
terminate process gracefully
windows kill
kill java process windows

I'm writing a server application in Go, and using a wrapper to run it as a Windows service.

There's a need to gracefully shut down the server (to close resources and connections properly), and in UNIX it would be handled through the SIGTERM signal. No big deal.

Though on Windows things seem very different. I see on this guide that signals actually exist on windows (?), and the SIGTERM is defined, though other pages indicate they don't, or to use other mechanisms like WM_CLOSE.

What is the preferable way to tell a headless process to gracefully terminate? How should it be implemented in Go?

The server is designed to be multiplatform, so the most standard way of doing it is preferable.

Signaling is implemented on Windows but Unix signals are unavailable. There is an example in the signal package of golang for Windows to send a Ctrl-Break. It is refactored for interactive use here.

On Windows, how can I gracefully ask a running program to terminate?, Is there any other way to find the main windows of a process? In other words: Is there any way to terminate any process gracefully just like the task manager� A simple example is to use a text file of process ID's to terminate. If the batch process ID is listed in the file then it knows it needs to end: /* Process to run a Windows timer in background. The process is terminated by the process ID being added to the ProcTerminate.txt file. Only works for Windows - Sytem.Threading isn't available on Unix.

The go way to initiate canceling a task/service, is to use the context.Context package.

So if you want a signal handler to trigger the closing of a task's context.Context:

func registerSigHandler() context.Context {
        sigCh := make(chan os.Signal, 1)
        signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

        rootCtx := context.Background()
        taskCtx, cancelFn := context.WithCancel(rootCtx)

        go func() {
                sig := <-sigCh
                log.Println("received signal:", sig)

                // let sub-task know to wrap up: cancel taskCtx
                cancelFn()
        }()

        return taskCtx
}

and then pass the returned taskCtx on to your worker task for it to listen on.

select {
    case <-taskCtx.Done():
        // context was canceled
    default: // poll - rather than block in this example
}

Playground source-code.

Output:

2019/03/10 19:18:51 Worker PID: 33186
2019/03/10 19:18:51 Will terminate on these signals: interrupt terminated
2019/03/10 19:18:51 2019-03-10 19:18:51.018962 -0400 EDT m=+0.001727305
2019/03/10 19:18:52 2019-03-10 19:18:52.022782 -0400 EDT m=+1.005517010
2019/03/10 19:18:53 2019-03-10 19:18:53.019925 -0400 EDT m=+2.002630457

$ kill -INT 33186

2019/03/10 19:18:53 received signal: interrupt
2019/03/10 19:18:53 task context terminated reason: context canceled
2019/03/10 19:18:53 wrapping up task...
2019/03/10 19:18:53 workerTask() exited cleanly
2019/03/10 19:18:53 main exited cleanly

EDIT:

I tested this on Windows 10 and the clean-up is triggered when a Ctrl-C is issued from the same console. Not sure how to send signals externally on Windows - which may be the OP's original issue. Using say killtask /F /PID 33186 would indeed kill the process without any signal handler being triggered.

How to gracefully terminate a process?, switch (signum) {. case SIGTERM: case SIGKILL: case SIGINT: {. /* Unconditionally terminate the process. On Windows, killed processes */. There you can select a process in the list and either click on the End process button or hit the Del key. Using the End Task button means Windows first tries to see for a certain timeout if the process has really stopped responding, and attempts to collect a crash or memory dump of the process. It then terminates the app.

So, after a while, I just wanted to share how I solved it. It's ugly, but the only real way I found. Windows golang programs do listen to a CTRL+C, which is not a signal or anything like it, but it does trigger a graceful shutdown in the go program using the same signal UNIX has.

Here's the relevant code:

// Create shutdown channel
exitChan := make(chan int)
interruptChan := make(chan os.Signal, 1)
signal.Notify(interruptChan, os.Interrupt)
select {
case exitCode := <-exitChan:
    os.Exit(exitCode)
case <-interruptChan:
    // This works in Windows if ctrl-c is invoked. It sucks though
    svr.Stop()
    os.Exit(-1)
}

To trigger the CTRL+C given the process, I use a small program called windows-kill.exe

// Windows is "special" and we need to use a library for killing processes gracefully
// Unfortunately setting up the C++ library with go is a hassle, so we resort to a CLI tool
ex, _ := os.Executable()
cmdPath := filepath.Join(filepath.Dir(ex), "windows-kill.exe")
cmd := exec.CommandContext(context.Background(), cmdPath, "-SIGINT", strconv.Itoa(l.proc.Pid))

err := cmd.Start()
if err != nil {
    panic(err)
}

Here's the repo of the library and tool: https://github.com/alirdn/windows-kill

Graceful termination on process on Windows � Issue #1133 � libuv , Stop Programs from the Command Prompt Using Taskkill. Some programs and background services in Windows 10 can prove hard to kill when� Currently all child processes are forcefully killed on Windows libuv/src/win/process.c Lines 1167 to 1189 in d9d2077 switch (signum) { case SIGTERM: case SIGKILL: case SIGINT: { /* Unconditionally terminate the process.

If you are running some form of web-services, may be better to use the web-service itself to initiate a shutdown, as tracking PIDS for signals can get messy.

To stop a http web-service, simply add a route like this:

http.HandleFunc("/shutdown",
        func(w http.ResponseWriter, r *http.Request) {
            // stops server - may want to add admin credentials check here!
            srv.Shutdown(context.Background())
        })

Playground source-code example.

2019/03/10 16:58:15 Listening on: :8080

$ curl 'localhost:8080/shutdown'

2019/03/10 17:04:17 Listening on: :8080
2019/03/10 17:04:19 exited cleanly

End Processes like a Pro with the Taskkill Command, How to gracefully terminating a background process running on Windows? A listener program running in background uses sockets to listen on� I have this powershell code to close a program gracefully. Get-Process MyProgram | Foreach-Object { $_.CloseMainWindow() | Out-Null } the problem is that it will pop-up a Windows asking user to hit 'OK' or to 'Cancel'! what powershell code I can force it selects OK and not prompts for user to interact? thanks.

The idea of using *nix constructs a functions to Windows is generally bad and prone to weird tricks that may or may not work.

The proper way to cleanup on shutdown a GUI application is the handling of WM_QUERYENDSESSION and WM_ENDSESSION. Also, you have flexibility on generic shut-down mechanism, check here.

The proper way to get notified if you are a service is the service handler of your console application (SERVICE_STOPPED). For more, see Writing a ServiceMain.

Knowledge: How to gracefully terminating a background process , You can use it to stop or force stop Windows processes, and it provides a lot more information that Task Manager. Using the Command Prompt:. EnumWindows enumerates all the top level windows in a process. GetWindowThreadProcessId gets the process and Id of each thread. You now have enough information to gracefully close any GUI application. You can send WM_CLOSE messages to any window you wish to close.

Gracefully Stopping Iguana Service on Windows , One of possible ways to do it is to send WM_CLOSE message to the application's main window: works the same as when you press the '[X]' button. "End Task" (and taskkill) appears to post a WM_CLOSE message to the program's windows. (The same is done when you click the × "Close" button.) If the program does not exit in some time, user gets prompted to end the program forcefully. "Kill Process" and taskkill /f use TerminateProcess(). How To Terminate an Application "Cleanly" in Win32

How to gracefully kill (close) programs and processes via command , This step-by-step guide describes how to Kill Process in Linux or Unix-like operating systems using What Linux or Unix permissions do I need to kill a process? These kill differs in releasing resources forcibly or gracefully… I got a window I cannot close, it is on top and I cannot access to the menus.

Gracefully stop a Windows process remotely,

Comments
  • what type of "server" are you planning to run? Will it be a web-server? http could be used to more cleanly initiate a targeted shutdown.
  • sending a signal requires hunting down the PID of the running process, which is not always a precise task.
  • See the answer, the service wrapper should not know what kind of service the server exposes (I might reuse it in the future)
  • There seems to be an official example of a go Windows service with stop command being properly handled: godoc.org/golang.org/x/sys/windows/svc and also this: github.com/kardianos/service
  • @JohnWhite - It's you who said "a server application in Go, and using a wrapper to run it as a Windows service", not me. That doesn't change the fact CTRL-C is not the solution anyway.
  • How the problem is handled internally is kind of off topic, the problem is how to actually code the signal handling, the documentation doesn't mention a clear way to do this.
  • @JohnWhite updated answer to include a working example of registering signal monitoring & context.Context integration.
  • Thanks, this is appreciated. Though you're right, a big part of the original issue is how to trigger it under windows, which is why after struggling for some hours I posted it there. Hope somebody posts a solution for it. The golang library doesn't implement signals for windows. Maybe taskkill without flags does it? I'll do some tests.
  • I created windows golang program to send signals to pids and can confirm os.Interrupt (-2) is not support - issued at runtime. os.Kill (-9) is supported but as we can see terminates the process without handlers being called.
  • This is exactly why I created this question. Apparently go has no mechanism for receiving the signal from the OS.
  • CTRL+C is only valid for console process and is not particularly graceful to the app if app doesn't handle it. It can also be handled so it does nothing : docs.microsoft.com/en-us/windows/console/…
  • Unfortunately it's even more messy, as I would need to first authenticate and then issue the shutdown command. Keeping the pid is cleaner and simpler. Plus it would make it necessary to know the listening address from the service wrapper, breaking encapsulation (it shouldn't even know it's a web server)
  • with a web-services - you can access this remotely without needing remote-shell access. If you are deploying this on multiple architectures - a web-service would be your cleanest interface. Even if your underlying server is not a http service, you could employ the above technique to listen for shutdown events.