Process.Start takes long time to start outside application - c#

I have a method that launches a second exe. The issue I'm having is that if I'm in debug mode in Visual Studio and I put a breakpoint directly after the Process.Start call my second application launches immediately but if I have no break points in VS or run my main C# application outside of VS the launching of my second application via Process.Start can take up to two minutes. My method is below and where I put my breakpoint to see an immediate launch of the 2nd app is at line "if(null != _ProcessMine)". I put the launch of the second exe in a worker thread because when I close my main exe I want the second exe to close also.
public static void RunBtnProcessThread(string processName, String sArgs, Button btn)
{
// disable the button until we release the newly launched process
btn.Enabled = false;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (doWorkSender, doWorkArgs) =>
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = processName;
startInfo.Arguments = sArgs;
try
{
using ( _ProcessMine = Process.Start(startInfo))
{
if(null != _ProcessMine)
_ProcessMine.WaitForExit();
}
}
catch (Exception ex)
{
string _Funk = ReflectionHelper.GetMethodFullName(MethodBase.GetCurrentMethod());
// error
Debug.Assert(false, "Error: " + ex.Message);
// Log error.
TraceUtil.LogException(_Funk, ex);
}
System.Threading.Thread.Sleep(500);
};
worker.RunWorkerCompleted += (completedSender, completedArgs) =>
{
btn.Enabled = true;
_ProcessMine)= null;
};
worker.RunWorkerAsync();
}

You don't actually need a separate thread for your scenario. You can accomplish the same thing by subscribing to the Process.Exited() event:
public static void RunBtnProcessThread(string processName, String sArgs, Button btn)
{
// disable the button until we release the newly launched process
btn.Enabled = false;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = processName;
startInfo.Arguments = sArgs;
try
{
_ProcessMine = Process.Start(startInfo);
_ProcessMine.EnableRaisingEvents = true;
_ProcessMine.Exited += (sender, e) =>
{
btn.Invoke((MethodInvoker)delegate {
btn.Enabled = true;
});
_ProcessMine = null;
};
}
catch (Exception ex)
{
string _Funk = ReflectionHelper.GetMethodFullName(MethodBase.GetCurrentMethod());
// error
Debug.Assert(false, "Error: " + ex.Message);
// Log error.
TraceUtil.LogException(_Funk, ex);
}
}
You could close it using something like:
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_ProcessMine != null && !_ProcessMine.HasExited)
{
// Depending on the type of app:
_ProcessMine.CloseMainWindow();
// ... or ...
_ProcessMine.Kill();
}
}

Related

C# Prevent a started app from writing in the console

I have an app which starts another app. This other app prints a few lines into the Console but noone needs this output and it prints it's output betwenn my own. How can I prevent this other app from printing it's stuff into my console?
I tried to run with ProcessStartInfo.UseShellExecutive both on true and false, also tried to change the console output into a MemoryStream before starting but since I need the Console i had to change the output back and it looks like the other app got their input changed back too.
Process serverprocess = new Process();
serverprocess.StartInfo.FileName = Path.GetFileName(serverpath);
serverprocess.StartInfo.Arguments = launch;
serverprocess.StartInfo.UseShellExecute = false;
serverprocess.StartInfo.RedirectStandardOutput = true;
serverprocess.Start();
In your code ensure that you are re-directing both StandardOutput and StandardError that way everything that the "ThirdPartyApp" writes will be captured in either of these streams.
I have written a small Helper Class that helps with this
You can use like
//Launching excel.exe with /safe as arg
var excelExample1 = #"""C:\Program Files (x86)\Microsoft Office\Office15\EXCEL.EXE"" /safe";
LaunchCMD.Invoke(excelExample1);
//To get its output, if any
var getOutput = LaunchCMD.Output;
LaunchCMD Helper Class
class LaunchCMD
{
public static string Output
{
get; set;
} = "";
public static void Invoke(string command, bool waitTillExit = false, bool closeOutputWindow = false)
{
ProcessStartInfo ProcessInfo;
Process Process = new Process();
ProcessInfo = new ProcessStartInfo("cmd.exe", "/C " + command);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = false;
ProcessInfo.RedirectStandardOutput = true;
ProcessInfo.RedirectStandardError = true;
Process.EnableRaisingEvents = true;
Process = Process.Start(ProcessInfo);
Process.ErrorDataReceived += ConsoleDataReceived;
Process.OutputDataReceived += ConsoleDataReceived;
Process.BeginOutputReadLine();
Process.BeginErrorReadLine();
if (waitTillExit == true)
{
Process.WaitForExit();
}
if (closeOutputWindow == true)
{
Process.CloseMainWindow();
}
Process.Close();
System.Threading.Thread.Sleep(1000);
Output.ToString();
}
private static void ConsoleDataReceived(object sender, DataReceivedEventArgs e)
{
System.Threading.Thread.Sleep(1000);
if (e.Data != null)
{
Output = Output + e.Data;
}
}
}

How to determine when FFmpeg process completes

var origFilePath = "C:/MyOriginalFile.webm";
var processedFilePath = "C:/MyProcessedFile.webm";
RunFfmpeg($"-i \"{origFilePath}\" -af \"silenceremove=1:0.1:0.001, areverse, silenceremove=1:0.1:0.001, areverse\" \"{processedFilePath}\" -y");
// fails with IOException as the file presumably not been released by FFmpeg
System.IO.File.Delete(origFilePath);
When the file is deleted, the following exception is frequently (maybe 80% of the time) thrown:
IOException: The process cannot access the file 'C:\MyOriginalFile.webm' because it is being used by another process.
The call to create and run the FFmpeg process goes like this:
private List<string> RunFfmpeg(string arguments)
{
using (var process = new Process())
{
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.FileName = _hostingEnvironment.ContentRootPath + _settings.FfmpegPath;
process.StartInfo.Arguments = arguments;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
// ffmpeg only uses strerr for its output
var output = new List<string>();
process.ErrorDataReceived += new DataReceivedEventHandler((s, e) => {
if (e.Data != null)
output.Add(e.Data);
});
process.Start();
process.BeginErrorReadLine();
process.WaitForExit();
return output;
}
}
It appears that even though the process has completed when the file is deleted, FFmpeg is still processing or has otherwise not released it.
Is this behaviour expected? How do I go about ensuring that FFmpeg has finished processing the files before continuing?
If it were me I'd run this on a background thread and tap into the process.Exited event and try to delete the File in there.
The MSDN Documentation for the Process Exit Event uses this strategy and polls for 30 seconds till the Exited event fires and checks the ExitCode. Depending on the ExitCode, you could give it more time, check if the file is still locked or perform another action until process.ExitCode == 0
private Process myProcess = new Process();
private int elapsedTime;
private bool eventHandled;
public void RunFfmpeg(string arguments)
{
elapsedTime = 0;
eventHandled = false;
try
{
myProcess.StartInfo.FileName = _hostingEnvironment.ContentRootPath + _settings.FfmpegPath;
myProcess.StartInfo.Arguments = arguments;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.EnableRaisingEvents = true;
myProcess.Exited += new EventHandler(myProcess_Exited);
myProcess.Start();
}
catch (Exception ex)
{
Console.WriteLine("An error occurred trying to print \"{0}\":" + "\n" + ex.Message, fileName);
return;
}
// Wait for Exited event, but not more than 30 seconds.
const int SLEEP_AMOUNT = 100;
while (!eventHandled)
{
elapsedTime += SLEEP_AMOUNT;
if (elapsedTime > 30000)
{
break;
}
Thread.Sleep(SLEEP_AMOUNT);
}
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
eventHandled = true;
Console.WriteLine("Exit time: {0}\r\n" +
"Exit code: {1}\r\nElapsed time: {2}", myProcess.ExitTime, myProcess.ExitCode, elapsedTime);
}

How to identify that Process is complete now, end close this event OutputDataReceived in C#

I'm using Process of C# for open cmd and fire command. After completion of this process I want to use List which one is filled by OutputDataReceived for further use.
But, i couldn't be able to identify that Process is completes now and you can use List.
I'm getting this error. collection-was-modified-enumeration-operation-may-not-execute
Code
String exeFileName = "cmd";
List<string> list = new List<string>();
ProcessStartInfo info = new ProcessStartInfo(exeFileName);
info.Arguments = #"/c myCommand";
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
Process proc = new Process();
proc.StartInfo = info;
// Set our event handler to asynchronously read the sort output.
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
try
{
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
// i don't want to pass some time interval here like WaitForExit(1000)
// cause command takes dynamic time
proc.WaitForExit();
if (!proc.HasExited)
{
proc.Kill();
}
// this throws error like "collection-was-modified-enumeration-operation-may-not-execute"
foreach(i in list)
{
//logic
}
}
catch (Exception ex)
{
throw;
}
}
static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
// logic
list.Add(e.data);
}
}
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
// logic
list.Add(e.data);
}

Execute another EXE from an application with parameters, as admin

I have created two projects under the same solution. ProjectA is a Windows Form Application and ProjectB is a simple console application.ProjectB will be executed from ProjectA with admin privileges.
Sample from ProjectA
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = txtIP.Text;
bindingPort = txtPort.Text;
if (!fileChosen)
{
CreateCertificate();
//
}
//After this step i want to execute ProjectB with admin provileges with 3 parameters
ExecuteB_AsAdminWithPrivileges(ipAddress, bindingPort, serverCert);
}
}
So when i click the button name Finish i want the ProjectB.exe to be executed with parameters that i will give from ProjectA.
And ProjectB will look sth like:
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort)
{
//
}
This is the method which will be using the parameters from ProjectA.
How can i get the Parameters from ProjectA to this method in ProjectB?
You could use this method:
public static int RunProcessAsAdmin(string exeName, string parameters)
{
try {
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = CurrentDirectory;
startInfo.FileName = Path.Combine(CurrentDirectory, exeName);
startInfo.Verb = "runas";
//MLHIDE
startInfo.Arguments = parameters;
startInfo.ErrorDialog = true;
Process process = System.Diagnostics.Process.Start(startInfo);
process.WaitForExit();
return process.ExitCode;
} catch (Win32Exception ex) {
WriteLog(ex);
switch (ex.NativeErrorCode) {
case 1223:
return ex.NativeErrorCode;
default:
return ErrorReturnInteger;
}
} catch (Exception ex) {
WriteLog(ex);
return ErrorReturnInteger;
}
}
The first parameter will be your .exe file and the second one will be the parameters you want to give to your .exe file
After this you should make changes in your .exe file in the main section.
Something like:
static void Main(string[] args)
{
if (args.Length <= 1) return;
try
{
if (args.Length == 2)
{
_IpAddress = args[0];
_IpPort = args[1];
FunctionName(_IpAddress, _IpPort);
}
else
{
_return
}
}
catch (Exception)
{
throw new Exception("Invalid number of parameters!");
}
}
I hope this helps.
Update
ProgramA{
string ip ="123.123.123";
File.WriteAllText("c://MtDataFromA.txt","ip="+ip);
}
private void btnFinish_Click(object sender, EventArgs e)
{
ipAddress = File.WriteAllText("c://MtDataFromA.txt");//some algorithem to find the ip from text file
}
public static void StoreAndBindCertificate(string pfxFileServerCert, string ipAddress, string ipPort){
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "YourFile.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "ipAddress"+" " +"ipPort";
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
}
link

RedirectStandard Output/Error wont call my event for Standard Output/Error

hi i'm trying to build a parser for my System to Manage my Tekkit Server i am using C# but i have RedirectStandardOutput on my Tekkit Server process and there is a method set-up to then send that output to my console after adding to a List but it's not adding to a List<string>
Here is my code:
public void StartServer(string maxMem, string minMem, string path)
{
ThreadStart server = new ThreadStart(delegate() { StartServerThread(maxMem, minMem, path); });
server.Invoke();
}
private void StartServerThread(string maxMem, string minMem, string TekkitPath)
{
try
{
TekkitServer.StartInfo.FileName = "java";
TekkitServer.StartInfo.Arguments = String.Format("-Xmx{0} -Xms{1} -jar \"" + TekkitPath + "\" -nojline nogui", maxMem, minMem);
TekkitServer.StartInfo.UseShellExecute = false;
TekkitServer.StartInfo.RedirectStandardInput = true;
TekkitServer.StartInfo.RedirectStandardOutput = true;
TekkitServer.OutputDataReceived += new DataReceivedEventHandler(TekkitServer_OutputDataReceived);
IsStarted = TekkitServer.Start();
TekkitServerInput = TekkitServer.StandardInput;
}
catch (Exception)
{
}
}
void TekkitServer_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
/*B*/recordedData.Add(e.Data);
Console.Out.WriteLine(e.Data);
}
Where /*B*/ is a break point, the breakpoint is never activating
By default, the standard output is directed at the console window.
If you need to do something with it, you need to redirect it, hence, you need to set RedirectStandardOutput = true; for the event to be fired.
Edit: This is my working code (with error handling and logging omitted):
public int ExecuteCommand(CommandParameters parameters)
{
Process process = new Process();
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += StdOutputHandler;
process.ErrorDataReceived += StdErrorHandler;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = ...;
process.StartInfo.Arguments = ...;
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit(parameters.Timeout);
return process.ExitCode;
}
private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outdata)
{
if (!string.IsNullOrEmpty(outdata.Data))
{
OutputMessages.Add(outdata.Data);
}
}
Most likely the missing link in your code is the BeginOutputReadLine that actually gets the handler method on it's way.
Also, I use a fresh Process object and that I wait on it to finish it's job, so no interference with previous calls is possible.

Categories