Windows send signal to process

windows-kill

windows-kill – Send signal to process by PID in Windows, like POSIX kill

Send signal to process by PID in Windows, like POSIX kill

Windows has no process signaling mechanism like what POSIX provide using the kill command. But windows-kill could send signal to process by PID. :)

Why windows-kill?

Well, I’m a node.js developer. Node has a functionality that could send signals to other process, by their PID. This functionality works great on POSIX OSes but in Windows, no signal is supported (even SIGINT and SIGBREAK, that node stated is supported in Windows), and sending any of those signals to any process in windows, will result in an immediate kill of those process (Even if those process have listener on sent signals). In one of my project signal sending and listening was a serious need, so I’ve made some research about signal sending in windows, and found an OLD (2003) project named SendSignal. Well that project doesn’t support 64bit systems and also could only send SIGBREAK (Ctrl + Break) signal. More searches lead me to an enhanced version of SendSignal, that add support of 64bit systems, but only support sending SIGINT (Ctrl + C) signal. So I’ve decided to write a library that support both 32bit & 64bit systems, and also both SIGBREAK and SIGINT signals. The result is the windows-kill-library that has all the functionality I’ve needed, and is the heart of windows-kill. For information about windows-kill-library navigate to windows-kill-library folder.

Features

  • Support both 32bit (Win32) & 64bit (x64) Windows
  • Support both SIGBREAK (Ctrl + Break) and SIGINT (Ctrl + C) Signals
  • A library that could be used directly (#include), As a static library (.lib) and a dynamic library (.dll)
  • Prebuilt binaries and libraries

How it works & Limitations

To read a detailed info please visit windows-kill-library Readme. But it’s good to know that windows-kill will create a ctrl event in the process that is calling it. If the caller process has no child process or not a child process of another process, nothing will happen. But if has child process or is child process, sending signal will trigger the ctrl routine of all processes in the process group, and as a result, those process will be terminated.

Installation

Chocolatey — 32bit (Win32) and 64bit (x64)

Install

> choco install windows-kill

Update

> choco upgrade windows-kill

Prebuilt Binaries

You can also download the prebuilt binaries of windows-kill. You could find them in repo’s Releases page.
Both the 32bit (Win32) and 64bit (x64) versions are available.

Compile & Build from Source code

If you want to compile from the source code, you must install Visual Studio plus C++ development tools first. I’m developing this project in Visual Studio 2015 update 3. So it’s recommended to use the same Visual Studio version. But maybe other versions are ok to use. Also I didn’t compile the project using other C++ compilers. So any contribution to add other compilers support is welcomed!

After the Visual Studio installation, clone the GitHub project or download the latest master branch source code and extract the downloaded zip.
Go to the project folder and open the windows-kill.sln in Visual Studio. From the build menu, click on batch build. There you could see different build configurations. For more information about build configuration see Build Configurations.

Usage Examples

Using the windows-kill is easy & straightforward. It’s just like POSIX kill. Just navigate to the folder that contains windows-kill.exe and open a cmd from that folder. Or add that folder in Environment Variables so you could use the command from any cmd.
If signal sending was successful or any error occurred during the sending, appropriate message will be print in cmd.

Sending signal to PID

> windows-kill -SIGNALTYPE PID

Sending SIGBREAK (Ctrl + Break) to sample 1234 PID

> windows-kill -SIGBREAK 1234

Sending SIGINT (Ctrl + C) to sample 1234 PID

> windows-kill -SIGINT 1234

List supported signal types

Usage help

Build Configurations

windows-kill Visual Studio solution contains different build configuration. There are two main Debug and Release configuration category. Each of these categories have two separate configurations for building the windows-kill-library as a dynamic link library (.dll) or static link library (.lib). Also all the build configurations in Visual Studio has 32bit (Win32) & 64bit (x64) versions. In Summary there are 4 build configurations:

  • Debug Dll: Build windows-kill-library as a .dll with debug enabled.
  • Debug Lib: Build windows-kill-library as a .lib with debug enabled.
  • Release Dll: Build windows-kill-library as a .dll without debug.
  • Release Lib: Build windows-kill-library as a .lib without debug.

Resources

The windows-kill & windows-kill-library are based on:

  • SendSignal
  • Enhanced version of SendSignal

Contributing

We love contributions from everyone. Please read Contributing guide.

License

MIT

When working with Windows and needing to send a signal to another program or process, the approach differs from Unix systems. In Windows, signals are not used in the traditional sense; instead, programs within Windows receive messages. This distinction is essential for effectively interacting with processes and ensuring they respond as expected.

To achieve the termination of another program or process programmatically in Windows, a tool called “pskill” from SysInternals’ pstools can be utilized. This tool operates similarly to the Unix “kill” command by terminating processes.

One key aspect to understand when working with Windows applications is how messages are handled. For example, actions such as clicking the close button on a window generate messages like WM_CLOSE, while deleting a window triggers WM_DESTROY. When an application is closing, WinMain receives WM_QUIT. It is crucial for programs to appropriately handle these messages to ensure proper behavior. Failure to do so could result in creating an application that cannot be closed using standard methods.

In scenarios where a user attempts to end a task using the Windows Task Manager, clicking “End Task” will send a WM_CLOSE message along with another message (specifically WM_DESTROY). On the other hand, selecting “End Process” directly terminates the process without sending any messages, abruptly ending its execution.

For more advanced operations, obtaining the window handle (HWND) of another process’ window allows for message passing using functions like PostMessage and DispatchMessage. This capability enables inter-process communication by sending messages to specific windows of external processes.

node-windows-kill

Enhance node’s process.kill to support signals in Windows

Installation

$ npm install windows-kill

Features

  • No code change is needed. Just old process.kill calls.
  • Support Both x86 & x64 Windows
  • No effect on Non Windows operation systems (Linux, Mac OS X, etc…)
  • Support both SIGINT and SIGBREAK (Just the two signals that are available on Windows)

Why windows-kill?

Sending signal to another process, just by knowing it’s PID, is not available at Windows OS. It’s a POSIX OSes feature. But, sending signal to other process, for telling it that something is going to happen to you, is a way to give other process some time for graceful shutdown/restart. windows-kill tries to fix this issue by bringing the ability to send signals, SIGINT and SIGBREAK, to another process by PID.

How it works?

To read a detailed info please visit windows-kill-library Readme.

Limitations

To send the signal, windows-kill at first send a same signal to the process that is calling it, to find a thread address. Then the founded address is used to send the real signal. Because of this, the process that is sending the signal will get the same signal too. But windows-kill register a signal handle during this procedure, so the process will not terminate. But if the process that is sending signal has child process, or is a child process of another process, sending signal will trigger the signal handles in other process in the same process group. And the default behavior of Windows console/application in case of getting a SIGINT or SIGBREAK, is to terminate.

sum up: If you are sending signal in node app that has child process (any kind of it), or is a child process of another process, the result is the termination of all the processes in the same process group, except the sender (well if it’s a child process, because the master is terminated, it will terminate too).

PS: Currently there is no solution for this problem. But I’m working on it, to find a solution. Solutions for different scenarios added.

  • Parent process only send signal
  • Cluster master only send signal

PS-1: A solution for parent processes that wants to send signal (no way for child processes currently), is added. It’s setting the warmUp: true option when first calling the windows-kill in parent, before any child processes creation.

Usage

windows-kill expose a function. Simply run the exposed function with/without the options. Thats it. It should be called before any usage of process.kill.

By default, windows-kill will enhance the node’s process.kill in a way, that no code changes are needed in your codebase. Enhance means that windows-kill will replace the node’s process.kill with a custom function with the same arguments and functionality. Just some changes to achieve signaling in Windows.

Simplest usage

The returned function from calling the exported function, could be used to send signal, just like the way you call process.kill. It will accept two argument. a PID and a SIGNAL.

/*
    Require and call the function that's exported.
    The returned function can be used to send signal.
*/
var windowsKill = require('windows-kill')();

/*
    By default, process.kill is enhanced (only in Windows OS),
    so you can either call process.kill, or the returned function.
*/
process.kill(PID, SIGNAL);
windowsKill(PID, SIGNAL);
/*
    Just call the function, and no need to change any code.
    Every process.kill calls in your code, will now enhanced.
    Just need to call windows-kill, before any call of
    process.kill.
*/
require('windows-kill')();

process.kill(PID, SIGNAL);

Options

Options and default values are:

const defaultOptions = {
    replaceNodeKill: true,
    warmUp: false
};

replaceNodeKill

This option will tell the windows-kill that should it enhance/replace the nodes process.kill? By setting true (default value), the nodes process.kill will replaced by a custom function. This custom function will check the signal that is sending using process.kill. If the signal is a member of supported signals, which is SIGINT or SIGBREAK, it will call the modules function that is responsible for sending signal. Otherwise, the signal and pid will be passed to the node’s original process.kill.

const options = {
    replaceNodeKill: true, // Should windows-kill enhance/replace node's process.kill? Default: true
};

require('windows-kill')(options);

process.kill(PID, SIGNAL);
const options = {
    replaceNodeKill: false // Should windows kill enhance/replace node's process.kill. Default: true
};

var windowsKill = require('windows-kill')(options);

windowsKill(PID, SIGNAL);

warmUp

By setting warmUp to true, windows-kill will find and save the ctrl-routine addresses, without any need to send signal. By default, the address will find, when the first signal of that type is sending. Future call will use the founded address.

const options = {
    warmUp: true, // Should windows-kill warm-up by finding the ctrl-routines addresses? Default: false
};

require('windows-kill')(options);

process.kill(PID, SIGNAL);

Warm-up, is one way to overcome the limitations. Setting this option, will make the windows-kill to find needed address, before any signal sending. As stated in limitations section, finding address will cause the processes that have child process, or is a child process, trigger the ctr-routine of all process group members, which means termination of all of them. But warm-up mechanism can be used, to fix the issue in parent process. By setting it to true, before any child process creation, sending signal in future will use the save addresses and no need to find addresses again.

Parent process only send signal
var options = {
    warmUp: false
};

var cp = require('child_process');
var windowsKill = require('windows-kill')(options);

var cp1 = cp.spawn('node', ['cp.js']);

windowsKill(PID, 'SIGINT');

By running the above code, the cp1 child process will terminate. Because the SIGINT signal is sent for the first time, and the ctrl-routine address in not available. So windows-kill will try to find it, and trying to find it will trigger the SIGINT handler of cp1 child process, which lead to termination of cp1.

To solve the issue, we can set the warmUp option true. Just remember, the initialization of windows-kill with warmUp option should be done before any child process creation. Like below:

var options = {
    warmUp: true
};

var cp = require('child_process');
var windowsKill = require('windows-kill')(options);

var cp1 = cp.spawn('node', ['cp.js']);

windowsKill(PID, 'SIGINT');
Cluster master only send signal

To avoid the termination of child processes (forks), you should use warmUp option in the master creation part.

var cluster = require('cluster');
var os = require('os');

if (cluster.isMaster) {
    /*
        Initialize windows-kill here. Before and child process creation,
        just in the master process code.
    */
    var windowsKill = require('windows-kill')({
        "warmUp": true
    });

    for (let i = 0; i < os.cpus().length; i++) {
        cluster.fork();
    }
} else {
    /* Child Process code, that is not using windows-kill*/
}

Contributing

We love contributions from everyone. Please read Contributing guide.

License

MIT

In usual fashion, I’ve written a complete sample application. The source code is available here.

Sometimes you want detect if a specific application is running and signal it to terminate in a clean manner. It may be your upgrade installer making sure that the current version of the application is not running before performing it’s upgrade. It could be a long running helper process that needs to be signaled when it is no longer needed. Whatever it is, one method to accomplish this is to use a uniquely named shared event.

NOTE: The method I’m about describe only works for processes who’s source code is under your control. If you want a way to generically signal any running process (e.g. search for a list of running OS and 3rd-party processes that might interfere with your installer and signal them to terminate) then this is not what you want.

A Bit of Background

A similar problem to the one we are discussing here is signaling all running threads to terminate. The idea is that there could be multiple places in the code where an application might need to initiate a process termination, but you need to synchronize that across all threads and allow them to perform their own cleanup. One way to do this is have long running threads periodically check to see if they should shutdown by checking to see if an event is signaled.

Windows Events

On the Windows platform when an event object is created it is done so in an object namespace. In addition to the ability to create your own private namespaces, there are also two special kernel object namespaces – Global and Local. There is a Local namespace associated with each client session on the machine. By default the Local namespace is used for any object created by a process that was started under a client session. As the name implies, there is a single Global namespace system-wide. The Global namespace is used primarily by system services but can also be used by client session processes by prefixing the event name with “Global\”.

The CreateEvent function is used to (surprise!) create an event. It can create either a named or unnamed event. If you use a named event and the named event already exists before the call to CreateEvent then the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. By creating a named event, the OS enforces that only a single instance of the object exists in that namespace at any one time and that all processes referring to that event will receive a handle to the same instance, creating a form of interprocess communication. Thus if the Local namespace is used, then the event object is shared across all processes that refer to it in that client session. Likewise if it is created in the Global namespace, it is shared across all processes that refer to it on the entire system.

There are two reset mechanisms used by event objects: AutoReset and ManualReset. An AutoReset event will automatically be reset to a non-signaled state as soon as single waiting thread is released. A ManualReset event requires a call to ResetEvent in order to be returned to a non-signaled state.

Lastly, an event can be set to either the signaled or non-signaled state when it is initially created.

Signal Terminate via Named Event Object

By combining the concept of checking for a signaled event to determine when to shutdown and using a named event object, it is possible to signal one process to shutdown via another process. By using an event object created in the Local namespace you can signal processes across a single client session. Conversely by using an event object created in the Global namespace you can signal processes across the entire system.

When creating the terminate event object you want to use a ManualReset event created in the non-signaled state initially. If it were instead an AutoReset event, then as soon as one of the waiting threads from any of the processes was released, the event would return to the non-signaled state. This would result in only a single thread receiving the terminate message, which is not what we want. As for the state, if it were instead initially signaled then the threads would begin terminating as soon as they started running and began checking the event.

Below is an example of creating a named ManualReset event in the Local object namespace that is intially non-signaled. I’m using a GUID for the object name to avoid the potential for unintentional naming collisions with other applications. While a GUID is probably overkill, using a name like “shutdown_event” probably isn’t a good idea.

static const LPCSTR fp_terminate_event_name =
   "Local\\0BAF85D0-0786-4cbf-AF3B-E36322382DBF";

// Create a manual-reset event in the non-signaled state
fh_terminate_event =
  CreateEvent( NULL,                              // default security attributes
               TRUE,                              // manual-reset event.
               FALSE,                             // initial state is non-signaled
               TEXT( fp_terminate_event_name ) ); // object name

@agent max How do I use the system command? The windows native taskkill.exe does not seem to be sending signals, it just shuts down the process. I have tried using signal handlers with taskkill but it does not work. The idea to use pipe seems to be interesting, could you perhaps direct me to an example of an easy implementation of pipes? I am not a C programmer, so all of this is new to me.

@seeplus Thanks for the link. I looked at WaitForSingleObject() but it seems like it just waits for the event to occur. But I need something like a passive wait mechanism, that just sits there, and allows the rest of the program to move on.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


#ifdef __cplusplus
extern "C" {
#endif

#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif

void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);

static struct sigaction old_act;

void signalinit_() {
// initialise dmrcc's responses to signals
   struct sigaction act_term;
   memset(&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &parent_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);
}

void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
   pid_t pid_parent;
   char pidchar[10];
   char command[40];

   pid_parent = getpid();
   sprintf(pidchar, "%d", pid_parent);

   printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);

   sendSignalToChildren(SIGTERM);
   sleep(5);
   sendSignalToChildren(SIGKILL);

   printf("\n Program dmrcc terminating\n"); fflush(stdout);
   dmrccend_(&ERROR_SIGTERM);
}

void sendSignalToChildren(int sig) {
   int ownPid = getpid();
   int pid, numPids = 0;
   int *pids;
   FILE *pidfile = fopen("pids", "r");
   if (pidfile == NULL) {
      printf("Error: Could not open pids file\n");
      return;
   }

   // number of running processes other than the current process
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         numPids++;
      }
   }
   rewind(pidfile);

   // read other process' PIDs
   pids = (int *)malloc(numPids * sizeof(int));
   int i = -1;
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         pids[++i] = pid;
      }
   }

   // send signal sig to processes
   printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
   for (i = 0; i < numPids; i++) {
      kill((pid_t)pids[i], sig);
   }
   
   fclose(pidfile);
   free(pids);
}

void signalinitchild_() {
// initialise child's responses to signals
   struct sigaction act_term;
   memset (&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &child_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);

   struct sigaction act_segv;
   memset (&act_segv, '\0', sizeof(act_segv));
   act_segv.sa_sigaction = &child_sigsegv_;
   act_segv.sa_flags = SA_SIGINFO;
   sigaction(SIGSEGV, &act_segv, &old_act);
}

void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
   mrccend_(&ERROR_SIGTERM);
}

void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
   mrccend_(&ERROR_SIGSEGV);
   if(old_act.sa_flags & SA_SIGINFO)
   {
      (*old_act.sa_sigaction)(signum, siginfo, context);
   } 
   else 
   { 
      (*old_act.sa_handler)(signum);
   }
}

#ifdef __cplusplus
}
#endif  

Multiple processes are launched by MPI. Then from the fortran code, the master process calls signalinit_() and all the child processes call signalinitchild_(). I don’t even need to send signals to the child processes, I just need some way for the parent process to communicate with all the child processes so that the child processes know which error has occurred, and then I can directly call childsigterm_() and childsigsegv_.

Понравилась статья? Поделить с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
  • Bluetooth наушники не отображаются в звуковых устройствах windows
  • Утилиты командной строки windows для работы с сетью
  • Драйвер для сетевой карты realtek для windows 10 64 bit
  • Как настроить два экрана на компьютере windows 10
  • Как попасть в среду восстановления windows 10 если компьютер не загружается