私は(過去に)書かれたクロスプラットフォーム(Windowsの/ Unixの)コマンドラインから起動、アプリケーションは、ユーザーが入力した扱い持ってCtrl- C(きれいにアプリケーションを終了するIE)と同じ方法で組み合わせを。
Windowsで、別の(無関係の)プロセスからのプロセスに相当するCtrl- C/ SIGINT /を送信して、クリーンに終了するように要求することは可能ですか(リソースを整理する機会を与えるなど)?
私は(過去に)書かれたクロスプラットフォーム(Windowsの/ Unixの)コマンドラインから起動、アプリケーションは、ユーザーが入力した扱い持ってCtrl- C(きれいにアプリケーションを終了するIE)と同じ方法で組み合わせを。
Windowsで、別の(無関係の)プロセスからのプロセスに相当するCtrl- C/ SIGINT /を送信して、クリーンに終了するように要求することは可能ですか(リソースを整理する機会を与えるなど)?
回答:
私が解決策に到達した最も近いものは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
バッチファイルで実行する場合は、前に付けてください。)
私はこのトピックについていくつかの調査を行いましたが、予想よりも人気があったことがわかりました。KindDragonの回答は重要なポイントの1つでした。
私はこのトピックについてより長いブログ投稿を書き、実用的なデモプログラムを作成しました。これは、このタイプのシステムを使用して、いくつかの優れた方法でコマンドラインアプリケーションを閉じる方法を示しています。その投稿には、私が研究で使用した外部リンクもリストされています。
つまり、これらのデモプログラムは次のことを行います。
編集:今ここにあるコードに興味がある人のための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){}
}
wait()
を削除しました。いくつかのテスト(複数の呼び出し、すでに終了したプロセスでも)を実行しましたが、副作用は見つかりませんでした(まだ?)。SetConsoleCtrlHandler(null, false);
私はこの質問に少し遅れていると思いますが、同じ問題を抱えている人のためにとにかく何かを書きます。これは私がこの質問にしたのと同じ答えです。
私の問題は、アプリケーションを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()の動作も少し奇妙なので、誰かがそれについての詳細を知っているなら、共有してください。
編集:
GUIアプリの場合、Windows開発でこれを処理する「通常の」方法は、プロセスのメインウィンドウにWM_CLOSEメッセージを送信することです。
コンソールアプリの場合、SetConsoleCtrlHandlerを使用してを追加する必要がありますCTRL_C_EVENT
。
アプリケーションがそれを尊重しない場合は、TerminateProcessを呼び出すことができます。
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
}
これが私のC ++アプリで使用するコードです。
良い点:
ネガティブポイント:
// 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;
}
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);
}
}
現時点ではそうではないので、それは明確にする必要があります。 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/で入手可能なソースを適合させ、自分でコンパイルすることをお勧めします。
Javaでは、C ++ソリューションと同様に、Kernel32.dllライブラリでJNAを使用します。CtrlCSender mainメソッドをプロセスとして実行します。このプロセスは、プロセスのコンソールを取得してCtrl + Cイベントを送信し、イベントを生成します。コンソールなしで個別に実行されるため、Ctrl + Cイベントを無効にして再度有効にする必要はありません。
CtrlCSender.java - Nemo1024とKindDragonの回答に基づいています。
既知のプロセス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();
はい。windows-kill
このプロジェクトは、あなたが望むものを正確に行います。
windows-kill -SIGINT 1234
コマンドラインで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
私の友人は、問題を解決するためのまったく異なる方法を提案し、それは私のために働きました。以下のようなvbscriptを使用します。アプリケーションが起動し、7秒間実行して、ctrl + cを使用して閉じます。。
'VBScriptの例
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "notepad.exe"
WshShell.AppActivate "notepad"
WScript.Sleep 7000
WshShell.SendKeys "^C"
// 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 ();
SIGINTは、windows-killを使用して、構文によってプログラムに送信できますwindows-kill -SIGINT PID
。ここでPID
、Microsoftのpslistから取得できます。
SIGINTのキャッチに関しては、プログラムがPythonである場合、このソリューションのようにSIGINT処理/キャッチを実装できます。
プロセス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/
jstack
確実に、この特定の問題のために代わりに使用することができます。stackoverflow.com/a/47723393/603516