Я немного поиграл с созданием GUI для автоматизации сервера командной строки (хорошо, это Minecraft BDS). Пока я проводил некоторое тестирование, я случайно (более одного раза) остановил отладчик до того, как сервер был должным образом выключен, что оставило процесс исполняемого файла «осиротевшим». Я даже не смог найти его в диспетчере задач, поэтому я прибегнул к добавлению проверки в своем коде для любых уже запущенных экземпляров bedrock_server.exe
, а затем просто убил ее. Однако я бы хотел бы , чтобы позволить программному обеспечению сервера завершать работу "нормально", а не просто завершать процесс.
Мой вопрос в том, что командное окно не отображается и больше не «привязан» к экземпляру моего приложения, есть ли способ отправить ему команду stop
, чтобы она могла корректно завершить работу?
Я пытался использовать StandardInput
, но поскольку текущий экземпляр моего приложения не является экземпляром, который фактически запустил процесс, он не может записать в StandardInput
потерянного процесса:
myProc.StandardInput.WriteLine("stop")
приводит к исключению, System.InvalidOperationException: 'StandardIn has not been redirected.'
Я думал об использовании SendKeys
:
AppActivate(myProc.Id)
My.Computer.Keyboard.SendKeys("stop")
, но AppActivate
не удается, когда я пытаюсь AppActivate
процесс с исключением, System.ArgumentException: 'Process '[Id]' was not found.'
Использование myProc.Kill()
действительно завершает процесс, мне просто интересно, может ли быть менее «ядерный» вариант.
Другая причина, по которой я хотел бы это выяснить, заключается в том, что я хотел бы , чтобы иметь возможность повторно использовать существующий / работающий процесс сервера, а не убивать его. Однако, если я не могу понять, как подключиться к этому процессу для выдачи команд, мой единственный вариант (на данный момент) - завершить процесс и начать заново с экземпляром fre sh.
МОЙ КОД
Для справки, код, который я использую для запуска сервера:
'------------------------------
'PUBLIC VARIABLE DECLARATION LOCATED IN ANOTHER FILE
Public BDS_PROC As Process
'------------------------------
Dim ServerStartInfo As New ProcessStartInfo
With ServerStartInfo
.FileName = <PATH TO bedrock_server.exe>
.UseShellExecute = False
.WindowStyle = ProcessWindowStyle.Hidden
.CreateNoWindow = True
.RedirectStandardError = True
.RedirectStandardOutput = True
.RedirectStandardInput = True
End With
BDS_PROC = New Process
With BDS_PROC
.StartInfo = ServerStartInfo
.EnableRaisingEvents = True
AddHandler .OutputDataReceived, AddressOf BDSMessageReceived
AddHandler .ErrorDataReceived, AddressOf BDSErrorReceived
.Start()
.BeginErrorReadLine()
.BeginOutputReadLine()
End With
и полная проверка текущих "потерянных" запущенных процессов (с моими неудачными попытками для отправки закомментированной команды stop
:
Public Function IsInstanceRunning() As Boolean
Dim IsRunning As Boolean = True
Dim Proc As Process() = Process.GetProcessesByName("bedrock_server")
If Proc.Count > 0 Then
If MessageBox.Show("The server is already running [" & Proc.Count.ToString & " instance(s)]." & vbCrLf & vbCrLf &
"Do you want to stop the currently running server?",
"SERVER RUNNING",
MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) = DialogResult.Yes Then
Try
For p As Integer = 0 To Proc.Count - 1
'Proc(p).StandardInput.WriteLine("stop")
Proc(p).Kill()
Next p
IsRunning = False
Catch ex As Exception
'Try
' AppActivate(Proc(0).Id)
' My.Computer.Keyboard.SendKeys("stop")
'Catch ex2 As Exception
'End Try
MessageBox.Show(ex.Message)
End Try
End If
End If
Return IsRunning
End Function
РЕДАКТИРОВАТЬ:
Я нашел предложение с кодом из вопроса на C# Угол и попытался принять эту реализацию для моих целей. Я не получил никаких исключений или ошибок, но, к сожалению, он не остановил сервер. Вот этот код для справки, на случай, если он кому-то поможет:
Imports System.Runtime.InteropServices
Imports System.Text
Module PUBLIC
<DllImport("kernel32.dll")>
Private Function GetConsoleTitle(ByVal lpConsoleTitle As StringBuilder, ByVal nSize As UInteger) As UInteger
End Function
<DllImport("user32.dll")>
Private Function FindWindow(ByVal ZeroOnly As IntPtr, ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Private Function AttachConsole(ByVal dwProcessId As UInteger) As Boolean
End Function
<DllImport("kernel32")>
Private Function FreeConsole() As Boolean
End Function
<DllImport("user32.dll")>
Private Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
End Function
Public Function IsInstanceRunning() As Boolean
Dim IsRunning As Boolean = True
Dim Proc As Process() = Process.GetProcessesByName("bedrock_server")
If Proc.Count > 0 Then
If MessageBox.Show("The server is already running [" & Proc.Count.ToString & " instance(s)]." & vbCrLf & vbCrLf &
"Do you want to stop the currently running server?",
"SERVER RUNNING",
MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) = DialogResult.Yes Then
Try
For p As Integer = 0 To Proc.Count - 1
FreeConsole()
Dim ok As Boolean = AttachConsole(CUInt(Proc(p).Id))
If Not ok Then
Dim [error] As Integer = Marshal.GetLastWin32Error()
MessageBox.Show([error].ToString)
Else
Dim sb As StringBuilder = New StringBuilder(256)
GetConsoleTitle(sb, 256UI)
Dim hWnd As IntPtr = FindWindow(IntPtr.Zero, sb.ToString())
SetForegroundWindow(hWnd)
SendKeys.SendWait("stop{ENTER}")
End If
Next p
IsRunning = False
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End If
End If
Return IsRunning
End Function
End Module
Проблема, с которой я сталкиваюсь, заключается в том, что, поскольку консольное приложение было запущено без окна, нет ничего на самом деле для меня, чтобы подключиться с этим кодом. Я все еще вынужден Kill()
процесс.