Puis-je envoyer un CTRL-C (SIGINT) à une application Windows?
j'ai (dans le passé) écrit des applications multiplateformes (Windows/Unix) qui, à partir de la ligne de commande, ont traité une combinaison Ctrl - C de la même manière (c.-à - d. pour terminer l'application proprement).
Est-il possible sur Windows pour envoyer un Ctrl - C /SIGINT/équivalent à un processus à partir d'un autre (sans relation) le processus de demande de qui il mettre fin à proprement (lui donnant la possibilité de ranger des ressources, etc.)?
12 réponses
le plus proche que j'ai trouvé une solution est le SendSignal troisième partie app. L'auteur liste le code source et un exécutable. J'ai vérifié qu'il fonctionne sous windows 64 bits (comme un programme 32 bits, tuant un autre programme 32 bits), mais j'ai pas trouvé comment intégrer le code dans un programme windows (32 bits ou 64 bits).
Comment cela fonctionne:
après avoir beaucoup fouillé dans le débogueur J'ai découvert que le point d'entrée qui fait réellement le comportement associé à un signal comme ctrl-break est kernel32!CtrlRoutine. La fonction avait le même prototype que ThreadProc, donc elle peut être utilisée avec CreateRemoteThread directement, sans avoir à injecter du code. Cependant, ce n'est pas un symbole exporté! Il est à des adresses différentes (et a même des noms différents) sur différentes versions de Windows. Que faire?
Voici la solution que j'ai finalement trouvée. - Je installer un console ctrl gestionnaire pour mon application, puis générer un ctrl-pause signal pour mon application. Quand mon gestionnaire est appelé, je regarde en haut de la pile pour trouver les paramètres passés à kernel32!BaseThreadStart. J'attrape le premier param, qui est l'adresse de départ désirée du thread, qui est l'adresse de kernel32!CtrlRoutine. Puis je reviens de mon gestionnaire, indiquant que j'ai géré le signal et que mon application ne devrait pas être terminée. De retour dans le fil principal, j'attends l'adresse de kernel32!CtrlRoutine a été retrouvée. Une fois que je l'ai, je crée un thread distant dans le processus cible avec l'adresse de départ découverte. Cela fait que les manipulateurs ctrl dans le processus cible sont évalués comme si ctrl-break avait été pressé!
la bonne chose est que seulement le processus cible est affecté, et n'importe quel processus (même un processus fenêtré) peut être ciblé. Un inconvénient est que ma petite application ne peut pas être utilisé dans un fichier batch, car il va le tuer quand il envoie l'événement Ctrl-break pour découvrir l'adresse de kernel32!CtrlRoutine.
(précédez-le de start
si vous l'exécutez dans un fichier batch.)
j'ai fait quelques recherches sur ce sujet, qui s'est avéré être plus populaire que je ne l'avais prévu. La réponse de KindDragon fut l'un des points essentiels.
j'ai écrit un post de blog plus long sur le sujet et a créé un programme de démonstration de travail, qui démontre l'utilisation de ce type de système pour fermer une application en ligne de commande dans un couple de belles modes. Ce post répertorie également les liens externes que j'ai utilisés dans Mes recherches.
In bref, ces programmes de démonstration font ce qui suit:
- Démarrer un programme avec une fenêtre visible en utilisant .Net, se cacher avec pinvoke, courir pendant 6 secondes, afficher avec pinvoke, arrêter avec .Net.
- lancer un programme sans fenêtre en utilisant .Net, exécuter pendant 6 secondes, arrêter en attachant la console et en émettant ConsoleCtrlEvent
Edit: la version modifiée de La solution de KindDragon pour ceux qui sont intéressés dans le code, ici et maintenant. Si vous prévoyez de lancer d'autres programmes après avoir arrêté le premier, vous devriez réactiver la gestion de Ctrl-C, sinon le prochain processus héritera de l'état de handicap du parent et ne répondra pas à Ctrl-C.
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
public void StopProgram(Process proc)
{
//This does not require the console window to be visible.
if (AttachConsole((uint)proc.Id))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
// Must wait here. If we don't and re-enable Ctrl-C
// handling below too fast, we might terminate ourselves.
proc.WaitForExit(2000);
FreeConsole();
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
aussi, prévoir une solution d'urgence si AttachConsole()
ou le signal envoyé devait échouer, par exemple dormir alors ceci:
if (!proc.HasExited)
{
try
{
proc.Kill();
}
catch (InvalidOperationException e){}
}
je suppose que je suis un peu en retard sur cette question mais je vais écrire quelque chose de toute façon pour quiconque ayant le même problème. C'est la même réponse que j'ai donnée à cette question .
mon problème était que je voudrais que mon application soit une application GUI mais les processus exécutés devraient être exécutés en arrière-plan sans aucune fenêtre de console interactive attachée. Je pense que cette solution devrait aussi fonctionner lorsque le processus parent est un processus de la console. Vous pouvez il faut cependant supprimer le drapeau "CREATE_NO_WINDOW".
j'ai réussi à résoudre ce problème en utilisant GenerateConsoleCtrlEvent () avec une application wrapper. La partie la plus délicate est juste que la documentation n'est pas vraiment clair sur exactement comment il peut être utilisé et les pièges.
ma solution est basée sur ce qui est décrit ici . Mais cela n'a pas vraiment l'expliquer tous les détails et avec une erreur, voici donc la détails sur la façon de le faire fonctionner.
Créer une nouvelle application d'aide "Aide.EXE." Cette demande se situera entre votre demande (parent) et le processus enfant que vous voulez fermer. Il créera également le processus enfant réel. Vous devez avoir ce processus de "middle man" ou GenerateConsoleCtrlEvent() échouera.
utiliser une sorte de mécanisme de PCI pour communiquer du parent au processus d'aide que l'aide devrait fermer l'enfant processus. Lorsque l'Assistant obtient cet événement il appelle "GenerateConsoleCtrlEvent(CTRL_BREAK, 0)" qui se ferme lui-même et le processus enfant. J'ai utilisé un objet event pour cela moi-même que le parent complète quand il veut annuler le processus enfant.
pour créer votre Helper.exe le créer avec CREATE_NO_WINDOW et CREATE_NEW_PROCESS_GROUP. Et lors de la création du processus enfant, créez-le sans drapeaux (0), ce qui signifie qu'il dérivera la console de son parent. À défaut de ce faire va l'amener à ignorer l'événement.
il est très important que chaque étape soit faite comme ceci. J'ai essayé toutes sortes de combinaisons, mais cette combinaison est le seul qui fonctionne. Vous ne pouvez pas envoyer un événement CTRL_C. Elle sera un succès mais sera ignorée par le processus. CTRL_BREAK est le seul qui fonctionne. Cela n'a pas vraiment d'importance puisqu'ils appelleront tous les deux ExitProcess() à la fin.
vous ne pouvez pas non plus appeler GenerateConsoleCtrlEvent() avec un processus groupd id de l'enfant processus id permettant directement au processus d'aide de continuer à vivre. Cela ne fonctionne pas ainsi.
j'ai passé une journée entière à essayer d'obtenir ce travail. Cette solution fonctionne pour moi, mais si quelqu'un a quelque chose à ajouter s'il vous plaît. J'ai parcouru le net pour trouver des tas de gens avec des problèmes similaires, mais pas de solution définitive au problème. Comment GenerateConsoleCtrlEvent () fonctionne est aussi un peu bizarre donc si quelqu'un sait plus de détails sur elle s'il vous plaît part.
Edit:
pour une application GUI, la façon" normale " de gérer cela dans le développement de Windows serait d'envoyer un message WM_CLOSE à la fenêtre principale du processus.
pour une application de console, vous devez utiliser SetConsoleCtrlHandler pour ajouter un CTRL_C_EVENT
.
si l'application ne respecte pas cela, vous pouvez appeler TerminateProcess .
en quelque sorte GenerateConsoleCtrlEvent()
erreur de retour si vous l'appelez pour un autre processus, mais vous pouvez joindre à une autre application de la console et envoyer l'événement à tous les processus enfants.
void SendControlC(int pid)
{
AttachConsole(pid); // attach to process console
SetConsoleCtrlHandler(NULL, TRUE); // disable Control+C handling for our app
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // generate Control+C event
}
, Il doit être clairement explicité car pour l'instant il ne l'est pas. il existe une version modifiée et compilée de SendSignal pour envoyer Ctrl-C (par défaut, elle n'envoie que Ctrl+Break). Voici quelques binaires:
(2014-3-7) : j'ai construit des versions 32-bit et 64-bit avec Ctrl-C, il s'appelle Sendsignalcrlc.exe et vous pouvez le télécharger à: https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86/SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/SendSignalCtrlC.exe -- Juraj Michalak
j'ai aussi reproduit ces fichiers juste au cas où:
Version 32 bits: https://www.dropbox.com/s/r96jxglhkm4sjz2/SendSignalCtrlC.exe?dl=0
64-bit version: https://www.dropbox.com/s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0
Avertissement: je n'ai pas construit ces fichiers. Aucune modification n'a été faite à la compilation les fichiers originaux. La seule plateforme testée est Windows 7 64 bits. Il est recommandé d'adapter la source disponible à http://www.latenighthacking.com/projects/2003/sendSignal / et compilez-le vous-même.
voici le code que j'utilise dans mon application C++.
points positifs:
- Fonctionne à partir de la console application
- Fonctionne à partir de Windows service
- Pas de temps
- ne ferme pas l'application actuelle
points négatifs:
- la console principale est perdue et une un nouveau est créé (voir FreeConsole )
- la commutation de la console donne des résultats étranges...
// Inspired from http://stackoverflow.com/a/15281070/1529139
// and http://stackoverflow.com/q/40059902/1529139
bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent)
{
bool success = false;
DWORD thisConsoleId = GetCurrentProcessId();
// Leave current console if it exists
// (otherwise AttachConsole will return ERROR_ACCESS_DENIED)
bool consoleDetached = (FreeConsole() != FALSE);
if (AttachConsole(dwProcessId) != FALSE)
{
// Add a fake Ctrl-C handler for avoid instant kill is this console
// WARNING: do not revert it or current program will be also killed
SetConsoleCtrlHandler(nullptr, true);
success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE);
FreeConsole();
}
if (consoleDetached)
{
// Create a new console if previous was deleted by OS
if (AttachConsole(thisConsoleId) == FALSE)
{
int errorCode = GetLastError();
if (errorCode == 31) // 31=ERROR_GEN_FAILURE
{
AllocConsole();
}
}
}
return success;
}
exemple d'utilisation:
DWORD dwProcessId = ...;
if (signalCtrl(dwProcessId, CTRL_C_EVENT))
{
cout << "Signal sent" << endl;
}
void SendSIGINT( HANDLE hProcess )
{
DWORD pid = GetProcessId(hProcess);
FreeConsole();
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(NULL, true);
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); // SIGINT
//Re-enable Ctrl-C handling or any subsequently started
//programs will inherit the disabled state.
SetConsoleCtrlHandler(NULL, false);
WaitForSingleObject(hProcess, 10000);
}
}
basé sur l'identifiant du processus, nous pouvons envoyer le signal à process pour terminer avec force ou élégance ou tout autre signal.
Liste tous les processus :
C:\>tasklist
tuer le processus:
C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F
détails:
http://tweaks.com/windows/39559/kill-processes-from-command-prompt /
en Java, en utilisant JNA avec le Kernel32.dll bibliothèque, similaire à une solution C++. Exécute la méthode principale CtrlCSender comme un processus qui obtient simplement la console du processus pour envoyer L'événement Ctrl+C et génère l'événement. Comme il fonctionne séparément, sans console Ctrl+C l'événement n'a pas besoin d'être désactivé et activé à nouveau.
CtrlCSender.java - basé sur Nemo1024 et Kinddragon's répond.
avec un identifiant de processus connu, cette application consoless attachera la console du processus ciblé et générera un événement CTRL+C dessus.
import com.sun.jna.platform.win32.Kernel32;
public class CtrlCSender {
public static void main(String args[]) {
int processId = Integer.parseInt(args[0]);
Kernel32.INSTANCE.AttachConsole(processId);
Kernel32.INSTANCE.GenerateConsoleCtrlEvent(Kernel32.CTRL_C_EVENT, 0);
}
}
application principale - exécute CtrlCSender en tant que processus de consolation séparé
ProcessBuilder pb = new ProcessBuilder();
pb.command("javaw", "-cp", System.getProperty("java.class.path", "."), CtrlCSender.class.getName(), processId);
pb.redirectErrorStream();
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
Process ctrlCProcess = pb.start();
ctrlCProcess.waitFor();
un de mes amis a suggéré une façon complètement différente de résoudre le problème et cela a fonctionné pour moi. Utilisez un vbscript comme ci-dessous. Il démarre et s'applique, laisse tourner pendant 7 secondes et ferme en utilisant Ctrl+C .
"Exemple VBScript
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "notepad.exe"
WshShell.AppActivate "notepad"
WScript.Sleep 7000
WshShell.SendKeys "^C"