Start a EXE file from a service and stop it by sen

2020-02-16 04:20发布

问题:

I have written a service which starts a java.exe or ruby.exe (I know there are some solutions but I need my own service for some reasons).

The service works find so far, I gather my configs from the registry, then start the services. When the service get stopped I get my processes and send a .Kill(). So far so good. But I found out, that the .Kill() is a problem due the fact that the ruby.exe (I use thin to start a service) or the java.exe (I start a SOLR with it) listen on a tcp socket port. If this port get in use and I kill the process windows will block the ports for 72 seconds (by design).

If I do a solr:start and a thin -start from a shell command shell and stop it using the Ctrl+C the processes terminate and the port is available to use instantly.

So my guess is: If I manage to send a ctrl-c to the process it terminate correctly.

So I found this thread How to Run an exe from windows service and stop service when the exe process quits? where a proof of concept is posted. But by starting a process from a window service I don't have a windowHandle.

I start my service like this:

m_process.StartInfo = new ProcessStartInfo
                                  {
                                      FileName = "java"
                                      , Arguments = arguments
                                      , UseShellExecute = true
                                      , WorkingDirectory = workDirectory
                                      , CreateNoWindow = false
                                  };
m_process.Start();

Where arguments contais the jetty data to start the SOLR or in the ruby case I use "ruby.exe thin start ...".

Now on stopping the service I tried:

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

foreach (int i in m_processList)
  {
    MyLogEvent(Process.GetProcessById(i).MainModule.ModuleName);
    MyLogEvent(Process.GetProcessById(i).MainWindowTitle);
    try
    {
      IntPtr ptr = FindWindow(null, Process.GetProcessById(i).MainWindowTitle);
      {
        SetForegroundWindow(ptr);
        Thread.Sleep(1000);
        InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.CANCEL);
        // SendKeys.Send("^{BREAK}");
        Thread.Sleep(1000);
      }
      //Process.GetProcessById(i).Kill();
    }
    catch(Exception ex)
    {
      MyLogEvent(ex.ToString());
      Process.GetProcessById(i).Kill();
    }
  }

But as I don't have a WindowTitle, I guess I don't even have a window I can't allocate the process like this.

So does anyone have a idea how I can allocate process and send the stop signal to it? I can live with just killing the process, but this just a service restart impossible without waiting a long time. Thanks for any hints, tips and solutions.

回答1:

GenerateConsoleCtrlEvent might work

 m_process.StartInfo = new ProcessStartInfo
                              {
                                  FileName = "java"
                                  , Arguments = arguments
                                  , UseShellExecute = true
                                  , WorkingDirectory = workDirectory
                                  , CreateNoWindow = false
                              };
 var process = m_process.Start();

When time to kill child app...

 GenerateConsoleCtrlEvent(CTRL_C_EVENT, process.Id);

member declarations

 public const UInt32 CTRL_C_EVENT = 0;

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent,
   uint dwProcessGroupId);