I have found a lock file issue with System.Diagnostics.Process when it is running several threads from the console application.
For example, I run two command-line applications (App1, App2) using the code below and pass two different files for each app (App1 -> File1, App2 -> File2)
Sometimes, the App1 -> File1 finish processing the file, but for an unknown reason, File1 is locked by the App2 app.
It is not possible to read the File1 file until App2 exits, through File1 was executed with App1 app in the first System.Diagnostics.Process instance.
This problem only happens when Process.StartInfo.UseShellExecute is set to False. I need to disable shell execute to be able to read Process.StandardError property if execution fails.
Any idea what is going on? Is the code below thread-safe?
using (var p = new Process
{
StartInfo =
{
FileName = "c:\commandLine.exe",
Arguments = "c:\file.jpg",
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true
}
})
{
try
{
p.Start();
p.PriorityClass = ProcessPriorityClass.Normal;
p.WaitForExit();
var output = await p.StandardOutput.ReadToEndAsync();
var error = await p.StandardError.ReadToEndAsync();
var exitCode = p.ExitCode;
return .....;
}
finally
{
try
{
if (!p.HasExited)
p.Kill();
}
catch (InvalidOperationException)
{
//The process is not found, probably executable was not fount
}
p.Close();
}
}
Related
I am trying to run an exe file from my own machine:
string versionInFolder = #"c:\test.exe";
public void Install(string versionInFolder)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
FileName = versionInFolder,
WindowStyle = ProcessWindowStyle.Hidden,
};
using (Process process = Process.Start(processStartInfo))
{
process.WaitForExit();
}
}
This file exist and can run manually but i got this error:
System.ComponentModel.Win32Exception: 'The requested operation
requires elevation'
I found this post but did not understand the reason for this error and how to solve it.
You need to run your programm as admininistrator.
Check that first.
And if that doesn't work, or if you're the administrator, try to move your file in another place.
As discussed in other post, I came to know that Verb = "runas" works as elevated.
I need to run "logman.exe" arguments with Elevated privileged. With below code, I am not getting any output,
try
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "logman.exe",
Arguments = "PerfCounterCustom",
Verb = "runas",
RedirectStandardOutput = true,
CreateNoWindow = true,
}
};
process.Start();
string lineData;
while ((lineData = process.StandardOutput.ReadLine()) != null)
{
if (lineData.Contains("Root Path:"))
{
Console.WriteLine(lineData.Trim());
}
}
process.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Note - When I am running above EXE, right click as Admin, I m getting the output.
What changes required so that I can make Elevated through code in C# and output?
Process.Start() can use the OS or Shell (Explorer.exe) to spawn a new process, but only the Shell will prompt for elevation.
Thus you have to specify UseShellExecute=true in ProcessStartInfo, according to this answer: processstartinfo-verb-runas-not-working
UseShellExecute=false will allow you to capture Standard Output and Standard Error messages.
I'm trying to build UWP app and I'm struggling with an issue:
I have noticed there is now way to launch external executable, but I can open file with its default application using LaunchFile (Like mentioned here https://learn.microsoft.com/en-us/uwp/api/Windows.System.Launcher#Windows_System_Launcher_LaunchFileAsync_Windows_Storage_IStorageFile_).
Should it work for a .bat file too? Where should i place the .bat file if it is.? Because i tried to give a path to a .bat file i wrote and I got an exception.
Thanks!!
Its not going to work. If you can get some 3rd party application to open and handle bat files by default then maybe, but otherwise nothing the OS handles like cmd or vbscript will run. Not sure what your goal is, but a 3rd party scripting app like Autohotkey does work.
From .NET 4 there is and Process and ProcessStartInfo
classes under the System.Diagnostics namespace which allow you
to Win32 applications from C#.
public static Task<bool> ExecuteBatchFile(string BatchFilePath, string BatchFileDirectory)
{
try
{
Task<bool> executeBatchFileTask = Task.Run<bool>(() =>
{
bool hasProcessExited = false;
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = BatchFilePath,
CreateNoWindow = false,
UseShellExecute = true,
WindowStyle = ProcessWindowStyle.Normal,
WorkingDirectory = BatchFileDirectory
};
// Start the process with the info we specified.
// Call WaitForExit and then the using-statement will close.
using (System.Diagnostics.Process exeProcess = System.Diagnostics.Process.Start(startInfo))
{
while (!exeProcess.HasExited)
{
//Do nothing
}
hasProcessExited = true;
}
return hasProcessExited;
});
return executeBatchFileTask;
}
catch (Exception ex)
{
throw;
}
}
And call this method in your code by:
bool hasBatchFileExecuted = await ExecuteBatchFile(BatchFilePath, BatchFilePathDirectory);
Create your own batch file like:
1) Create a text file in notepad and save it as run.bat under save as category and all files.
2) Write the following code to execute a C# program/However you can write your own commands also:
csc someRandomCode.cs
pause
3) Save and quit.
I am trying to convert a video when the user submits a form. It seems to convert ok but the file "is being used by another proccess" when I try to do anything with it. It looks like ffmpeg.exe never exits. My code is below is there anything I should be doing different to allow the process to release the file? If I run this manually it exits fine.
internal class ConversionUtility : Utility
{
public void Convert(string videoFileName)
{
var video = new VideoFile(videoFileName);
if (!video.infoGathered)
GetVideoInfo(video);
var Params = string.Format("-y -i \"{0}\" -coder ac -me_method full -me_range 16 -subq 5 -sc_threshold 40 -vcodec libx264 -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -i_qfactor 0.71 -keyint_min 25 -b_strategy 1 -g 250 -r 20 \"{1}\"", video.Path, Path.ChangeExtension(videoFileName,".mp4"));
//var Params = string.Format("-y -i \"{0}\" -acodec libfaac -ar 44100 -ab 96k -coder ac -me_method full -me_range 16 -subq 5 -sc_threshold 40 -vcodec libx264 -s 1280x544 -b 1600k -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -i_qfactor 0.71 -keyint_min 25 -b_strategy 1 -g 250 -r 20 c:\\output3.mp4", video.Path, videoFileName);
//var Params = String.Format(" {0} \"{1}\"",this.FFmpegLocation, video.Path);
var threadStart = new ParameterizedThreadStart(del => RunProcess(Params));
var thread = new Thread(threadStart);
thread.Start();
//RunProcess(Params);
}
}
internal class Utility
{
public string FFmpegLocation { get; set; }
private string WorkingPath { get { return Path.GetDirectoryName(FFmpegLocation); } }
protected string RunProcess(string Parameters)
{
//create a process info
var oInfo = new ProcessStartInfo(this.FFmpegLocation, Parameters)
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
//Create the output and streamreader to get the output
string output = null; StreamReader srOutput = null;
//try the process
try
{
//run the process
Process proc = System.Diagnostics.Process.Start(oInfo);
proc.WaitForExit();
//if (!proc.WaitForExit(10000))
// proc.Kill();
//get the output
srOutput = proc.StandardError;
//now put it in a string
output = srOutput.ReadToEnd();
proc.Close();
}
catch (Exception)
{
output = string.Empty;
}
finally
{
//now, if we succeded, close out the streamreader
if (srOutput != null)
{
srOutput.Close();
srOutput.Dispose();
}
}
return output;
}
After a good bit of research I came across this post. Which set me on the right track. I read more about RedirectStandardError and found I needed to change the order I was calling things. After calling WaitForExit at the end everything worked as expected. Here is the updated code:
protected string RunProcess(string Parameters)
{
//create a process info
var oInfo = new ProcessStartInfo(this.FFmpegLocation, Parameters)
{
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
};
var output = string.Empty;
try
{
Process process = System.Diagnostics.Process.Start(oInfo);
output = process.StandardError.ReadToEnd();
process.WaitForExit();
process.Close();
}
catch (Exception)
{
output = string.Empty;
}
return output;
}
You should always read the StandardOutput and the StandardError buffers while the process is running. Whenever the buffers reach their limits, the process will literally hang until you read the buffers to end.
FFMpeg constantly reports information about the conversion. Every update of the information you see in the bottom of the Command Prompt is added to the StandardOutput buffer, thus filling up the buffer. The buffer is likely to fill up quickly when converting large files.
Does your web application(app pool process identity) have the proper permissions for what's being done in the process threads you are spawning?
I have had issues like this in the past related to System.File.Move running Process Threads doing IO from ASP.NET, which is really a Win32 issue under the hood.
Use System.File.Copy or the Win32 equivalent and then delete the old file if you can. My guess is the .exe you are calling out(executing) to isn't yours(your own source code to be able to modify) and you can't change it.
You can also use SysInternals to see what may be holding on to or blocking resources.
When working with a command line program, via a c# class method.
How do you determine if the commandline program was successfully executed and the operation it has performed is ok or has failed?
Also how do you get the screen commandline output into the c# class method?
You can use the Process class to execute a command line command.
The following code captures the standard output to output, and assigns the processes exit code to exitCode.
using (Process p = new Process())
{
p.StartInfo.FileName = exeName;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
int exitCode = p.ExitCode;
}
Something like:
Process mycommand = new Process();
mycommand.StartInfo.FileName = "myexe.exe";
mycommand.StartInfo.Arguments = "param1";
mycommand.StartInfo.UseShellExecute = false;
mycommand.StartInfo.RedirectStandardOutput = true;
mycommand.Start();
Console.WriteLine(mycommand.StandardOutput.ReadToEnd());
mycommand.WaitForExit();
You usually determine an exe's state wether the exit code is 0, but that is arguably down to the writer of the exe
I assume you're using the Process class to call the command line app.
You can find the exit code of the process using Process.ExitCode. You can redirect its standard output by setting ProcessStartInfo.RedirectStandardOutput before starting it, and then either using Process.StandardOutput or the Process.OutputDataReceived event.
Take a look at this questionenter link description here.
The additional information you might need is process.ExitCode to see if it was sucessful. Of course, the Main method of the console app must return an exit code when it is unsuccessful, which many do not.
For this, you use the Process.Start method. You can control how the process runs with the passed in ProcessStartInfo:
var myProcess = Process.Start(new ProcessStartInfo {
FileName = "process.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
});
if (!myProcess.WaitForExit(5000)) { // give it 5 seconds to exit
myProcess.Kill();
}
if (myProcess.ExitCode != 0) {
// error!
}
var output = myProcess.StandardOutput.ReadToEnd(); // access output