Can I send a ctrl-C (SIGINT) to an application on Windows?

前端 未结 17 2821
甜味超标
甜味超标 2020-11-22 09:15

I have (in the past) written cross-platform (Windows/Unix) applications which, when started from the command line, handled a user-typed Ctrl-C combinat

17条回答
  •  生来不讨喜
    2020-11-22 09:56

    I have done some research around this topic, which turned out to be more popular than I anticipated. KindDragon's reply was one of the pivotal points.

    I wrote a longer blog post on the topic and created a working demo program, which demonstrates using this type of system to close a command line application in a couple of nice fashions. That post also lists external links that I used in my research.

    In short, those demo programs do the following:

    • Start a program with a visible window using .Net, hide with pinvoke, run for 6 seconds, show with pinvoke, stop with .Net.
    • Start a program without a window using .Net, run for 6 seconds, stop by attaching console and issuing ConsoleCtrlEvent

    Edit: The amended solution from KindDragon for those who are interested in the code here and now. If you plan to start other programs after stopping the first one, you should re-enable Ctrl-C handling, otherwise the next process will inherit the parent's disabled state and will not respond to 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); 
      }
    }
    

    Also, plan for a contingency solution if AttachConsole() or the sent signal should fail, for instance sleeping then this:

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

提交回复
热议问题