Windows上のアプリケーションにCtrl-C(SIGINT)を送信できますか?


91

私は(過去に)書かれたクロスプラットフォーム(Windowsの/ Unixの)コマンドラインから起動、アプリケーションは、ユーザーが入力した扱い持ってCtrl- C(きれいにアプリケーションを終了するIE)と同じ方法で組み合わせを。

Windowsで、別の(無関係の)プロセスからのプロセスに相当するCtrl- C/ SIGINT /を送信して、クリーンに終了するように要求することは可能ですか(リソースを整理する機会を与えるなど)?


1
スレッドダンプを取得するためだけに、Ctrl-CをJavaサービスプロセスに送信することに興味がありました。それが表示されますjstack確実に、この特定の問題のために代わりに使用することができます。stackoverflow.com/a/47723393/603516
ヴァジム・

回答:


31

私が解決策に到達した最も近いものはSendSignalですサードパーティアプリです。作者はソースコードと実行可能ファイルをリストします。64ビットウィンドウ(32ビットプログラムとして実行、別の32ビットプログラムを強制終了)で動作することを確認しましたが、コードをWindowsプログラム(32ビット)に埋め込む方法がわかりませんでした。または64ビット)。

使い方:

デバッガーをよく調べた結果、ctrl-breakなどのシグナルに関連付けられた動作を実際に実行するエントリポイントがkernel32!CtrlRoutineであることがわかりました。この関数のプロトタイプはThreadProcと同じであるため、コードを挿入しなくても、CreateRemoteThreadで直接使用できます。ただし、これはエクスポートされたシンボルではありません。Windowsのバージョンが異なれば、アドレスも異なります(名前も異なります)。何をすべきか?

これが私がついに思いついた解決策です。アプリのコンソールctrlハンドラーをインストールしてから、アプリのctrl-breakシグナルを生成します。ハンドラーが呼び出されると、スタックの最上位を振り返って、kernel32!BaseThreadStartに渡されたパラメーターを見つけます。最初のパラメータを取得します。これは、スレッドの目的の開始アドレスであり、kernel32!CtrlRoutineのアドレスです。次に、ハンドラーから戻り、シグナルを処理したので、アプリを終了しないように指示します。メインスレッドに戻り、kernel32!CtrlRoutineのアドレスが取得されるまで待ちます。取得したら、検出された開始アドレスを使用して、ターゲットプロセスにリモートスレッドを作成します。これにより、ターゲットプロセスのctrlハンドラーは、ctrl-breakが押されたかのように評価されます。

良い点は、ターゲットプロセスのみが影響を受け、任意のプロセス(ウィンドウ化されたプロセスも含む)をターゲットにできることです。欠点の1つは、私の小さなアプリをバッチファイルで使用できないことです。これは、kernel32!CtrlRoutineのアドレスを検出するためにctrl-breakイベントを送信するとアプリが強制終了されるためです。

startバッチファイルで実行する場合は、前に付けてください。)


2
+ 1-リンクされたコードはCtrl-CではなくCtrl-Breakを使用しますが、その表面上(GenerateConsoleCtrlEventのドキュメントを参照)はCtrl-Cに適合させることができます。
マシューマードック

8
実際にこれを行う関数があります、笑、「GenerateConsoleCtrlEvent」。参照:msdn.microsoft.com/en-us/library/ms683155(v=vs.85).aspx ここにも私の返事を参照してください。serverfault.com/a/501045/10023
Triynko

1
上記の回答に関連Githubのリンク: github.com/walware/statet/tree/master/...
meawoppl

3
Windowsは古いものですが、MicrosoftはコンソールアプリケーションAがコンソールアプリケーションBからの信号をリッスンするメカニズムを提供していないため、コンソールアプリケーションBはコンソールアプリケーションAにクリーンにシャットダウンするように指示できます。いずれにせよ、MSバッシングはさておき、SendSignalを試したところ、「CreateRemoteThreadが0x00000005で失敗しました」というメッセージが表示されました。シグナル(便利なシグナル)をリッスンするようにアプリケーションAをコーディングする方法、およびそれをシグナルする方法に関する他のアイデアはありますか?
デビッドI.マッキントッシュ2014

3
@ DavidI.McIntosh両方のプロセスで名前付きイベントを作成したいようです。そうすれば、一方から他方に信号を送ることができます。CreateEventのドキュメントを確認してください
arolson101

58

私はこのトピックについていくつかの調査を行いましたが、予想よりも人気があったことがわかりました。KindDragonの回答は重要なポイントの1つでした。

私はこのトピックについてより長いブログ投稿を書き、実用的なデモプログラムを作成しました。これは、このタイプのシステムを使用して、いくつかの優れた方法でコマンドラインアプリケーションを閉じる方法を示しています。その投稿には、私が研究で使用した外部リンクもリストされています。

つまり、これらのデモプログラムは次のことを行います。

  • .Netを使用してウィンドウが表示された状態でプログラムを開始し、pinvokeで非表示にし、6秒間実行し、pinvokeで表示し、.Netで停止します。
  • .Netを使用してウィンドウなしでプログラムを開始し、6秒間実行し、コンソールを接続してConsoleCtrlEventを発行して停止します。

編集:今ここにあるコードに興味がある人のためのKindDragonからの修正されたソリューション。最初のプログラムを停止した後に他のプログラムを開始する場合は、Ctrl-C処理を再度有効にする必要があります。そうしないと、次のプロセスが親の無効状態を継承し、Ctrl-Cに応答しません。

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();

[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);

delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);

// 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);

    //Moved this command up on suggestion from Timothy Jannace (see comments below)
    FreeConsole();

    // Must wait here. If we don't and re-enable Ctrl-C
    // handling below too fast, we might terminate ourselves.
    proc.WaitForExit(2000);

    //Re-enable Ctrl-C handling or any subsequently started
    //programs will inherit the disabled state.
    SetConsoleCtrlHandler(null, false); 
  }
}

また、AttachConsole()送信された信号が失敗した場合、たとえばスリープ状態の場合、次のような緊急時の解決策を計画します。

if (!proc.HasExited)
{
  try
  {
    proc.Kill();
  }
  catch (InvalidOperationException e){}
}

4
これに触発されて、私はそれを行う「スタンドアロン」のcppアプリを作成しました:gist.github.com/rdp/f51fb274d69c5c31b6be便利な場合に備えて。はい、このメソッドはctrl + cまたはctrl + breakを「任意の」pidに送信できるようです。
rogerdpack 2015年

DLLのインポートの1つが欠落していると、「SetConsoleCtrlHandler」がインポートされますが、これは機能します。
Derf Skren 2015

優れた投稿。素晴らしいブログ。実験プログラムでStopProgramWithInvisibleWindowUsingPinvoke関数を終了することをお勧めします。私はあなたが解決策を見つけられなかったと思ってアイデアをほとんど放棄しました
Wilmer Saint

回避するために、再度有効にするCtrl-C()wait()を削除しました。いくつかのテスト(複数の呼び出し、すでに終了したプロセスでも)を実行しましたが、副作用は見つかりませんでした(まだ?)。SetConsoleCtrlHandler(null, false);
56ka 2017年

FreeConsoleは、GenerateConsoleCtrlEventを送信した直後に呼び出す必要があります。呼び出されるまで、アプリケーションは他のコンソールに接続されます。他のコンソールが終了する前に強制終了されると、アプリケーションも終了します。
Timothy Jannace 2018

17

私はこの質問に少し遅れていると思いますが、同じ問題を抱えている人のためにとにかく何かを書きます。これは私がこの質問にしたのと同じ答えです。

私の問題は、アプリケーションをGUIアプリケーションにしたいのですが、実行されるプロセスは、インタラクティブなコンソールウィンドウを接続せずにバックグラウンドで実行する必要があるということでした。このソリューションは、親プロセスがコンソールプロセスの場合にも機能するはずだと思います。ただし、「CREATE_NO_WINDOW」フラグを削除する必要がある場合があります。

ラッパーアプリでGenerateConsoleCtrlEvent()を使用してこれを解決することができました。トリッキーな部分は、ドキュメントがそれをどのように使用できるか、そしてそれの落とし穴について実際には明確ではないということです。

私の解決策は、ここで説明されていることに基づいています。しかし、それでもすべての詳細が説明されておらず、エラーが発生したため、これを機能させる方法の詳細を以下に示します。

新しいヘルパーアプリケーション「Helper.exe」を作成します。このアプリケーションは、アプリケーション(親)と閉じたい子プロセスの間に配置されます。また、実際の子プロセスも作成します。この「仲介者」プロセスが必要です。そうしないと、GenerateConsoleCtrlEvent()が失敗します。

ある種のIPCメカニズムを使用して、親からヘルパープロセスに、ヘルパーが子プロセスを閉じる必要があることを伝えます。ヘルパーがこのイベントを取得すると、「GenerateConsoleCtrlEvent(CTRL_BREAK、0)」を呼び出し、それ自体と子プロセスを閉じます。子プロセスをキャンセルしたいときに親が完了するイベントオブジェクトを自分で使用しました。

Helper.exeを作成するには、CREATE_NO_WINDOWおよびCREATE_NEW_PROCESS_GROUPを使用して作成します。また、子プロセスを作成するときは、親からコンソールを派生させることを意味するフラグ(0)なしで作成します。これを怠ると、イベントが無視されます。

各ステップがこのように行われることが非常に重要です。私はさまざまな種類の組み合わせを試してきましたが、機能するのはこの組み合わせだけです。CTRL_Cイベントを送信することはできません。成功を返しますが、プロセスによって無視されます。CTRL_BREAKが機能する唯一のものです。どちらも最終的にExitProcess()を呼び出すため、実際には問題ではありません。

また、子プロセスIDのプロセスグループ化IDを使用してGenerateConsoleCtrlEvent()を呼び出して、ヘルパープロセスが存続できるようにすることもできません。これも失敗します。

私はこれを機能させるために一日を過ごしました。この解決策は私にとってはうまくいきますが、他に何か追加するものがあれば、やってください。私はネット中を行き来して、同様の問題を抱えている多くの人々を見つけましたが、問題に対する明確な解決策はありませんでした。GenerateConsoleCtrlEvent()の動作も少し奇妙なので、誰かがそれについての詳細を知っているなら、共有してください。


6
解決してくれてありがとう!これは、WindowsAPIがそのようなひどい混乱であるもう1つの例です。
vitaut 2012年

8

編集:

GUIアプリの場合、Windows開発でこれを処理する「通常の」方法は、プロセスのメインウィンドウにWM_CLOSEメッセージを送信することです。

コンソールアプリの場合、SetConsoleCtrlHandlerを使用してを追加する必要がありますCTRL_C_EVENT

アプリケーションがそれを尊重しない場合は、TerminateProcessを呼び出すことができます。


コマンドラインアプリ(ウィンドウなし)でこれを実行したいと思います。TerminateProcessは強制強制終了メカニズム(SIGKILLと同様)であるため、終了アプリケーションにクリーンアップする機会を与えません。
マシューマードック

@Matthew Murdoch:コンソールアプリでこれを処理する方法に関する情報を含むように更新されました。同じメカニズムを介して、Windowsのシャットダウン、コンソールのクローズなど、他のイベントをキャッチすることもできます。詳細については、MSDNを参照してください。
リードコプシー

@Reed Copsey:しかし、これは別のプロセスから開始したいと思います。GenerateConsoleCtrlEvent(SetConsoleCtrlHandlerから参照)を見てきましたが、これは、終了するプロセスが終了を行うプロセスと同じ「プロセスグループ」にある場合にのみ機能するようです...何かアイデアはありますか?
マシューマードック

1
マシュー:私が持っている唯一のアイデアは、プロセスを見つけ、その親(コンソール)を見つけ、親のメインウィンドウハンドル(コンソール)を取得し、SendKeysを使用してCtrl + Cを直接送信することです。これにより、コンソールプロセスがCTRL_C_EVENTを受信するはずです。しかし、それを試したことがありません-それがどれほどうまくいくかはわかりません。
リードコプシー

ここでのSendKeysと同等のC ++のだ stackoverflow.com/questions/6838363/...も参照stackoverflow.com/questions/10407769/... ...どうやらそれはその後も動作する保証はないですけれども
rogerdpack

7

GenerateConsoleCtrlEvent()別のプロセスに対して呼び出すと、どういうわけかエラーが返されますが、別のコンソールアプリケーションに接続して、すべての子プロセスにイベントを送信できます。

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
}

と制御を返す方法は?次に、control-c、stoparプログラムを送信しますが、彼の手では何もできません。
Yaroslav L.

コントロールをどこに戻すか?
KindDragon 2013

SetConsoleCtrlHandler(NULL、FALSE)を呼び出してハンドラーを削除すると思います。他のさまざまな応答を参照してください。
rogerdpack 2015年

6

これが私のC ++アプリで使用するコードです。

良い点:

  • コンソールアプリから動作します
  • Windowsサービスから動作します
  • 遅延は必要ありません
  • 現在のアプリを閉じません

ネガティブポイント:

  • メインコンソールが失われ、新しいコンソールが作成されます(FreeConsoleを参照)。
  • コンソールの切り替えは奇妙な結果をもたらします...

// 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;
}

使用例:

DWORD dwProcessId = ...;
if (signalCtrl(dwProcessId, CTRL_C_EVENT))
{
    cout << "Signal sent" << endl;
}

4
        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);
            }
        }

2

現時点ではそうではないので、それは明確にする必要があります。 Ctrl-Cを送信するためのSendSignalの変更およびコンパイルされたバージョンがあります(デフォルトでは、Ctrl + Breakのみを送信します)。ここにいくつかのバイナリがあります:

(2014-3-7):Ctrl-Cを使用して32ビットバージョンと64ビットバージョンの両方をビルドしました。これはSendSignalCtrlC.exeと呼ばれ、https ://dl.dropboxusercontent.com/u/49065779/からダウンロードできます。 sendsignalctrlc / x86 / SendSignalCtrlC.exe https://dl.dropboxusercontent.com/u/49065779/sendsignalctrlc/x86_64/SendSignalCtrlC.exe-Juraj Michalak

念のため、これらのファイルもミラーリングしました:
32ビットバージョン:https//www.dropbox.com/s/r96jxglhkm4sjz2/SendSignalCtrlC.exe?
dl = 0 64ビットバージョン:https//www.dropbox.com /s/hhe0io7mcgcle1c/SendSignalCtrlC64.exe?dl=0

免責事項:私はそれらのファイルを作成しませんでした。コンパイルされた元のファイルは変更されていません。テストされた唯一のプラットフォームは64ビットのWindows7です。http: //www.latenighthacking.com/projects/2003/sendSignal/で入手可能なソースを適合させ、自分でコンパイルすることをお勧めします。


2

Javaでは、C ++ソリューションと同様に、Kernel32.dllライブラリでJNAを使用します。CtrlCSender mainメソッドをプロセスとして実行します。このプロセスは、プロセスのコンソールを取得してCtrl + Cイベントを送信し、イベントを生成します。コンソールなしで個別に実行されるため、Ctrl + Cイベントを無効にして再度有効にする必要はありません。

CtrlCSender.java - Nemo1024KindDragonの回答に基づいています。

既知のプロセスIDが与えられると、このconsolessアプリケーションは、ターゲットプロセスのコンソールを接続し、その上でCTRL + Cイベントを生成します。

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);
    }
}

メインアプリケーション-CtrlCSenderを個別のコンソールプロセスとして実行します

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();

うーん、これをPowerShellプロセスに対して実行すると、プロセスは存続します。一方、興味深いことに、JVMでインプロセスを実行すると、実際にVMがダウンします。PowerShellプロセスがこの手法に応答しない理由を知っていますか?
Groostav


1

コマンドラインでpython3.xを使用できる場合、ここから見つけた解決策は非常に簡単です。まず、次の内容のファイル(ctrl_c.py)を保存します。

import ctypes
import sys

kernel = ctypes.windll.kernel32

pid = int(sys.argv[1])
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)

次に、電話します。

python ctrl_c.py 12345

それでも問題が解決しない場合は、windows-killプロジェクトを試すことをお勧めします:https//github.com/alirdn/windows-kill


0

私の友人は、問題を解決するためのまったく異なる方法を提案し、それは私のために働きました。以下のようなvbscriptを使用します。アプリケーションが起動し、7秒間実行して、ctrl + cを使用して閉じます。

'VBScriptの例

Set WshShell = WScript.CreateObject("WScript.Shell")

WshShell.Run "notepad.exe"

WshShell.AppActivate "notepad"

WScript.Sleep 7000

WshShell.SendKeys "^C"

これは、AppActive(フォアグラウンドにする)ができるウィンドウがアプリケーションにある場合に機能します。
rogerdpack 2015年

0

私はこれがすべて複雑すぎることに気づき、回避策としてSendKeysを使用してコマンドラインウィンドウ(つまり、cmd.exeウィンドウ)にCTRL-Cキーストロークを送信しました。


0
// Send [CTRL-C] to interrupt a batch file running in a Command Prompt window, even if the Command Prompt window is not visible,
// without bringing the Command Prompt window into focus.
// [CTRL-C] will have an effect on the batch file, but not on the Command Prompt  window itself -- in other words,
// [CTRL-C] will not have the same visible effect on a Command Prompt window that isn't running a batch file at the moment
// as bringing a Command Prompt window that isn't running a batch file into focus and pressing [CTRL-C] on the keyboard.
ulong ulProcessId = 0UL;
// hwC = Find Command Prompt window HWND
GetWindowThreadProcessId (hwC, (LPDWORD) &ulProcessId);
AttachConsole ((DWORD) ulProcessId);
SetConsoleCtrlHandler (NULL, TRUE);
GenerateConsoleCtrlEvent (CTRL_C_EVENT, 0UL);
SetConsoleCtrlHandler (NULL, FALSE);
FreeConsole ();

0

SIGINTは、windows-killを使用し、構文によってプログラムに送信できますwindows-kill -SIGINT PID。ここでPID、Microsoftのpslistから取得できます。

SIGINTのキャッチに関しては、プログラムがPythonである場合、このソリューションのようにSIGINT処理/キャッチを実装できます。


-1

プロセスIDに基づいて、シグナルをプロセスに送信して、強制的または正常に終了するか、その他のシグナルを送信できます。

すべてのプロセスを一覧表示します:

C:\>tasklist

プロセスを強制終了するには:

C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F

詳細:

http://tweaks.com/windows/39559/kill-processes-from-command-prompt/


おそらくこれはsigtermを送信しますか?
teknopaul

TaskkillはCTRL-Cを送信しません。Windowsの信号を確認してください。
ジョシ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.