Process (CMD) is not closed hence no new process can be started - c#

I created a CMDHandler class to handle all my CMD commands i'm having in my project.
For example:
CMDHandler.Run("/c \"" + DWClientSetupChainerV1 + "\"");
CMDHandler.Run("taskkill /F /IM \"DWClientSetupChainerV1 .exe\"");
Though it seems like the process of my CMDHandler is never closed and therefore it's not able to start a second one. Does someone have an idea what i'm doing wrong?
FYI: The Exitcode is not always been used.
namespace Automated_Tests
{
class CMDHandler
{
private static readonly ILogger logger = LoggerFactory.Create(typeof(Program));
public int Run(string command)
{
var proc = new Process();
try
{
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "CMD",
Arguments = command,
UseShellExecute = false
};
proc = Process.Start(startInfo);
proc.WaitForExit();
return proc.ExitCode;
}
catch (Exception ex)
{
logger.Log(LogLevel.Error, ex.Message);
}
finally
{
proc.Close();
}
return 0;
}
}
}

I found a workaround by creating separate tasks for my CMDHandler.Run method:
Task setupTask = new Task(() => CMDHandler.Run("/c \"" + DWClientSetupChainerV1 + "\""));
setupTask.Start();
Thread.Sleep(10000);
Task killSetupTask = new Task(() => CMDHandler.Run("/c taskkill /F /IM \"DWClientSetupChainerV1.exe\"", true));
killSetupTask.Start();
Task.WaitAll(setupTask, killSetupTask);

Related

Process.Start cannot run batch file properly

I am trying to run a batch file using C#
The batch file for the test purposes contains
msg * Test
It works if I run it manually.
Then I use the following code to run this .bat file
filePath = full path to batch file
var startInfo = new ProcessStartInfo
{
Arguments = "/C \"" + filePath + "\"",
FileName = "cmd.exe",
UseShellExecute = true
};
Process p = Process.Start(startInfo);
and it does not work ->
cannot find msg
What I am doing wrong?
P.S. the batch file should not be changed.
Try this way:
batchfile:
set "msg=%SystemRoot%\System32\msg.exe"
if not exist "%msg%" set "msg=%SystemRoot%\Sysnative\msg.exe"
"%msg%" * Hello
code:
string sFile = <full path to batch file>;
Process.Start("cmd.exe", "/c " + sFile);
Probably need some authorization, you may try the following code:
static void ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.Domain = "domain"; // Your own domain
processInfo.UserName = "userName"; // Your own user name
System.Security.SecureString s = new System.Security.SecureString();
s.AppendChar('p'); // Your own password
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('o');
s.AppendChar('r');
s.AppendChar('d');
processInfo.Password = s;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
// Warning: This approach can lead to deadlocks, see Edit #2
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" :
output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" :
error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
static void Main()
{
ExecuteCommand(#"C:\displayMsg.bat");
}
You may check your domain in Control Panel >> User Account >> Manage User Accounts
Source of reference: source
The problem is the location of the file (msg.exe) in the different OS versions (32bit/64bit)
I suppose it helps How can I execute msg.exe by C# in windows?
Edited:
It works fine -
class Program
{
static void Main(string[] args)
{
int ExitCode;
try
{
var returnedMsgPath = string.Empty;
if (LocateMsgExe(out returnedMsgPath))
{
var startInfo = new ProcessStartInfo()
{
FileName = returnedMsgPath,
Arguments = #"* /v Hello",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
var p = Process.Start(startInfo);
p.WaitForExit();
// *** Read the streams ***
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
ExitCode = p.ExitCode;
MessageBox.Show("output >>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
p.Close();
}
else
{
MessageBox.Show("Not found");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public static bool LocateMsgExe(out string returnedMsgPath)
{
returnedMsgPath = null;
string[] msgPaths = new string[] { Environment.ExpandEnvironmentVariables(#"%windir%\system32\msg.exe"),
Environment.ExpandEnvironmentVariables(#"%windir%\sysnative\msg.exe") };
foreach (string msgPath in msgPaths)
{
if (File.Exists(msgPath))
{
returnedMsgPath = msgPath;
return true;
}
}
return false;
}
}

Reliably kill node.js process launched via cmd process in a .NET Core app

What am I trying to solve
To improve my build pipeline, I'd like to add an end-to-end test step. I'm planning to achieve it by means of a CLI tool (.NET "Console App"). The tool will spin up and orchestrate a few npm/node commands (processes).
More specifically, there will be:
a back-end process;
a front-end process;
and a test process.
When a test process (3) completes, the CLI tool should terminate back-end (1) and front-end (2) processes gracefully, plus return 0 exit code if every orchestrated process has successfully terminated.
Trouble
In my Minimal, Complete, and Verifiable example below I'm trying to spin up a process serviceAlikeProcess and a failing process (brokenWithErrorProcess). When the latter one fails, I'm trying to forcibly terminate the former one via Kill(process) method.
!!! As it is suggested here, the node/npm processes are being launched via cmd process. I.e. I'm first spinning up a cmd process, and then write node test.js to its stdin stream. The node process gets launched just fine but when the cmd process is terminated later, the node process keeps running and producing the output.
I suppose this happens due to the fact cmd and node processes are not getting linked in a parent-child relationship (because if I manually terminate the cmd process from a Task Manager, I observe same exact behavior).
Question
How do I reliably kill both processes?
Idea: I was thinking about capturing the node process' pid and then terminate both cmd and node processes myself, but I haven't found a way to capture that pid...
Code
Program.cs
using System;
using System.Diagnostics;
using System.IO;
namespace RunE2E
{
public class Program
{
static string currentDirectory = Directory.GetCurrentDirectory();
public static int Main(string[] args)
{
var serviceAlikeProcess = StartProcessViaCmd("node", "test.js", "");
var brokenWithErrorProcess = StartProcessViaCmd("npm", "THIS IS NOT A REAL COMMAND, THEREFORE EXPECTED TO FAIL", "");
brokenWithErrorProcess.Exited += (_, __) => KillProcess(serviceAlikeProcess);
serviceAlikeProcess.WaitForExit();
return serviceAlikeProcess.ExitCode;
}
private static Process StartProcessViaCmd(string command, string arguments, string workingDirectory)
{
workingDirectory = NormalizeWorkingDirectory(workingDirectory);
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
FileName = "cmd",
Arguments = arguments,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
}
};
process.ErrorDataReceived += (_, e) => handle(command, arguments, workingDirectory, "ERROR", e.Data);
process.OutputDataReceived += (_, e) => handle(command, arguments, workingDirectory, "OUTPUT", e.Data);
try
{
Console.WriteLine($"[{workingDirectory}] {command} {arguments}");
var _ = process.Start();
process.BeginOutputReadLine();
process.StandardInput.WriteLine($"{command} {arguments} & exit");
}
catch (Exception exc)
{
Console.WriteLine($"[{workingDirectory}] {command} {arguments} : {exc}");
throw;
}
return process;
}
static string NormalizeWorkingDirectory(string workingDirectory)
{
if (string.IsNullOrWhiteSpace(workingDirectory))
return currentDirectory;
else if (Path.IsPathRooted(workingDirectory))
return workingDirectory;
else
return Path.GetFullPath(Path.Combine(currentDirectory, workingDirectory));
}
static Action<string, string, string, string, string> handle =
(string command, string arguments, string workingDirectory, string level, string message) =>
Console.WriteLine($"[{workingDirectory}] {command} {arguments} {level}: {message}");
static void KillProcess(Process process)
{
if (process != null && !process.HasExited)
process.Kill();
}
}
}
test.js
setInterval(() => {
console.info(new Date());
}, 1000);
Screenshots
Before launching any processes programmatically
After the `cmd` process is killed
Console output
I really really don't like ending up answering my own question(s), especially when the answer is based on a hack-ish way of achieving the results.
However, I understand that this may save somebody else their time. So, here's my solution:
Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
namespace RunE2E
{
public class Program
{
static string currentDirectory = Directory.GetCurrentDirectory();
public static int Main(string[] args)
{
var serviceAlikeProcessResult = StartProcessViaCmd("node", "test.js", "");
var serviceAlikeProcess = serviceAlikeProcessResult.MainProcess;
var brokenWithErrorResult = StartProcessViaCmd("npm", "THIS IS NOT A REAL COMMAND, THEREFORE EXPECTED TO FAIL", "");
var brokenWithErrorProcess = brokenWithErrorResult.MainProcess;
brokenWithErrorProcess.Exited += (_, __) =>
{
KillProcesses("Front-End", serviceAlikeProcessResult.MainProcess, serviceAlikeProcessResult.CreatedProcesses);
KillProcesses("E2E-Test", brokenWithErrorResult.MainProcess, brokenWithErrorResult.CreatedProcesses);
};
serviceAlikeProcess.WaitForExit();
return serviceAlikeProcess.ExitCode;
}
private static CommandStartResult StartProcessViaCmd(string command, string arguments, string workingDirectory)
{
workingDirectory = NormalizeWorkingDirectory(workingDirectory);
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
FileName = "cmd",
Arguments = arguments,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
},
};
var createdProcesses = new List<Process>();
process.ErrorDataReceived += (_, e) => handle(command, arguments, workingDirectory, "ERROR", e.Data);
process.OutputDataReceived += (_, e) => handle(command, arguments, workingDirectory, "", e.Data);
var commandId = $"[{workingDirectory}] {command} {arguments}";
try
{
WriteLine(commandId);
createdProcesses = StartProcessAndCapture(commandId, process);
process.BeginOutputReadLine();
process.StandardInput.WriteLine($"{command} {arguments} & exit");
}
catch (Exception exc)
{
WriteLine($"{commandId}: {exc}");
throw;
}
return new CommandStartResult
{
MainProcess = process,
CreatedProcesses = createdProcesses,
};
}
static List<Process> StartProcessAndCapture(string commandId, Process processToStart)
{
var before = Process.GetProcesses().ToList();
var beforePidSet = new HashSet<int>(before.Select(process => process.Id));
var _ = processToStart.Start();
Thread.Sleep(3000);
var after = Process.GetProcesses().ToList();
var newlyCreatedProcessIdList = new HashSet<int>(after.Select(process => process.Id));
newlyCreatedProcessIdList.ExceptWith(beforePidSet);
var createdProcesses = after.Where(process => newlyCreatedProcessIdList.Contains(process.Id)).ToList();
foreach (var process in createdProcesses)
WriteLine($"{commandId} ||| [{process.Id}] {process.ProcessName}", ConsoleColor.Blue);
return createdProcesses;
}
static string NormalizeWorkingDirectory(string workingDirectory)
{
if (string.IsNullOrWhiteSpace(workingDirectory))
return currentDirectory;
else if (Path.IsPathRooted(workingDirectory))
return workingDirectory;
else
return Path.GetFullPath(Path.Combine(currentDirectory, workingDirectory));
}
static Action<string, string, string, string, string> handle =
(string command, string arguments, string workingDirectory, string level, string message) =>
{
var defaultColor = Console.ForegroundColor;
Write($"[{workingDirectory}] ");
Write($"{command} ", ConsoleColor.DarkGreen);
Write($"{arguments}", ConsoleColor.Green);
Write($"{level} ", level == "" ? defaultColor : ConsoleColor.Red);
WriteLine($": {message}");
};
static void KillProcesses(string prefix, Process baseProcess, List<Process> processList)
{
processList = baseProcess == null ?
processList :
processList.Where(process => process.Id != baseProcess.Id).Append(baseProcess).ToList();
foreach (var process in processList)
KillProcess(prefix, process);
}
static void KillProcess(string prefix, Process process)
{
if (process != null && !process.HasExited)
try
{
WriteLine(prefix + " | Kill (" + process.ProcessName + ") [" + process.Id + "]");
process.Kill();
}
catch (Win32Exception win32exc)
{
WriteLine(prefix + " | Kill (" + process.ProcessName + ") [" + process.Id + "]: " + win32exc.Message);
}
}
static void WaitForExit(Process process)
{
while (process.HasExited == false) { }
}
static object console = new object();
static void Write(string text, ConsoleColor? color = null)
{
lock (console)
{
var original = Console.ForegroundColor;
Console.ForegroundColor = color.HasValue ? color.Value : original;
Console.Write(text);
Console.ForegroundColor = original;
}
}
static void WriteLine(string text = null, ConsoleColor? color = null)
{
lock (console)
{
var original = Console.ForegroundColor;
Console.ForegroundColor = color.HasValue ? color.Value : original;
Console.WriteLine(text);
Console.ForegroundColor = original;
}
}
}
class CommandStartResult
{
public Process MainProcess { get; set; }
public List<Process> CreatedProcesses { get; set; }
}
}
Additionally, one may want to use the following methods when dealing with .NET Core processes.
private static CommandStartResult StartDotnetProcess(string arguments, string workingDirectory)
{
var command = "dotnet";
workingDirectory = NormalizeWorkingDirectory(workingDirectory);
var process = PrepareProcess(command, arguments, workingDirectory);
var createdProcesses = new List<Process>();
var commandId = $"[{workingDirectory}] {command} {arguments}";
try
{
WriteLine(commandId);
createdProcesses = StartProcessAndCapture(commandId, process);
process.BeginOutputReadLine();
}
catch (Exception exc)
{
WriteLine($"{commandId} : {exc}");
throw;
}
return new CommandStartResult
{
MainProcess = process,
CreatedProcesses = createdProcesses,
};
}
private static Process PrepareProcess(
string command,
string arguments,
string workingDirectory
)
{
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
FileName = command,
Arguments = arguments,
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
},
};
process.ErrorDataReceived += (_, e) => handle(command, arguments, workingDirectory, "ERROR", e.Data);
process.OutputDataReceived += (_, e) => handle(command, arguments, workingDirectory, "", e.Data);
process.StartInfo.Environment.Add("ASPNETCORE_ENVIRONMENT", "Development");
return process;
}

Diagnostic Process ends with exit code 1 but same action on command prompt works

My code uses a C# Diagnostic Process to run a GDAL process.
This process ends with an exit code 1.
But running from a command prompt works.
Where's my mistake?
Already tested (see code below):
files to transform exists,
directory to write to exists and "My programme" has access rights,
the GDAL library exists.
This is my code:
private string AddSrs(string tempFile, string User)
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
string geoserverDatadir = ConfigurationManager.AppSettings["geoserverDatadir"];
string dirOut = Path.Combine(geoserverDatadir, User, Guid.NewGuid().ToString());
string fileOut = Path.Combine(dirOut, (Path.GetFileNameWithoutExtension(tempFile) + ".geoTiff"));
Directory.CreateDirectory(dirOut);
if (File.Exists(fileOut))
{
File.Delete(fileOut);
}
string binDirectory = ConfigurationManager.AppSettings["binDirectory"];
startInfo.FileName = Path.Combine(binDirectory, "gdal_translate.exe");
string args = String.Format(#"-a_srs EPSG:28992 {0}{1}{0} {0}{2}{0}", "\"", tempFile, fileOut);
startInfo.Arguments = args;
process.StartInfo = startInfo;
if(!File.Exists(startInfo.FileName))
{
_log.Error("file not exists: " + startInfo.FileName));
}
if (!File.Exists(tempFile))
{
_log.Error("file not exists: " + tempFile));
}
if (!Directory.Exists(dirOut))
{
_log.Error("directory not exists: " + dirOut));
}
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have access to view the permissions.
System.Security.AccessControl.DirectorySecurity ds = Directory.GetAccessControl(dirOut);
}
catch (UnauthorizedAccessException ex)
{
_log.Error("no access rights for directory: " + dirOut));
}
try
{
process.Start();
int processTimeOut = 1000;
if (!process.WaitForExit(processTimeOut))
{
process.Kill();
_log.Error("Process killed by timeOut: " + processTimeOut));
return string.Empty;
}
else
{
var exitCode = process.ExitCode;
_log.Error("Process ended. Exitcode: " + exitCode));
return fileOut;
}
}
catch (Exception ex)
{
_log.Error(ex.Message));
_log.Error(ex.StackTrace));
return string.Empty;
}
}
EDIT:
This is the full command as logged in my logfile:
D:\OSGeo4W64\bin\gdal_translate.exe -a_srs EPSG:28992 "D:\data\Temp\Merkator\Hengelo Veldwijk Zuid Revisie.temp" "D:\data\Geoserver\data\Merkator\aa84dc6d-aff2-4254-975a-3ede8eea5c6d\Hengelo Veldwijk Zuid Revisie.geoTiff"
And this is the same command pasted in commandline (works with and without admin rights):
C:\Users\Administrator>D:\OSGeo4W64\bin\gdal_translate.exe -a_srs EPSG:28992 "D:\data\Temp\Merkator\Hengelo Veldwijk Zuid Revisie.temp" "D:\data\Geoserver\data\Merkator\aa84dc6d-aff2-4254-975a-3ede8eea5c6d\Hengelo Veldwijk Zuid Revisie.geoTiff"
Input file size is 1273, 378
0...10...20...30...40...50...60...70...80...90...100 - done.
INFO:
The problem exists on a (virtual) server. On my computer (debug-mode) this code works fine.

Run Active Directory shell commands using C#

I am writing a module which will be executing any kind of shell commands related to Active Directory and other shell commands on a particular domain controller.
Some of command are working but some of commands are not working properly.
Here is the code
public static void ExecuteShellCommand(string _FileToExecute, string _CommandLine, ref string _outputMessage, ref string _errorMessage)
{
System.Diagnostics.Process _Process = null;
try
{
_Process = new System.Diagnostics.Process();
string _CMDProcess = string.Format(System.Globalization.CultureInfo.InvariantCulture, #"{0}\cmd.exe", new object[] { Environment.SystemDirectory });
string _Arguments = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", new object[] { _FileToExecute });
_Arguments = string.Format(" /C \"{0}\"", _Arguments);
Console.WriteLine("---aruguments quering : cmd.exe" + _Arguments);
System.Diagnostics.ProcessStartInfo _ProcessStartInfo = new System.Diagnostics.ProcessStartInfo(_CMDProcess, _Arguments);
_ProcessStartInfo.CreateNoWindow = true;
_ProcessStartInfo.UseShellExecute = false;
_ProcessStartInfo.RedirectStandardOutput = true;
_ProcessStartInfo.RedirectStandardInput = true;
_ProcessStartInfo.RedirectStandardError = true;
_Process.StartInfo = _ProcessStartInfo;
//_ProcessStartInfo.Domain = System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain().Name;
_Process.Start();
_errorMessage = _Process.StandardError.ReadToEnd();
_Process.WaitForExit();
_outputMessage = _Process.StandardOutput.ReadToEnd();
_Process.WaitForExit();
}
catch (Exception _Exception)
{
Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
}
finally
{
_Process.Close();
_Process.Dispose();
_Process = null;
}
}
CommandExecutionEngine.ExecuteShellCommand("nltest", "/logon_query /server:india.cobra.net", ref output, ref error);
Console.WriteLine("output for dir : " + output + " error : " + error);
Commands:
repadmin /showrepl
dcdiag
dcdiag /s:<dcname
command nltest executing but not returning any result. Where the other mentioned commands giving error is not recognized as internal or external command. Where if I execute command directly from console its working fine.
Here I am invoking a process under the context of domain administrator account so that I will not be have any permission issues.
Please suggest.
Possibly since UseShellExecute = false, the application location is not being found. Use the full path.

Related to ProcessStartInfo() method in C#

I want to give command name and file path in ProcessStartInfo() method in C#.
So I have a command name("F:\AndroidProjects\AndProj3>) and file path("F:\Android\apache-ant-1.8.2-bin\apache-ant-1.8.2\bin\ant debug") just like that but it is not working and process can not be started.
Please give me a solution for starting the process because command name first execute and after that file path will be execute.
How I can pass the both argument in ProcessStartInfo() method?
public static string BuildAndroidProject()
{
string result="";
// string ProjNameNDLocation = ProjectLocation + "\\" + ProjectName + ">";
try
{
System.Diagnostics.ProcessStartInfo androidBuildProj = new System.Diagnostics.ProcessStartInfo("F:\\AndroidProjects\\AndProj3 F:\\Android\\apache-ant-1.8.2-bin\\apache-ant-1.8.2\\bin\\ant debug");//ProjNameNDLocation, Program.ANDROIDDEBUGGCMD);
androidBuildProj.RedirectStandardOutput = true;
androidBuildProj.UseShellExecute = false;
androidBuildProj.CreateNoWindow = true;
System.Diagnostics.Process androidProcess = new System.Diagnostics.Process();
androidProcess.StartInfo = androidBuildProj;
androidProcess.Start();
result = androidProcess.StandardOutput.ReadToEnd();
androidProcess.Close();
}
catch (Exception e)
{
}
return result;
}
Problem is in the ProcessInfoStart Function. How can I run this command?
Based on the question, the closest I can see is:
using (var proc = Process.Start(new ProcessStartInfo
{
WorkingDirectory = #"F:\AndroidProjects\AndProj3",
FileName = #"F:\Android\apache-ant-1.8.2-bin\apache-ant-1.8.2\bin\ant",
Arguments = "debug"
}))
{
// maybe wait and check exit-code
// proc.WaitForExit();
// int i = proc.ExitCode;
}

Categories

Resources