Diagnostics.Process - Dump output to file - c#

Hi I need to write the result of a mysqldump to a file with a standard windows commands.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.WorkingDirectory = "sample directory";
proc.StartInfo.FileName = "mysqldump";
proc.StartInfo.Arguments = "-u root -pPassword --all-databases > db.sql";
proc.StartInfo.RedirectStandardOutput = false;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
But it doesn't write to file this way...
I don't want to read the output and then write it to file, since mysqldump output can become really big...
Any solutions?

Try executing through cmd.exe and enquote the command to keep your program from gobbling up the redirect:
proc.StartInfo.FileName = "cmd.exe";
proc.startinfo.Arguments =
"/c \"mysqldump -u root -pPassword --all-databases\" > db.sql"

If it's a lot of output you can use the proc.OutputDataReceived event, in the event handler just write the output to your file.
Read the MSDN article here

For piped output you may need ShellExecute to be true. If that doesnt work, you may had to pipe it yourself (i.e. either handle the data events, or have an async read/write loop), but the size shouldn't matter since you should only be reading small chunks of it at any time.

Pretty much on similar lines, but written in VB.NET. Convert it and you should be good... Link

Related

Fast Directory Listing using Shell ( DIR )

I'm doing some work to optimise listing the contents of a Folder across a network drive in C#, file and folders. For files I need the FileName, File Size and DateModified, for folders just the Name and DateModified.
I've searched on StackOverflow for various solutions and settled on using Directory.GetFiles, there's no benefit in using EnumerateFiles as I'm not processing the files in parallel.
With my test case over the WAN with 4000 and 5 sub folders GetFiles can still take 30 seconds or more, yet Windows can DIR the folder in 2 seconds.
I don't want to get into too much Windows API code, so I thought a good middle ground would be Shell out the DIR command, redirect the standard output and parse the input. Not pretty, but should be OK. I found this code which does pretty much what I want:
Process process = new Process();
process.StartInfo.FileName = "ipconfig.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
// Synchronously read the standard output of the spawned process.
StreamReader reader = process.StandardOutput;
string output = reader.ReadToEnd();
This works fine for ipconfig.exe, but DIR isn't an exe, so any ideas how I might call it? I want to redirect something like this:
DIR "\MyNasDrive\MyFolder"
Worst case I could wrap this up in a .bat file, but that feels fairly horrible.
Any ideas appreciated.
== Found my own solution, but if you can see anything wrong with it please let me know ==
string DirPath = "\\\\MYServer\\MyShare\\";
Process process = new Process();
process.StartInfo.FileName = "C:\\Windows\\System32\\cmd.exe";
process.StartInfo.Arguments = "/C DIR /-C \"" + DirPath + "\"";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.Start();
// Synchronously read the standard output of the spawned process. Note we aren't reading the standard err, as reading both
// Syncronously can cause deadlocks. https://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
//if we need to do this in the future then might be able to use https://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline(v=vs.110).aspx
StreamReader reader = process.StandardOutput;
string output = reader.ReadToEnd();
process.WaitForExit();
process.Close();
DIR isn't an exe
cmd.exe is an exe.
Call cmd.exe with 2 parameters:
/C
dir
Note: you could also set UseShellExecute to true.

Process stuck with black cmd screen

So I have this code to launch a bat script which will execute certain java commands, starting with "java -version" just to get some output. The first time I call it it works, but the second time I am stuck with a black cmd screen.
The same code is used but in different locations.
Process proc = new Process();
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.RedirectStandardOutput = true;
StartInfo.RedirectStandardError = true;
StartInfo.FileName = path + "javaScript.bat";
StartInfo.Arguments = "\"" + path + "\"";
StartInfo.UseShellExecute = false;
StartInfo.CreateNoWindow = false;
proc.StartInfo = StartInfo;
proc.Start();
proc.WaitForExit();
string output = proc.StandardOutput.ReadToEnd();
Anyone can help me figure out what happens? Since I don't get any echo I doubt the bat file gets stuck anywhere (echo is on and the first command is java -version so it should write something instead of just getting stuck at black cmd window)
proc.WaitForExit();
string output = proc.StandardOutput.ReadToEnd();
You are deadlocking the process with this code. It cannot exit until you empty its output buffer. But you don't read its output until it exits. The program can't continue, nor can you. A "deadly embrace", better known as deadlock.
Simply swap these two lines of code to fix the problem.
Do note that you have a problem with StandardError as well, it will still deadlock when it sends a bunch of error text to that stream. If you don't want to read it then don't redirect it. If you want to make it completely solid then you'll need to use BeginErrorReadLine and BeginOutputReadLine.

Redirecting console output to another application

So say I run my program:
Process proc = new Process();
proc.StartInfo.FileName = Path.GetDirectoryName(Application.ExecutablePath)
+ #"\Console.exe";
proc.Start();
And then wish to output my console stream to this application, how would I go about doing so?
So say I have:
Console.WriteLine("HEY!");
I want that to show up in program that I ran's console. I know I have to redirect the output using
Console.SetOut(TextWriter);
But I have no idea how I would go about making it write to the other program.
I can see how I could do it if I were running my main program from Console.exe using RedirectStandardInput.. but that doesn't really help :P
RedirectStandardInput makes Console.exe take its input from a stream which you can access in the main program. You can either write directly to that stream, or use SetOut to redirect console output there...
Process proc = new Process();
proc.StartInfo.FileName = Path.GetDirectoryName(Application.ExecutablePath)
+ #"\Console.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
proc.StandardInput.WriteLine("Hello");
Console.SetOut(proc.StandardInput);
Console.WriteLine("World");
EDIT
It's possible that Console.exe doesn't cope well with having data piped into it rather than entered interactively. You could check this from the command line with something like
echo "Hello" | Console.exe
If that doesn't do what you expect, redirecting your program's output won't either. To test your program without worrying about the target program, you could try
proc.StartInfo.FileName = #"cmd";
proc.StartInfo.Arguments = #"/C ""more""";
If that displays the text that you write to it, then the problem is on the receiving end.
RedirectStandardInput isn't the problem. Console is the problem.
StreamWriter myConsole = null;
if (redirect)
{
Process proc = new Process();
proc.StartInfo.FileName = Path.GetDirectoryName(Application.ExecutablePath)
+ #"\Console.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
myConsole = myProcess.StandardInput;
}
else
myConsole = Console.Out;
Then just use myConsole as you would Console.
You need to use Process.StandardOutput and Process.StandardInput. Check out this article from MSDN, which may help point you into the right direction: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput.aspx
By the way, a much easier way to do what you are doing can be found here, as an accepted answer to a similar SO question: c# redirect (pipe) process output to another process

Open txt file from C# application

The following code is suppose to open CMD from my C# application and open the file text.txt.
I tried to set the file path as an environment variable but when notepad opens it looks for %file%.txt instead of text.txt
Any idea why?
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.EnvironmentVariables.Add("file", "c:\\text.txt");
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "notepad";
proc.StartInfo.Arguments="%file%";
proc.Start();
proc.WaitForExit();
Console.WriteLine(proc.ExitCode);
If your purpose is to start the editor with a .txt file (like the title of the question says) just use:
Process.Start("C:\\text.txt")
The short version is that I suspect you are going to have to pass the arg more directly, i.e.
proc.StartInfo.Arguments = #"""c:\text.txt""";
Although you can set environment variables (for use within the process), I don't think you can use them during the process start.
What are you trying to accomplish with %file%? The command line argument for notepad.exe is the file you want to open. You need to do something like this:
proc.StartInfo.Arguments = "c:\\text.txt";
set UseShellExecute = true
that way it should use the cmd.exe processor to expand the %file% variable
One obvious problem is that you have UseShellExecute set false. This means you are executing notepad directly without passing via the command shell cmd.exe. Therefore environment variables aren't being expanded.
I'm not sure what you're trying to achieve (why do you need to add an environment variable?) but the following would work:
System.Diagnostics.Process proc =
new System.Diagnostics.Process();
proc.EnableRaisingEvents = false;
proc.StartInfo.EnvironmentVariables.Add("file", "c:\\text.txt");
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.Arguments = "/c notepad %file%";
proc.Start();
proc.WaitForExit();
Try this:
proc.StartInfo.Arguments = System.Environment.GetEnvironmentVariable("file");
Perhaps it has to do with how the StartInfo.Arguments work. If you can't find anything better, this worked for me:
proc.StartInfo.FileName = "cmd";
proc.StartInfo.Arguments="/c notepad %my_file%";
I am willing to bet you need to set WorkingDirectory to get this to work. NOTEPAD.exe is usually located in %SYSTEMROOT% (C:\windows) however the default is %SYSTEMROOT%\system32. Try out the below.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.WorkingDirectory = "%SYSTEMROOT%";
proc.StartInfo.EnvironmentVariables.Add("file", "c:\\text.txt");
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.FileName = "notepad";
proc.StartInfo.Arguments="%file%";
proc.Start();
proc.WaitForExit();
Console.WriteLine(proc.ExitCode);

Service hangs up at WaitForExit after calling batch file

I have a service that sometimes calls a batch file. The batch file takes 5-10 seconds to execute:
System.Diagnostics.Process proc = new System.Diagnostics.Process(); // Declare New Process
proc.StartInfo.FileName = fileName;
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.WaitForExit();
The file does exist and the code works when I run the same code in-console. However when it runs inside the service, it hangs up at WaitForExit(). I have to kill the batch file from the Process in order to continue. (I am certain the file exists, as I can see it in the processes list.)
How can I fix this hang-up?
Update #1:
Kevin's code allows me to get output. One of my batch files is still hanging.
"C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" -i -h localhost -p 5432 -U postgres -F p -a -D -v -f "c:\backupcasecocher\backupdateevent2008.sql" -t "\"public\".\"dateevent\"" "DbTest"
The other batch file is:
"C:\EnterpriseDB\Postgres\8.3\bin\vacuumdb.exe" -U postgres -d DbTest
I have checked the path and the postgresql path is fine. The output directory does exist and still works outside the service. Any ideas?
Update #2:
Instead of the path of the batch file, I wrote the "C:\EnterpriseDB\Postgres\8.3\bin\pg_dump.exe" for the proc.StartInfo.FileName and added all parameters to proc.StartInfo.Arguments. The results are unchanged, but I see the pg_dump.exe in the process window. Again this only happens inside the service.
Update #3:
I have run the service with a user in the administrator group, to no avail. I restored null for the service's username and password
Update #4:
I created a simple service to write a trace in the event log and execute a batch file that contains "dir" in it. It will now hang at proc.Start(); - I tried changing the Account from LocalSystem to User and I set the admnistrator user and password, still nothing.
Here is what i use to execute batch files:
proc.StartInfo.FileName = target;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit
(
(timeout <= 0)
? int.MaxValue : timeout * NO_MILLISECONDS_IN_A_SECOND *
NO_SECONDS_IN_A_MINUTE
);
errorMessage = proc.StandardError.ReadToEnd();
proc.WaitForExit();
outputMessage = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
I don't know if that will do the trick for you, but I don't have the problem of it hanging.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace VG
{
class VGe
{
[STAThread]
static void Main(string[] args)
{
Process proc = null;
try
{
string targetDir = string.Format(#"D:\adapters\setup");//this is where mybatch.bat lies
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "mybatch.bat";
proc.StartInfo.Arguments = string.Format("10");//this is argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine("Exception Occurred :{0},{1}", ex.Message,ex.StackTrace.ToString());
}
}
}
}
string targetDir = string.Format(#"D:\");//PATH
proc = new Process();
proc.StartInfo.WorkingDirectory = targetDir;
proc.StartInfo.FileName = "GetFiles.bat";
proc.StartInfo.Arguments = string.Format("10");//argument
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
Tested,works clear.
What does the batch file do? Are you certain the process is getting launched with enough privs to execute the batch file? Services can be limited in what they are allowed to do.
Also make sure if you are doing something like usin the copy command to overwrite a file that you do something like:
echo Y | copy foo.log c:\backup\
Also, make sure you are using full paths for the batch commands, etc. If the batch file is launching a GUI app in some sort of "Console" mode, that may be an issue too. Remember, services don't have a "Desktop" (unless you enable the "interact with desktop") to draw any kind of windows or message boxes to. In your program, you might want to open the stdout and stderr pipes and read from them during execution in case you are getting any error messages or anything.
WebServices are probably executing as the IUSR account, or the anonymous account, which ever, so that might be an issue for you. If it works when you run it in console, that's just the first step. :)
I don't recall if System.Diagnostics. are available only in debug or not. Probably not, but some of them might be. I'll have to check up on that for ya.
Hope this gives you some ideas.
Larry
pg_dump.exe is probably prompting for user input. Does this database require authentication? Are you relying on any ENVIRONMENT variables that won't be present for the service? I don't know pg_dump but what are the other possible reasons it would prompt for input?
The next step I would take is to fire up the debugger, and see if you can tell what the program is waiting on. If you are expierenced at debugging in assembly, you may be able to get an IDEA of what's happening using tools like ProcExp, FileMon, etc.
Being a windows SERVICE, and not a web service, makes quite a bit of difference. Anyways, have you tried my suggestion of setting the "Allow Service to interact with desktop"?
If you are desperate, you might try launching cmd.exe instead of your batch file. Then, using the cmd.exe's cmd line parameters, you can have IT start the batch file. This would probably give you a cmd prompt window to view the actual output, if you turn on the interact with desktop.
For complete help on cmd.exe, just type cmd /? at any command prompt.
Larry
Here is the solution. The solution is not clear because I have changed so many time the code and now it's working!
I have tried to use a Account of User, and it's not what worked. Use LocalSystem. Here is the code that execute, mostly what Kevin gave me.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = fileName;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.UseShellExecute = false;
proc.Start();
proc.WaitForExit();
output1 = proc.StandardError.ReadToEnd();
proc.WaitForExit();
output2 = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
Thank you all, I'll up-vote everybody and accept Kevin since he helps me since the beginning. Very weird because it works now...
Daok, it looks as if the only thing you changed was the timeout period on the initial WaitForExit(). You need to be VERY careful of that. If something DOES hang your service, it will NEVER return (and well, pretty much work like it has been for you thus far.. heh), but it won't be good for the end users...
Now, perhaps that you know what's causing this to hang, you can debug it further and find the full solution...
That, or spin this off in some thread that you can monitor, and kill if it hangs too long.
Just my 2 cents worth, which usually isn't a whole lot. ;)

Categories

Resources