I'm trying to get the activation status of Windows. I've got this code:
Process proc = new Process();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/C slmgr /xpr";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
string x = "";
while (!proc.HasExited)
{
x += proc.StandardOutput.ReadToEnd();
}
return x;
As some of you may know, the command "slmgr /xpr" will make a pop-up appear informing you of your Windows activation status.
Executing this code, I get the pop-up box (and "x" is empty).
What I want is to get the text that's in it (so it appears on a label in my form). I wonder if there's any way to extract just the text from inside the pop-up that appears, in this case it would be something like "the machine is permanently activated".
Is there any simple way to achieve this?
slmgr is actually a VBScript file not an executable, when you run it it will default to using the WScript runtime which is for windowed scripts and uses Message Boxes for default output. If you change to CScript you will get console output:
proc.StartInfo.FileName = "cscript.exe";
proc.StartInfo.Arguments = "/nologo \"" + Path.Combine(Environment.SystemDirectory, "slmgr.vbs") + "\" /xpr";
You can then capture this: Capturing console output from a .NET application (C#)
You could also look inside the script file, see whats its doing & reimplement it in your code (ymmv).
I am working with voice records. I need to use an .exe file for convert Wav to .mp3 file. Everything is fine I can execute this exe but I need to do something after when process end with my output .mp3 file. I know my output directory but i cant handle MP3 file before its not created yet. I know maybe I need to use Thread.sleep(); or something like that because I cant catch a file before its not exist.
Here is my code:
string mp3GuidName = Guid.NewGuid().ToString();
var mp3FilePath = WavFilePath.Replace("finalWavFile", mp3GuidName).Replace("wav", "mp3");
var extrasFilePath = HttpContext.Current.Server.MapPath("/").Replace("DevApp.Web", "Extras");
string strArguments = "/c start " + extrasFilePath + "lame.exe --abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = strArguments ;
process.StartInfo = startInfo;
process.Start();
var attactment = new Attachment
{
CreatedOn = DateTime.Now,
UpdatedOn = DateTime.Now,
Title = mp3GuidName +".mp3",
Size = _storageProvider.GetFile(mp3FilePath).GetSize(), // here I am trying to get mp3 file but i cant catch it. Because if this wav files size is huge, then convert process is taking time and my mp3 file is not created yet.
FileExtension = _storageProvider.GetFile(mp3FilePath).GetFileType()
};
attactment.MimeType = _storageProvider.GetMimeType(attactment.FileExtension);
attactment.FileUrl = mp3GuidName+".mp3";// file.GetName();
attactment.AttachmentFolderId = folder.Id;
_attachmentRepository.Add(attactment);
I was try to use process.WaitForExit();but I cant solve this problem. I still cant acces to mp3 file.
so how can I catch when the process finish?
Best Regards.
Remove the start command argument from your argument string and you should be able to use process.WaitForExit(); to wait for Lame to finish with encoding:
string strArguments = "/c " + extrasFilePath + "lame.exe --abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
However, you can simplify your code and avoid this dance with cmd.exe altogether by starting lame.exe directly:
string strArguments = "--abr 80 -V5 " + WavFilePath + " " + mp3FilePath;
...
startInfo.FileName = extrasFilePath + "lame.exe";
startInfo.Arguments = strArguments;
...
Below some information about why using the start command argument in your scenario is counterproductive.
Executing console applications such as lame.exe using cmd.exe (or from a console or batch file) normally block cmd.exe (or console/batch file) until the console application exits.
However, using the start command turns this normally blocking execution of a console application into a non-blocking execution. With this argument, the cmd.exe (or console/batch file) will continue execution while the console application is still running. In your particular case it means cmd.exe will exit right after it has started lame.exe (since it has nothing else to execute), effectively sabotaging your attempt to wait for lame.exe to finish.
Edited:
Base on the comments below I have to make it clear that the oroginal solution I recommended does not check if the file is free but it checks only if the file exists!
So I rather recommend the following:
private bool IsBusy(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch ()
{
return true;
}
finally
{
if (stream != null)
stream.Close();
}
return false;
}
the usage will be:
while(IsBusy(fileinfo))
{
//just wait
}
ORIGINAL:
You can use a while loop to find out when the file is ready:
while(!File.Exists(mp3FileName))
{
//just wait
}
// add the attachment here
I'm new to c# as well as this website so I'll probably make some mistakes but hopefully I'll learn as well.
I'm trying to develop an app to extract from a .7z a file, read it and delete it before moving to the next (there is about 12k files in there, which take up a lot of space).
Now, I'm using .NET 2.0 and I haven't found easy solutions to extract single files from an archive. I came across a post explaining that you could use 7za.exe to do so from the command line, and so I did. Now, the problem is that if I try to do the same in my app 7za throws the "cannot use absolute pathnames" error. However, as I said, the same parameters work at the command prompt.
My idea is to get a list of all the files in the compressed folder and put it in a textfile. From there it is as easy as get the name of the first one, unzip it, read it, delete it and move to next. This is what I got to get the list:
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = #"C:\Users\yadayada\Desktop\7za.exe"; //just testing
string parameters= "-y l " + path_file7z + " > " + path_file_destination + #"\file.txt";
//needeless to say, path_file7z and path_file_destination are strings with the correct path like "C:\Users\yadayada\Desktop"
startInfo.Arguments = parameters
process.StartInfo = startInfo;
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();
As I said, as the process ends I can see a console opening and closing. I took a screenshot to check what it said and the error I got was the one I said at the begining of the question.
Now, does anyone know why I get this error and how can I fix this?
SOLUTION
Ended un going for a not too elegant (being gentle) solution that works.
To list the names:
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = #"cmd.exe";
string parameters= "/k -y l " + path_file7z + " > " + path_file_destination + #"\file.txt";
//needeless to say, path_file7z and path_file_destination are strings with the correct path like "C:\Users\yadayada\..."
startInfo.Arguments = parameters
process.StartInfo = startInfo;
process.StartInfo.UseShellExecute = false;
process.Start();
Thread.Sleep(1000);
process.Kill();
Now, once this has been done the extract part is tanken care of by a method similar to the one provided by Igor's links:
public void ExtractFile(string source, string element, string destination)
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = /*7zG.exe's path*/;
startInfo.Arguments = #"-y x " + source + " -o" + destination + " " + element + " -r";
process.StartInfo = startInfo;
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();
}
I think it is working well now. If I have further issues I'll update this with the solutions I come up with with the help provided. Thanks again!
Use this tutorial for 7zip:
https://stick2basic.wordpress.com/2013/04/25/how-to-extract-7z-file-to-a-folder-c-net/
Use this library if rar and zip are allowed:
http://dotnetzip.codeplex.com/
private void MyExtract()
{
string zipToUnpack = "C1P3SML.zip";
string unpackDirectory = "Extracted Files";
using (ZipFile zip1 = ZipFile.Read(zipToUnpack))
{
// here, we extract every entry, but we could extract conditionally
// based on entry name, size, date, checkbox status, etc.
foreach (ZipEntry e in zip1)
{
e.Extract(unpackDirectory, ExtractExistingFileAction.OverwriteSilently);
}
}
}
EDIT
This can help you:
Unzip a file in c# using 7z.exe
Happy to help you!
I am writing a C# winform application that starts a second process to execute shell commands like "dir" and "ping". I redirect the second process's output so my app can receive the command result. It roughly works fine.
The only problem is my winform app receives the command line output as a whole instead of line by line. For example, it has to wait for the external "ping" command to finish (which takes many seconds or longer) and then receives the whole output (many lines) at once.
What I want is the app receives the cmdline output in real-time, i.e. by lines not by block. Is this doable?
I am using this code to read the output:
while ((result = proc.StandardOutput.ReadLine()) != null)
But it does not work the way I expected.
Thanks in advance.
EDIT: here is the code I am using:
System.Diagnostics.ProcessStartInfo procStartInfo = new
System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// The following commands are needed to redirect the standard output.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
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;
try {
while ((result = proc.StandardOutput.ReadLine()) != null)
{
AppendRtfText(result+"\n", Brushes.Black);
}
} // here I expect it to update the text box line by line in real time
// but it does not.
Have a look at the example in this msdn article on how to do the reading completly async.
Beyond that I expect your code does to read line by line now but the UI doesn't get any time to repaint (missing Application.DoEvents(); after updating the RTFTextBox
Instead of loop using while ((result = proc.StandardOutput.ReadLine()) != null) you should of using:
...
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
This will start asynchronous reading the lines when they arrives, you then handle the lines read by e.Data in proc_DataReceived handler, since you are use BeginOutputReadline the e.Data will be a string lines.
This could be usefull:
http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/8d6cebfc-9b8b-4667-85b5-2b92105cd0b7/
http://www.dotnetperls.com/redirectstandardoutput
I had the same issue and got around it with the following. I found that if I had an error in the external app I was getting no output at all using the ReadToEnd() method, so switched to use the line by line streamreader. Will be switching over to use the answer provided by Saa'd though as that looks like the proper way to handle it.
Also found this solution: c# coding convention public/private contexts which provides for error handling at the same time and giving a fuller explanation to the use of externalApp.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
Process externalApp = new Process();
externalApp.StartInfo.FileName = config.ExternalApps + #"\location\DeleteApp.exe";
externalApp.StartInfo.Arguments = Directory.GetCurrentDirectory() + #"\..\..\..\project\argumentsForDeleteApp.xml";
externalApp.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
externalApp.StartInfo.UseShellExecute = false;
externalApp.StartInfo.RedirectStandardOutput = true;
Console.Out.WriteLine(DateTime.UtcNow.ToLocalTime().ToString() +
":###### External app: " + externalApp.StartInfo.FileName + " - START");
externalApp.Start();
using (StreamReader reader = externalApp.StandardOutput)
{
while (!reader.EndOfStream)
{
string result = reader.ReadLine();
Console.Out.WriteLine(result);
}
}
externalApp.WaitForExit();
Is there any way to run command prompt commands from within a C# application? If so how would I do the following:
copy /b Image1.jpg + Archive.rar Image2.jpg
This basically embeds an RAR file within JPG image. I was just wondering if there was a way to do this automatically in C#.
this is all you have to do run shell commands from C#
string strCmdText;
strCmdText= "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
System.Diagnostics.Process.Start("CMD.exe",strCmdText);
EDIT:
This is to hide the cmd window.
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
process.StartInfo = startInfo;
process.Start();
EDIT 2:
It is important that the argument begins with /C, otherwise it won't work. As #scott-ferguson said: /C carries out the command specified by the string and then terminates.
Tried RameshVel's solution but I could not pass arguments in my console application. If anyone experiences the same problem here is a solution:
using System.Diagnostics;
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine("echo Oscar");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
Console.WriteLine(cmd.StandardOutput.ReadToEnd());
var proc1 = new ProcessStartInfo();
string anyCommand;
proc1.UseShellExecute = true;
proc1.WorkingDirectory = #"C:\Windows\System32";
proc1.FileName = #"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c "+anyCommand;
proc1.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(proc1);
None of the above answers helped for some reason, it seems like they sweep errors under the rug and make troubleshooting one's command difficult. So I ended up going with something like this, maybe it will help someone else:
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\tf.exe",
Arguments = "checkout AndroidManifest.xml",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
WorkingDirectory = #"C:\MyAndroidApp\"
}
};
proc.Start();
Though technically this doesn't directly answer question posed, it does answer the question of how to do what the original poster wanted to do: combine files. If anything, this is a post to help newbies understand what Instance Hunter and Konstantin are talking about.
This is the method I use to combine files (in this case a jpg and a zip). Note that I create a buffer that gets filled with the content of the zip file (in small chunks rather than in one big read operation), and then the buffer gets written to the back of the jpg file until the end of the zip file is reached:
private void CombineFiles(string jpgFileName, string zipFileName)
{
using (Stream original = new FileStream(jpgFileName, FileMode.Append))
{
using (Stream extra = new FileStream(zipFileName, FileMode.Open, FileAccess.Read))
{
var buffer = new byte[32 * 1024];
int blockSize;
while ((blockSize = extra.Read(buffer, 0, buffer.Length)) > 0)
{
original.Write(buffer, 0, blockSize);
}
}
}
}
if you want to run the command in async mode - and print the results. you can you this class:
public static class ExecuteCmd
{
/// <summary>
/// Executes a shell command synchronously.
/// </summary>
/// <param name="command">string command</param>
/// <returns>string, as output of the command.</returns>
public static void ExecuteCommandSync(object command)
{
try
{
// create the ProcessStartInfo using "cmd" as the program to be run, and "/c " as the parameters.
// Incidentally, /c tells cmd that we want it to execute the command that follows, and then exit.
System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
// 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();
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
Console.WriteLine("ExecuteCommandSync failed" + objException.Message);
}
}
/// <summary>
/// Execute the command Asynchronously.
/// </summary>
/// <param name="command">string command.</param>
public static void ExecuteCommandAsync(string command)
{
try
{
//Asynchronously start the Thread to process the Execute command request.
Thread objThread = new Thread(new ParameterizedThreadStart(ExecuteCommandSync));
//Make the thread as background thread.
objThread.IsBackground = true;
//Set the Priority of the thread.
objThread.Priority = ThreadPriority.AboveNormal;
//Start the thread.
objThread.Start(command);
}
catch (ThreadStartException )
{
// Log the exception
}
catch (ThreadAbortException )
{
// Log the exception
}
catch (Exception )
{
// Log the exception
}
}
}
if you want to keep the cmd window open or want to use it in winform/wpf then use it like this
string strCmdText;
//For Testing
strCmdText= "/K ipconfig";
System.Diagnostics.Process.Start("CMD.exe",strCmdText);
/K
Will keep the cmd window open
Yes, there is (see link in Matt Hamilton's comment), but it would be easier and better to use .NET's IO classes. You can use File.ReadAllBytes to read the files and then File.WriteAllBytes to write the "embedded" version.
with a reference to Microsoft.VisualBasic
Interaction.Shell("copy /b Image1.jpg + Archive.rar Image2.jpg", AppWinStyle.Hide);
This can also be done by P/Invoking the C standard library's system function.
using System.Runtime.InteropServices;
[DllImport("msvcrt.dll")]
public static extern int system(string format);
system("copy Test.txt Test2.txt");
Output:
1 file(s) copied.
Here is little simple and less code version. It will hide the console window too-
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/C copy /b Image1.jpg + Archive.rar Image2.jpg";
process.Start();
I have the following method, which I use to run the command prompt commands from C#
In first parameter pass the command you want to run
public static string RunCommand(string arguments, bool readOutput)
{
var output = string.Empty;
try
{
var startInfo = new ProcessStartInfo
{
Verb = "runas",
FileName = "cmd.exe",
Arguments = "/C "+arguments,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = false
};
var proc = Process.Start(startInfo);
if (readOutput)
{
output = proc.StandardOutput.ReadToEnd();
}
proc.WaitForExit(60000);
return output;
}
catch (Exception)
{
return output;
}
}
You can achieve this by using the following method (as mentioned in other answers):
strCmdText = "'/C some command";
Process.Start("CMD.exe", strCmdText);
When I tried the methods listed above I found that my custom command did not work using the syntax of some of the answers above.
I found out more complex commands need to be encapsulated in quotes to work:
string strCmdText;
strCmdText = "'/C cd " + path + " && composer update && composer install -o'";
Process.Start("CMD.exe", strCmdText);
you can use simply write the code in a .bat format extension ,the code of the batch file :
c:/ copy /b Image1.jpg + Archive.rar Image2.jpg
use this c# code :
Process.Start("file_name.bat")
You can use RunProcessAsTask pacakge and run your process async and easily like this:
var processResults = await ProcessEx.RunAsync("git.exe", "pull");
//get process result
foreach (var output in processResults.StandardOutput)
{
Console.WriteLine("Output line: " + output);
}
This may be a bit of a read so im sorry in advance. And this is my tried and tested way of doing this, there may be a simpler way but this is from me throwing code at a wall and seeing what stuck
If it can be done with a batch file then the maybe over complicated work around is have c# write a .bat file and run it. If you want user input you could place the input into a variable and have c# write it into the file. it will take trial and error with this way because its like controlling a puppet with another puppet.
here is an example, In this case the function is for a push button in windows forum app that clears the print queue.
using System.IO;
using System;
public static void ClearPrintQueue()
{
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"#echo off",
"net stop spooler",
"del %systemroot%\\System32\\spool\\Printers\\* /Q",
"net start spooler",
//this deletes the file
"del \"%~f0\"" //do not put a comma on the last line
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
IF you want user input then you could try something like this.
This is for setting the computer IP as static but asking the user what the IP, gateway, and dns server is.
you will need this for it to work
public static void SetIPStatic()
{
//These open pop up boxes which ask for user input
string STATIC = Microsoft.VisualBasic.Interaction.InputBox("Whats the static IP?", "", "", 100, 100);
string SUBNET = Microsoft.VisualBasic.Interaction.InputBox("Whats the Subnet?(Press enter for default)", "255.255.255.0", "", 100, 100);
string DEFAULTGATEWAY = Microsoft.VisualBasic.Interaction.InputBox("Whats the Default gateway?", "", "", 100, 100);
string DNS = Microsoft.VisualBasic.Interaction.InputBox("Whats the DNS server IP?(Input required, 8.8.4.4 has already been set as secondary)", "", "", 100, 100);
//this is the path the document or in our case batch file will be placed
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
//this is the path process.start usues
string path1 = docPath + "\\Test.bat";
// these are the batch commands
// remember its "", the comma separates the lines
string[] lines =
{
"SETLOCAL EnableDelayedExpansion",
"SET adapterName=",
"FOR /F \"tokens=* delims=:\" %%a IN ('IPCONFIG ^| FIND /I \"ETHERNET ADAPTER\"') DO (",
"SET adapterName=%%a",
"REM Removes \"Ethernet adapter\" from the front of the adapter name",
"SET adapterName=!adapterName:~17!",
"REM Removes the colon from the end of the adapter name",
"SET adapterName=!adapterName:~0,-1!",
//the variables that were set before are used here
"netsh interface ipv4 set address name=\"!adapterName!\" static " + STATIC + " " + STATIC + " " + DEFAULTGATEWAY,
"netsh interface ipv4 set dns name=\"!adapterName!\" static " + DNS + " primary",
"netsh interface ipv4 add dns name=\"!adapterName!\" 8.8.4.4 index=2",
")",
"ipconfig /flushdns",
"ipconfig /registerdns",
":EOF",
"DEL \"%~f0\"",
""
};
//this writes the string to the file
using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "test.bat")))
{
//This writes the file line by line
foreach (string line in lines)
outputFile.WriteLine(line);
}
System.Diagnostics.Process.Start(path1);
}
Like I said. It may be a little overcomplicated but it never fails unless I write the batch commands wrong.