我可以向Windows上的应用程序发送ctrl-C(SIGINT)吗?

我可以向Windows上的应用程序发送ctrl-C(SIGINT)吗?,windows,signals,sigint,Windows,Signals,Sigint,我(过去)编写过跨平台(Windows/Unix)应用程序,当从命令行启动时,它们以相同的方式处理用户键入的Ctrl-C组合(即干净地终止应用程序) 在Windows上,是否可以从另一个(不相关)进程发送一个与该进程等效的Ctrl-C/SIGINT/以请求该进程干净地终止(使其有机会整理资源等)?编辑: 对于GUI应用程序,在Windows开发中处理此问题的“正常”方法是向进程的主窗口发送WM_CLOSE消息 对于控制台应用程序,您需要使用添加CTRL\u C\u事件 如果应用程序不尊重这一点,

我(过去)编写过跨平台(Windows/Unix)应用程序,当从命令行启动时,它们以相同的方式处理用户键入的Ctrl-C组合(即干净地终止应用程序)

在Windows上,是否可以从另一个(不相关)进程发送一个与该进程等效的Ctrl-C/SIGINT/以请求该进程干净地终止(使其有机会整理资源等)?

编辑:

对于GUI应用程序,在Windows开发中处理此问题的“正常”方法是向进程的主窗口发送WM_CLOSE消息

对于控制台应用程序,您需要使用添加
CTRL\u C\u事件


如果应用程序不尊重这一点,您可以致电。

我找到的最接近解决方案是第三方应用程序。作者列出了源代码和可执行文件。我已经验证了它在64位windows下工作(作为32位程序运行,杀死另一个32位程序),但我还没有弄清楚如何将代码嵌入windows程序(32位或64位)

工作原理:

在调试器中深入研究之后,我发现实际执行与诸如ctrl-break之类的信号相关联的行为的入口点是kernel32!CtrlRoutine。该函数与ThreadProc具有相同的原型,因此可以直接与CreateRemoteThread一起使用,而无需注入代码。但是,这不是导出的符号!在不同版本的Windows上,它位于不同的地址(甚至有不同的名称)。怎么办

这是我最后想出的解决办法。我为我的应用程序安装了控制台ctrl处理程序,然后为我的应用程序生成ctrl-break信号。当我的处理程序被调用时,我会回顾堆栈的顶部,找出传递给kernel32的参数!BaseThreadStart。我获取第一个参数,它是线程所需的开始地址,也是内核32的地址!CtrlRoutine。然后我从我的处理程序返回,表示我已经处理了信号,我的应用程序不应该被终止。回到主线程,我一直等到内核32的地址!已检索到CtrlRoutine。一旦得到它,我就在目标进程中创建一个具有发现的开始地址的远程线程。这将导致目标进程中的ctrl处理程序被评估为好像按下了ctrl-break

好的是,只有目标进程受到影响,任何进程(即使是窗口进程)都可以成为目标。一个缺点是,我的小应用程序不能在批处理文件中使用,因为当它发送ctrl-break事件以发现内核32的地址时,它会杀死它!CtrlRoutine


(如果在批处理文件中运行它,请在前面加上
start

我想我在这个问题上有点晚了,但无论如何,我会为有同样问题的人写一些东西。 这和我对这个问题的回答是一样的

我的问题是,我希望我的应用程序是一个GUI应用程序,但执行的进程应该在后台运行,而不附加任何交互式控制台窗口。我认为当父进程是控制台进程时,这个解决方案也应该起作用。不过,您可能必须删除“创建\u否\u窗口”标志

我设法用一个包装器应用程序解决了这个问题。棘手的是,文档中并没有明确说明如何使用它以及它的缺陷

我的解决方案基于所描述的内容。但这也不能真正解释所有的细节,并且有一个错误,所以这里是如何让它工作的细节

创建一个新的助手应用程序“helper.exe”。此应用程序将位于您的应用程序(父进程)和您希望能够关闭的子进程之间。它还将创建实际的子进程。您必须有此“中间人”进程,否则GenerateConsolectLevent()将失败

使用某种IPC机制从父进程与助手进程通信,让助手关闭子进程。当帮助程序获取此事件时,它调用“GenerateConsoleCtrlEvent(CTRL_BREAK,0)”,这将关闭自身和子进程。我自己使用了一个事件对象,当父进程想要取消子进程时,它会完成这个事件对象

要创建Helper.exe,请使用“创建\否\窗口”和“创建\新\流程\组”创建它。当创建子进程时,不使用标志(0)创建它,这意味着它将从其父进程派生控制台。否则将导致忽略该事件

这是非常重要的,每一步都是这样做的。我一直在尝试各种不同的组合,但这种组合是唯一有效的组合。无法发送CTRL\u C事件。它将返回成功,但会被过程忽略。CTRL\u BREAK是唯一有效的方法。这并不重要,因为它们最终都会调用ExitProcess()

您也不能使用子进程id的进程groupd id直接调用GenerateConsolectLevent(),以允许帮助进程继续运行。这也将失败


我花了一整天的时间想让它工作起来。这个解决方案对我很有效,但如果有人还有其他需要补充的,请添加。我走遍了整个网络,发现很多人都有类似的问题,但没有明确的解决办法。GenerateConsolectLevent()的工作原理也有点奇怪,所以如果有人知道有关它的更多详细信息,请与我们分享。

不知何故
GenerateConsolectLevent()
如果您为另一个进程调用它,则返回错误,但您可以附加到另一个控制台应用程序并将事件发送到所有子进程

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
}

我围绕这个主题做了一些研究,结果比我预期的更受欢迎。金德龙的回答是关键点之一

我写了一篇关于这个主题的文章,并创建了一个有效的演示程序,该程序演示了如何使用这种类型的系统以两种很好的方式关闭命令行应用程序。该帖子还列出了
Set WshShell = WScript.CreateObject("WScript.Shell")

WshShell.Run "notepad.exe"

WshShell.AppActivate "notepad"

WScript.Sleep 7000

WshShell.SendKeys "^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;
}
C:\>tasklist
C:\>Taskkill /IM firefox.exe /F
or
C:\>Taskkill /PID 26356 /F
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);
    }
}
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();
        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);
            }
        }
windows-kill -SIGINT 1234
// 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 ();
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
$ProcessID = 1234
$MemberDefinition = '
    [DllImport("kernel32.dll")]public static extern bool FreeConsole();
    [DllImport("kernel32.dll")]public static extern bool AttachConsole(uint p);
    [DllImport("kernel32.dll")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);
    public static void SendCtrlC(uint p) {
        FreeConsole();
        if (AttachConsole(p)) {
            GenerateConsoleCtrlEvent(0, p);
            FreeConsole();
        }
        AttachConsole(uint.MaxValue);
    }'
Add-Type -Name 'dummyName' -Namespace 'dummyNamespace' -MemberDefinition $MemberDefinition
[dummyNamespace.dummyName]::SendCtrlC($ProcessID) }