command line output validation in c# - c#

My output in the actual command prompt looks like this:
Name: My Software
Version: 1.0.1
Installed location: c:\my folder
I am trying to get this output via c# code
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + "my command to execute");
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
string[] lines = result.Split(new string[] { System.Environment.NewLine, }, System.StringSplitOptions.None);
foreach (string tmp in lines)
{
if (tmp.Contains("Version"))
{
isAvailable= true;
}
}
I don't want to just check a version tag, I am trying to get the version value and do a compare, for example if the value is 1.0.1, i would want that value and do a comparison with 2.0.0.
I can use indexof(like result.IndexOf("Version:");) - But that doesn't get me the value of the version
Any thoughts will be helpful.

You should use the .NET Version class and it's CompareTo(Object) method to do your comparison.
var input = new Regex(#"(?<=Version:)\s*(.*)").Matches(#"Name: My Software
Version: 1.0.1
Installed location: c:\my folder")[0].Value.Trim();
var a = new Version(input);
var b = new Version("2.0.0");
int comparison = a.CompareTo(b);
if(comparison > 0)
{
Console.WriteLine(a + " is a greater version.");
}
else if(comparison == 0)
{
Console.WriteLine(a + " and " + b +" are the same version.");
}
else
{
Console.WriteLine(b + " is a greater version.");
}

Try like below... it will help you...
Instead of Contains check the word by using IndexOf...
if (tmp.IndexOf("Version") != -1)
{
isAvailable = true;
string[] info = tmp.Split(':');
string version = info[1].Trim();
Console.WriteLine(version);
}

string versionText;
var stuff = tmp.Split(":");
if(stuff[0].Trim() == "Version")
{
isAvailable = true;
versionText = stuff[1].Trim();
}
if(versionText == expectedVersionText) // Do something specfic.

You might want to use Regular Expressions:
^Version:\s*(.*)$
should match the version number inside the parentheses.

string sought = "Version:";
foreach (string tmp in lines)
{
if (tmp.Contains(sought))
{
int position = tmp.IndexOf(sought) + sought.Length;
string version = tmp.Substring(tmp.IndexOf(sought) + sought.Length);
string[] versionParts = version.Split('.');
VersionCompare(versionParts, new string[]{"2", "0", "0"});
}
}
/// <summary>Returns 0 if the two versions are equal, else a negative number if the first is smaller, or a positive value if the first is larder and the second is smaller.</summary>
private int VersionCompare(string[] left, string[] right)
{
for(int i = 0; i < Math.Min(left.Length, right.Length); ++i)
{
int leftValue = int.Parse(left[i]), rightValue = int.Parse(right[i]);
if(leftValue != rightValue) return leftValue - rightValue;
}
return left.Length - right.Length;
}

Related

find numbers and save to string

I am trying to make a ui that gets the session id of a computer on the network. I want to take this session id and save it to a string to be used later.
private void button1_Click(object sender, EventArgs e)
{
string ComputerName = ComputerNameBox.Text;
string Username = UserNameBox.Text;
Process Process = new Process();
Process.StartInfo.FileName = "CMD.exe";
Process.StartInfo.Arguments = "/K" + "qwinsta /server:" + ComputerName + " " + Username;
Process.StartInfo.CreateNoWindow = true;
Process.StartInfo.UseShellExecute = false;
Process.StartInfo.RedirectStandardOutput = true;
Process.Start();
Process.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
Process.BeginOutputReadLine();
while (!Process.HasExited)
{
Application.DoEvents();
}
}
private void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (MessagePop.InvokeRequired)
{
MessagePop.BeginInvoke(new DataReceivedEventHandler(SortOutputHandler), new[] { sendingProcess, outLine });
}
else
{
MessagePop.AppendText(Environment.NewLine + outLine.Data);
}
}
This code runs and inputs the computername and username of the computer/person you want the session ID for. The username and ID will likely be different every time so I can't hard code it.
SESSIONNAME USERNAME ID STATE TYPE DEVICE
console mlynch 9 Active
This is the output I get in the textbox, I want to get the 9 and save it to a string, however, the number can be something like 10 or higher so I will need to be able to get both numbers. How do I do this?
Please try this:
var textBoxData =
"SESSIONNAME USERNAME ID STATE TYPE DEVICE\r\nconsole mlynch 9 Active";
var stringCollection = textBoxData.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries);
var finalCollection = new List<int>();
foreach (var s1 in stringCollection)
{
int n;
if (int.TryParse(s1, out n))
{
finalCollection.Add(n);
}
}
(or) create a function which takes in text box data and return int colleciton:
public List<int> GetData(string textBoxData)
{
var stringCollection = textBoxData.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries);
var finalCollection = new List<int>();
foreach (var s1 in stringCollection)
{
int n;
if (int.TryParse(s1, out n))
{
finalCollection.Add(n);
}
}
}
and then use it as
var intCollection = GetData(textBox1.Text);
I get in trouble for linking to my open source project UltimateHelper, but just pretend Word is a string.
public static List<Word> GetWords(string sourceText, char[] delimiters = null)
{
// initial value
List<Word> words = new List<Word>();
// typical delimiter characters
char[] delimiterChars = { ' ', '-', '/', ',', '.', ':', '\t' };
// if the delimiters exists
if (delimiters != null)
{
// use the delimiters passedin
delimiterChars = delimiters;
}
// verify the sourceText exists
if (!String.IsNullOrEmpty(sourceText))
{
// Get the list of strings
string[] strings = sourceText.Split(delimiterChars);
// now iterate the strings
foreach (string stringWord in strings)
{
// verify the word is not an empty string or a space
if (!String.IsNullOrEmpty(stringWord))
{
// Create a new Word
Word word = new Word(stringWord);
// now add this word to words collection
words.Add(word);
}
}
}
// return value
return words;
}
I have this in a class called WordParser, so to call it:
Edit: If you need to parse each line, there is also a method:
List<TextLine> textLines = WordParser.GetTextLines(text);
In your case, you want to parse on just space, so set Delimiters to this:
char[] delimiterChars = { ' ' };
List<Word> words = WordParser.GetWords(text, delimiterChars);
// Here is the full example using DataJuggler.Core.UltimateHelper Nuget package
if ((ListHelper.HasOneOrMoreItems(words)) && (words.Count >= 3))
{
Word idWord = Words[2];
int idValue = NumericHelper.ParseInteger(idWord.Text, 0, -1);
}
idValue will be the Id if it parses, 0 if it can't be parsed, and -1 if an error occurs trying to parse it.
I hope I didn't offend anyone by not posting all the classes used, but if it does I will post the full class for each, but kind of overkill to demonstrate a 3 line of code solution.
Hope it helps.
The full code is here:
https://github.com/DataJuggler/UltimateHelper/tree/master/UltimateHelper
And Word Parser:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/WordParser.cs
Word class:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/Objects/Word.cs
List Helper:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/ListHelper.cs
And Numeric Helper:
https://github.com/DataJuggler/UltimateHelper/blob/master/UltimateHelper/NumericHelper.cs
I figured out how to do what I needed.
Process GetSessionID = new Process();
GetSessionID.StartInfo.FileName = "CMD.exe";
GetSessionID.StartInfo.Arguments = "/C" + "for /f \"skip=1 tokens=3\" %1 in ('query user " + Username + "/server:" + ComputerName + "') do #echo %1";
GetSessionID.StartInfo.RedirectStandardOutput = true;
GetSessionID.StartInfo.UseShellExecute = false;
GetSessionID.StartInfo.CreateNoWindow = true;
GetSessionID.Start();
SessionIDOutput = GetSessionID.StandardOutput.ReadToEnd();
GetSessionID.WaitForExit();
DoAllTheThingsTextBox.Text = SessionIDOutput;
if (GetSessionID.HasExited == true)
{
var digitArray = DoAllTheThingsTextBox.Text.Where(Char.IsDigit).ToArray();
SessionID = new String(digitArray);
if (MouseControlCheck.Checked == true)
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName + " /control");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
else
{
Process Process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("CMD.exe", "/C" + "mstsc /shadow:" + SessionID + " /v " + ComputerName);
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
Process = Process.Start(startInfo);
}
}

How to create simple volume from unallocated partition

I'm trying to open a place on my harddisk to store some licensing files.
So far I have tried diskpart. It looks easy to use but I could not format the unallocated partition with diskpart. I have found a way to create the unallocated space but I have to format it to use(correct me if I am wrong here. I'm really new on disk partition stuff)
This is my method to select the right volume. I've take it from here and it's working good. Link : C# and diskpart: how to select by disk label and not by a number? and the code I am using is this :
public int GetIndexOfDrive(string drive)
{
drive = drive.Replace(":", "").Replace(#"\", "");
// execute DiskPart programatically
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
if (label.Equals(drive))
{
return index;
}
}
}
return -1;
}
once I get the index I run my own code to shrink that selected volume with this code :
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume "+index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
Once I do this the result is like this :
http://prntscr.com/mjwg0t (Picture of an unallocated partition only)
I can right click on it and create new simple volume from that unallocated partition but I have to do this with diskpart commands.
Can someone tell me which diskpart commands do I have to use to achieve this?
And how can I get detailed information about this volume?
I have solve my problem. This is my final code:
int index = GetIndexOfDrive(Path.GetPathRoot(#"E:\"));
Process DiskPartProc = new Process();
DiskPartProc.StartInfo.CreateNoWindow = true;
DiskPartProc.StartInfo.UseShellExecute = false;
DiskPartProc.StartInfo.RedirectStandardOutput = true;
DiskPartProc.StartInfo.FileName = #"C:\Windows\System32\diskpart.exe";
DiskPartProc.StartInfo.RedirectStandardInput = true;
DiskPartProc.Start();
DiskPartProc.StandardInput.WriteLine("select volume " + index);
DiskPartProc.StandardInput.WriteLine("shrink desired=16 minimum=16");
DiskPartProc.StandardInput.WriteLine("create partition primary size=16");
DiskPartProc.StandardInput.WriteLine("format fs=ntfs label=\"MyPlace\" quick");
DiskPartProc.StandardInput.WriteLine("exit");
string output = DiskPartProc.StandardOutput.ReadToEnd();
DiskPartProc.WaitForExit();
The problem was something about my own disk. I have put another spare one and did all my test on it and it work perfectly, now I can create a disk volume inside a disk and format it and then I can access it with its volume ID. I still have to find a way to do it in C#. I can do the last part from windows not from C# yet. I need a way to access that volume now. I tried Directory.Exist but it did not worked out.
EDIT : I found a way to check. Since I put only 1 file and nothing else in my volume I use this code :
Process MountProc = new Process();
MountProc.StartInfo.CreateNoWindow = true;
MountProc.StartInfo.UseShellExecute = false;
MountProc.StartInfo.RedirectStandardOutput = true;
MountProc.StartInfo.FileName = "mountvol";
MountProc.StartInfo.RedirectStandardInput = true;
MountProc.Start();
MountProc.StandardInput.WriteLine("mountvol");
MountProc.StandardInput.WriteLine("exit");
string MountOutput = MountProc.StandardOutput.ReadToEnd();
MountProc.WaitForExit();
string VolumeGUID = string.Empty;
List<string> VolList = MountOutput.Split(new string[] { "Possible values for VolumeName along with current mount points are:" }, StringSplitOptions.None)[1].Split('\n').Where(x => x != "\r").Where(x => x != "").ToList();
List<string> ClearList = VolList.Select(s => s.Trim().Replace("\r", "")).ToList();
for (int i = 0; i < ClearList.Count; i++)
{
if (ClearList[i].StartsWith(#"\\?\Volume") && ClearList[i + 1].StartsWith("***"))
{
string tmpPath = ClearList[i].Replace(#"\\?\", #"\\.\");
if (Directory.Exists(tmpPath))
{
string[] DirectoryList = Directory.GetDirectories(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
string[] FileList = Directory.GetFiles(tmpPath, "*.*", SearchOption.TopDirectoryOnly);
if(DirectoryList.Length==0 && FileList.Length==1)
{
if (Path.GetExtension(FileList[0]) == ".license")
{
Console.WriteLine("\n\n\n\t\rLicense file found in : " + FileList[0]);
File.Copy(FileList[0], "LIC.license", true);
Console.WriteLine("\n\t\rContent of license file : " + File.ReadAllText("LIC.license"));
File.Delete("LIC.license");
}
}
}
}
}
Console.ReadKey();
The reason why I copied the file to another location and open it there is I can't open it with File class if it's accessed with it's ID(e.g. \.\Volume{UNIQUE_ID}\)

UnInstalling Programs By registry (UnistallString)

I want to uninstall a software by using my code, my code is working on unistallString "msiexec.exe /x {your-product-code-guid}" and "C:\Program Files\TeamViewer\uninstall.exe\".
but now working on uninstallString like "C:\Program Files\Common Files\Adobe AIR\Versions\1.0\Resources\Adobe AIR Updater.exe -arp:uninstall"..
this throwing exception ("The system cannot find the file specified")
(Here S= MyUnistallString)
try
{
s = s.Replace("\"", ""); //Replace <">
string uninstallArguments = null;
string uninstallAssembly = null;
if (!s.Contains("/"))
{
uninstallAssembly = s;
}
else
{
string[] uninstallArgumentsArray = s.Split(new string[] { " /" }, StringSplitOptions.RemoveEmptyEntries); // Split for any parameters
if (uninstallArgumentsArray.Count() > 1)
{
for (int count = 1; count < uninstallArgumentsArray.Count(); count++)
{
uninstallArguments = "/" + uninstallArgumentsArray[count];
}
}
uninstallAssembly = uninstallArgumentsArray[0];
}
if (!string.IsNullOrWhiteSpace(uninstallAssembly))
{
Process uninstallProcess = new Process();
uninstallProcess.StartInfo = new ProcessStartInfo();
uninstallProcess.StartInfo.FileName = uninstallAssembly;
uninstallProcess.StartInfo.Arguments = uninstallArguments;
uninstallProcess.Start();
}
}
catch (Exception)
{
}
So,I've Found my mistake and it will also help others
Add this after
if (!s.Contains("/"))
if(s.Contains(" -"))
{
string[] uninstallArgumentsArray = s.Split(new string[] { " -" }, StringSplitOptions.RemoveEmptyEntries); // Split for any parameters
if (uninstallArgumentsArray.Count() > 1)
{
for (int count = 1; count < uninstallArgumentsArray.Count(); count++)
{
uninstallArguments += " -" + uninstallArgumentsArray[count];
}
}
this code works for all UnInstallString

Automatically find the path of the python executable

I'm doing a project that uses python as background script and C# as guy.
My problem is that I can't figure out how to cause my GUI to automatically search for the pythonw.exe file in order to run my python scripts.
Currently I'm using this path:
ProcessStartInfo pythonInfo = new ProcessStartInfo(#"C:\\Users\\Omri\\AppData\\Local\\Programs\\Python\\Python35-32\\pythonw.exe");
but I want it to auto detect the path of pythonw.exe (I need to submit the project and it won't run on others computers unless they change the code itself)
Any suggestions may be helpful.
Inspired by #Shashi Bhushan's answer I made this function for getting the Python path reliably;
private static string GetPythonPath(string requiredVersion = "", string maxVersion = "") {
string[] possiblePythonLocations = new string[3] {
#"HKLM\SOFTWARE\Python\PythonCore\",
#"HKCU\SOFTWARE\Python\PythonCore\",
#"HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\"
};
//Version number, install path
Dictionary<string, string> pythonLocations = new Dictionary<string, string>();
foreach (string possibleLocation in possiblePythonLocations) {
string regKey = possibleLocation.Substring(0, 4), actualPath = possibleLocation.Substring(5);
RegistryKey theKey = (regKey == "HKLM" ? Registry.LocalMachine : Registry.CurrentUser);
RegistryKey theValue = theKey.OpenSubKey(actualPath);
foreach (var v in theValue.GetSubKeyNames()) {
RegistryKey productKey = theValue.OpenSubKey(v);
if (productKey != null) {
try {
string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString();
// Comment this in to get (Default) value instead
// string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("").ToString();
if (pythonExePath != null && pythonExePath != "") {
//Console.WriteLine("Got python version; " + v + " at path; " + pythonExePath);
pythonLocations.Add(v.ToString(), pythonExePath);
}
} catch {
//Install path doesn't exist
}
}
}
}
if (pythonLocations.Count > 0) {
System.Version desiredVersion = new System.Version(requiredVersion == "" ? "0.0.1" : requiredVersion),
maxPVersion = new System.Version(maxVersion == "" ? "999.999.999" : maxVersion);
string highestVersion = "", highestVersionPath = "";
foreach (KeyValuePair<string, string> pVersion in pythonLocations) {
//TODO; if on 64-bit machine, prefer the 64 bit version over 32 and vice versa
int index = pVersion.Key.IndexOf("-"); //For x-32 and x-64 in version numbers
string formattedVersion = index > 0 ? pVersion.Key.Substring(0, index) : pVersion.Key;
System.Version thisVersion = new System.Version(formattedVersion);
int comparison = desiredVersion.CompareTo(thisVersion),
maxComparison = maxPVersion.CompareTo(thisVersion);
if (comparison <= 0) {
//Version is greater or equal
if (maxComparison >= 0) {
desiredVersion = thisVersion;
highestVersion = pVersion.Key;
highestVersionPath = pVersion.Value;
} else {
//Console.WriteLine("Version is too high; " + maxComparison.ToString());
}
} else {
//Console.WriteLine("Version (" + pVersion.Key + ") is not within the spectrum.");
}
}
//Console.WriteLine(highestVersion);
//Console.WriteLine(highestVersionPath);
return highestVersionPath;
}
return "";
}
You can find python installation path by lookup following keys on windows machine.
HKLM\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
HKCU\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
for win64 bit machine
HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\versionnumber\InstallPath
You can refer this post for how to read registry using C#
How to read value of a registry key c#
Find the environment variable name in Windows, for that assembly and use Environment.GetEnvironmentVariable(variableName)
Check out How to add to the pythonpath in windows 7?
An example on how to search for Python within the PATH environment variable:
var entries = Environment.GetEnvironmentVariable("path").Split(';');
string python_location = null;
foreach (string entry in entries)
{
if (entry.ToLower().Contains("python"))
{
var breadcrumbs = entry.Split('\\');
foreach (string breadcrumb in breadcrumbs)
{
if (breadcrumb.ToLower().Contains("python"))
{
python_location += breadcrumb + '\\';
break;
}
python_location += breadcrumb + '\\';
}
break;
}
}
Just change the FileName to "python.exe" if you already set python env path
private void runPython(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
On my machine, with Python 3.11 installed, I can query it by defining this property:
public string PythonInstallPath
{
get => (string)Microsoft.Win32.Registry.GetValue(
#"HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\3.11\InstallPath",
"ExecutablePath", null);
}
Pythonw.exe is located in the same path, so you can do:
public string PythonWInstallPath
{
get => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(PythonInstallPath),
"pythonw.exe");
}
There is also a way to look it up in the environment, check this out as an alternative.

How to get the video duration using FFMPEG in C# asp.net

I want to get the video file duration in string using C#. I searched the internet and all i get is:
ffmpeg -i inputfile.avi
And every1 say that parse the output for duration.
Here is my code which is
string filargs = "-y -i " + inputavi + " -ar 22050 " + outputflv;
Process proc;
proc = new Process();
proc.StartInfo.FileName = spath;
proc.StartInfo.Arguments = filargs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardOutput = false;
try
{
proc.Start();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
try
{
proc.WaitForExit(50 * 1000);
}
catch (Exception ex)
{ }
finally
{
proc.Close();
}
Now please tell me how can i save the output string and parse it for the video duration.
Thanks and regards,
There is another Option to get Video Length ,by using Media Info DLL
Using Ffmpeg :
proc.StartInfo.RedirectErrorOutput = true;
string message = proc.ErrorOutput.ReadToEnd();
Filtering shouldn't be an issue ,so do it you're self.
PS : using ffmpeg you should not read the StandardOutput but ErrorOutput i dont know why ,but it work's only like that.
FFmpeg is a little bit of an adventure to parse. But in any case, here's what you need to know.
First, FFmpeg doesn't play well with RedirectOutput options
What you'll need to do is instead of launching ffmpeg directly, launch cmd.exe, passing in ffmpeg as an argument, and redirecting the output to a "monitor file" through a command line output like so... note that in the while (!proc.HasExited) loop you can read this file for real-time FFmpeg status, or just read it at the end if this is a quick operation.
FileInfo monitorFile = new FileInfo(Path.Combine(ffMpegExe.Directory.FullName, "FFMpegMonitor_" + Guid.NewGuid().ToString() + ".txt"));
string ffmpegpath = Environment.SystemDirectory + "\\cmd.exe";
string ffmpegargs = "/C " + ffMpegExe.FullName + " " + encodeArgs + " 2>" + monitorFile.FullName;
string fullTestCmd = ffmpegpath + " " + ffmpegargs;
ProcessStartInfo psi = new ProcessStartInfo(ffmpegpath, ffmpegargs);
psi.WorkingDirectory = ffMpegExe.Directory.FullName;
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.Verb = "runas";
var proc = Process.Start(psi);
while (!proc.HasExited)
{
System.Threading.Thread.Sleep(1000);
}
string encodeLog = System.IO.File.ReadAllText(monitorFile.FullName);
Great, now you've got the log of what FFmpeg just spit out. Now to get the duration. The duration line will look something like this:
Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
Clean up the results into a List<string>:
var encodingLines = encodeLog.Split(System.Environment.NewLine[0]).Where(line => string.IsNullOrWhiteSpace(line) == false && string.IsNullOrEmpty(line.Trim()) == false).Select(s => s.Trim()).ToList();
... then loop through them looking for Duration.
foreach (var line in encodingLines)
{
// Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
if (line.StartsWith("Duration"))
{
var duration = ParseDurationLine(line);
}
}
Here's some code that can do the parse for you:
private TimeSpan ParseDurationLine(string line)
{
var itemsOfData = line.Split(" "[0], "="[0]).Where(s => string.IsNullOrEmpty(s) == false).Select(s => s.Trim().Replace("=", string.Empty).Replace(",", string.Empty)).ToList();
string duration = GetValueFromItemData(itemsOfData, "Duration:");
return TimeSpan.Parse(duration);
}
private string GetValueFromItemData(List<string> items, string targetKey)
{
var key = items.FirstOrDefault(i => i.ToUpper() == targetKey.ToUpper());
if (key == null) { return null; }
var idx = items.IndexOf(key);
var valueIdx = idx + 1;
if (valueIdx >= items.Count)
{
return null;
}
return items[valueIdx];
}
Just check it out::
//Create varriables
string ffMPEG = System.IO.Path.Combine(Application.StartupPath, "ffMPEG.exe");
system.Diagnostics.Process mProcess = null;
System.IO.StreamReader SROutput = null;
string outPut = "";
string filepath = "D:\\source.mp4";
string param = string.Format("-i \"{0}\"", filepath);
System.Diagnostics.ProcessStartInfo oInfo = null;
System.Text.RegularExpressions.Regex re = null;
System.Text.RegularExpressions.Match m = null;
TimeSpan Duration = null;
//Get ready with ProcessStartInfo
oInfo = new System.Diagnostics.ProcessStartInfo(ffMPEG, param);
oInfo.CreateNoWindow = true;
//ffMPEG uses StandardError for its output.
oInfo.RedirectStandardError = true;
oInfo.WindowStyle = ProcessWindowStyle.Hidden;
oInfo.UseShellExecute = false;
// Lets start the process
mProcess = System.Diagnostics.Process.Start(oInfo);
// Divert output
SROutput = mProcess.StandardError;
// Read all
outPut = SROutput.ReadToEnd();
// Please donot forget to call WaitForExit() after calling SROutput.ReadToEnd
mProcess.WaitForExit();
mProcess.Close();
mProcess.Dispose();
SROutput.Close();
SROutput.Dispose();
//get duration
re = new System.Text.RegularExpressions.Regex("[D|d]uration:.((\\d|:|\\.)*)");
m = re.Match(outPut);
if (m.Success) {
//Means the output has cantained the string "Duration"
string temp = m.Groups(1).Value;
string[] timepieces = temp.Split(new char[] {':', '.'});
if (timepieces.Length == 4) {
// Store duration
Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
}
}
With thanks,
Gouranga Das.

Categories

Resources