I'm trying to run the sqlite.exe tool as a process in my c# code in order to read a sql from a file.
If I run the sqLite3 tool in powershell then it works fine (sqlite3.exe "mydatabase.db" ".read mySql.sql")
But when I run the sqlite3 tool from my c# code as a process, then nothing happens to mydatabase.db. It's still 0b when sqlite3 terminates.
I get no error message, the output from the sqlite3.exe is an empty string and the exit code is 1 (verified in the exit event). Does anyone have a clue why the database.db why the records in the .sql file is not added to the .db file?.
using (Process pProcess = new Process())
{
pProcess.StartInfo.FileName = sqlLite3ExePath;
pProcess.StartInfo.Arguments = $"\"{sqLitePath2}\" \".read {sqlPath}\"";;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;//System.Diagnostics.ProcessWindowStyle.Hidden;
pProcess.StartInfo.CreateNoWindow = false;//true; //not diplay a windows
pProcess.EnableRaisingEvents = true;
pProcess.Exited += PProcess_Exited;
pProcess.Start();
string output = pProcess.StandardOutput.ReadToEnd(); //The output result
pProcess.WaitForExit();
Debug.WriteLine(output);
}
As CaiusJard said in the comments, an error was passed in the error stream. Adding the following lines told me that my path was wrong.
pProcess.StartInfo.RedirectStandardError = true;
string error = pProcess.StandardError.ReadToEnd(); //The output result
The path divider "\" was removed since the path was parsed twice. Once setting the argument, and once when it was read by the tool. Replacing "\" with "/" in my paths did the trick
I am using psexec.exe to install some software on a large amount of computers. I would like to be able to catch the %ERRORLEVEL% (or its c# equivalent) and write them to a TXT file. ( cmd.exe exited on 6440-nbpb00f51w with error code 0.)
After extensive research, I am finding several possibilities. My end-goal will be to have a .CSV file. Each row would have the computer name and the output of the psexec command. Using this data I will be able to determine which computers the update was successful on.
string[] pcnamearray = new string[] {"COMPUTERNAME1", "COMPUTERNAME2"};
foreach (string i in pcnamearray)
{
Process p = new Process();
string psexeclaunch = "psexec.exe";
p.StartInfo.FileName = psexeclaunch;
p.StartInfo.RedirectStandardOutput = false;
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = false;
string arg = "/silent";
p.StartInfo.Arguments = #" -i \\" + i + #" -u mydomain\admin -p mypassword -h -e cmd.exe /c start \\networkserver\sw\myfile.exe" + arg;
p.Start();
}
I am not here looking for someone to write the code for me. I am here to ask people for their advice, or past experiences with this. I know there are several different methods available. Ideally, I would prefer to have a short sweet line for each result, but if i have to take the entire output and use macros to shave them down to a more readable form, that is fine too.
So far, I have tried:
Console.WriteLine(p.StandardOutput.ReadToEnd());
And...
System.IO.File.WriteAllLines(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt", lines);
And...
FileStream filestream = new FileStream(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt", FileMode.Append);
var streamwriter = new StreamWriter(filestream);
streamwriter.AutoFlush = true;
Console.SetOut(streamwriter);
Console.SetError(streamwriter);
The last one seems to me like the best one but cannot seem to get it to work. I have tried including it in my foreach loop, but file is in use by another process, so after looking into it i found that it needed to be on its own. Even with it alone, i still cannot get it to work.
Please help/advise!
I was able to get it to actaully modify my TXT file, even though there was nothing added to it, by using the following:
System.IO.StreamReader reader = p.StandardOutput;
String sRes = reader.ReadToEnd();
StreamWriter SW;
SW = File.CreateText(#"C:\Users\areu2447\Desktop\UpdateDAT\Final\out.txt");
SW.WriteLine(sRes);
SW.Close();
Console.WriteLine("File Created Successfully");
reader.Close();
I would recommend using System.Management.Automation to create a PowerShell pipeline and run the script in process.
Executing PowerShell scripts from C#
I have .sql file (550 MB) and I want to import it to running mysql server. I know path to mysql.exe.
My idea is to immitate command line import mysql -u user -ppass db_name < file.sql. This from command line works well (I have set high max_allowed_packet). According to another thread here on Stackoverflow I found this working:
Process process = new Process();
process.StartInfo.FileName = mysqlexepath;
process.StartInfo.Arguments = "-v -u user -ppassworddbname";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
try
{
process.Start();
StreamWriter input = process.StandardInput;
using (StreamReader sr = new StreamReader(sqlfilepath))
{
while ((line = sr.ReadLine()) != null)
{
if (process.HasExited == true)
throw new Exception("DB went away.");
input.WriteLine(line);
input.Flush();
}
}
process.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I can see how tables are being created in DB. BUT my problem is that in about half the process exits. I was googling for some timeout settings but couldnt find anything.
I also tried to read file first:
var file = FileInfo(sqlfilepath);
StreamReader reader = file.OpenText();
string fileContents = reader.ReadToEnd();
StreamWriter input = process.StandardInput;
input.AutoFlush = true;
input.Write(fileContents);
input.Close();
But I get OutOfMemory exception. So the proper way doesnt lead through string.
I would be very thankful for any advice how to find out where is the problem. I dont even know if its Process timeout or mysql timeout or if the problem is in StreamReader.
I know this isn't a direct answer to your problem, honestly I'm not sure what is wrong with your approach. I can help by sharing how we run very large sql scripts using mysql.exe...
"C:\Program Files (x86)\MySQL\MySQL Server 5.0\bin\mysql.exe" -C -B --password=[password] -P 3306 --user=[username] --host=localhost --database=[database] -e "\. C:\Backups\Mybackup.sql"
Most of these parameters are obvious, connection info, etc.
What isn't obvious is the magical part -e "\. [filename]" the -e parameter specified that mysql should run the following command and exit. The prefix "\. " indicates that an input file should be used and is followed by that file name.
We use this to restore multi-gigabyte databases without issue. So here is the complete 'run a scirpt' with mssql...
public static int RunMySql(string server, int port, string user, string password, string database, string filename)
{
var process = Process.Start(
new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\MySQL\MySQL Server 5.0\bin\mysql.exe",
Arguments =
String.Format(
"-C -B --host={0} -P {1} --user={2} --password={3} --database={4} -e \"\\. {5}\"",
server, port, user, password, database, filename),
ErrorDialog = false,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WorkingDirectory = Environment.CurrentDirectory,
}
);
process.OutputDataReceived += (o, e) => Console.Out.WriteLine(e.Data);
process.ErrorDataReceived += (o, e) => Console.Error.WriteLine(e.Data);
// process.Start(); NOTE: NO need to start the process, because Process.Start has already started the process
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.StandardInput.Close();
process.WaitForExit();
return process.ExitCode;
}
I had the same problem and was too stubborn to use the accepted solution, even though it has gone unchallenged for two years. Also, I wanted to import from a .gz without first decompressing it to another file (and didn't want to have to assume a gzip executable was available). So I worked out what was wrong with the original code.
MySQL dump files can have very long lines with no breaks, so my guess is that ReadLine() is filling up a buffer without ever finding a newline character, causing the OutOfMemory exception. So Read() has to be used instead of ReadLine() to avoid this.
The other problem that may occur (which I had) is with binary blobs within a .sql file, which can get messed up by the text re-encoding when moving text strings from a file to stdin, so I've modified the code to avoid using Readers or Writers, instead using bytes and binary.
Here's my solution:
Process process = new Process();
process.StartInfo.FileName = mysqlexepath;
process.StartInfo.Arguments = "-v -u user -ppassworddbname";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
byte[] buffer = new byte[4096];
int count;
try
{
process.Start();
BinaryWriter stdinBinary = new BinaryWriter(process.StandardInput.BaseStream);
Stream fileStream;
if (sqlfilepath.EndsWith(".gz")) {
fileStream = new GZipStream(new FileStream(sqlfilepath, FileMode.Open, FileAccess.Read, FileShare.Read), CompressionMode.Decompress);
} else {
fileStream = new FileStream(sqlfilepath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
using (fileStream)
{
while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
if (process.HasExited == true)
throw new Exception("DB went away.");
stdinBinary.Write(buffer, 0, count);
stdinBinary.Flush(); // probably not needed
}
}
stdinBinary.Flush();
process.Close();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
I'm importing a 1.1 GB sql.gz database dump (3.5 GB uncompressed) without any problems, but I'd welcome any code improvements.
I have faced so many problems, the issue is not the code, your code is correct, the problem is the limit of virtual memory that the computer does not support working with a file that big on memory per process. The system of Windows processes limits the amount of memory that executes each process to prevent the system crashes.
I do not know the limit because it varies for each computer architecture and the amount of memory, bus, etc. ..
An advice would you from the file, creating tables in sequence, read a piece run, read another run, and so on.
Another idea would be to try to work in parallel on another thread or process, watch this: http://www.codeproject.com/Articles/189374/The-Basics-of-Task-Parallelism-via-C
When debugging .NET application, windbg+SOS is a good tool to choice.
install SOS extension for windbg http://msdn.microsoft.com/en-us/library/bb190764.aspx
start your program by windbg
when exception happens, check the HEAP Objects and CallStack to specific Exception object in HEAP, to find the reason.
http://blogs.msdn.com/b/johan/archive/2007/01/11/i-am-getting-outofmemoryexceptions-how-can-i-troubleshoot-this.aspx
gcroot command can find references to Object in HEAP.
http://blogs.msdn.com/b/tess/archive/2006/01/23/net-memory-leak-case-study-the-event-handlers-that-made-the-memory-baloon.aspx
I'm trying to create a small program to run on a centralized device. This program will run
"psexec \server(s) netstat -na | findstr "LISTENING""
to collect netstat data from remote nodes (should redirect output to string), then parse the data and compare against a known list. I can run the psexec cmd above without any issues from the cmd line, but when I try to run the same command as a process within my C# program, no data is returned to be parsed. I can see that the netstat is being run (cmd window flashes with netstat results), but the process.standardoutput is not catching the stream. If I use ping or pretty much anything other than psexec as an argument, the stream is caught and the results are shown in my text box. I've also tried setting the filename to psexec.exe and specifying the arguments but I get the same results. Last but not least, if I run psexec without any arguments, I get the help kickback info returned in my textbox. This is true if I'm running psexec.exe as the filename OR if I run cmd.exe as filename with "/c psexec" specified as args.
I'm just trying to get psexec output to be caught when executing locally at this point. I'll worry about psexec to remote machines later. Any help would be MUCH appreciated.
Here's the code:
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "cmd.exe";
pProcess.StartInfo.Arguments = "/c psexec netstat";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
string strOutput = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
if (pProcess.HasExited)
{
textBox1.Text = strOutput;
}
else
{
textBox1.Text = "TIMEOUT FAIL";
}
I would recommend also capturing the standard error output in case anything is being reported there.
Also, you may have a disconnect between "bitness" of psexec and your application if you are running on a 64-bit OS. If this is the case, change the platform for the project to match that of psexec rather than building as Any CPU.
Came across a few things to be changed but your recommendation of capturing standard error output was dead on and a good start. Turns out some info was being sent to the error output (even though really wasn't error, just run status 0 from psexec) so I knew at that point psexec wasn't just eating ALL the output. Once I started trying to pass remote hosts as args, I started getting user/pass error data back. Also needed to catch standard input if I wanted to supply credentials for proc run. Threw in some str literals and credentials for the remote exec, works perfectly. Thanks for the help. Here is the updated code--
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.Domain = "domain";
pProcess.StartInfo.UserName = "user with priv";
pProcess.StartInfo.Password = new System.Security.SecureString();
char [] pass = textBox3.Text.ToArray();
for (int x = 0; x < pass.Length; ++x)
{
pProcess.StartInfo.Password.AppendChar(pass[x]);
}
pProcess.StartInfo.FileName = #"psexec.exe";
pProcess.StartInfo.Arguments = #"\\remoteHost netstat -ano";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardInput = true;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.Start();
pProcess.WaitForExit(30000);
if (!pProcess.HasExited)
{
pProcess.Kill();
}
string strOutput = pProcess.StandardOutput.ReadToEnd();
string errOutput = pProcess.StandardError.ReadToEnd();
textBox1.Text = strOutput;
textBox2.Text = errOutput;
I am new to C# so please sorry if i make no sense in my question. In my application which is C# DLL need to open command prompt, give a plink command for Linux system to get a system related string and set that string as environment variable. I am able to do this when i create C# console application, using plink command to get the string on command prompt and use to set it environment variable using process class in C# to open plink as separate console process. But, in C# DLL i have to open cmd.exe 1st and then give this command which i don't know how can i achieve? I tried through opening cmd.exe as process and then trying to redirect input and output to process and give command and get string reply, but no luck. Please let me know any other way to solve this.
Thanks for answers,
Ashutosh
Thanks for your quick replys. It was my mistake in writing code sequence. Now few changes and the code is working like charm. Here is code,
string strOutput;
//Starting Information for process like its path, use system shell i.e. control process by system etc.
ProcessStartInfo psi = new ProcessStartInfo(#"C:\WINDOWS\system32\cmd.exe");
// its states that system shell will not be used to control the process instead program will handle the process
psi.UseShellExecute = false;
psi.ErrorDialog = false;
// Do not show command prompt window separately
psi.CreateNoWindow = true;
psi.WindowStyle = ProcessWindowStyle.Hidden;
//redirect all standard inout to program
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
//create the process with above infor and start it
Process plinkProcess = new Process();
plinkProcess.StartInfo = psi;
plinkProcess.Start();
//link the streams to standard inout of process
StreamWriter inputWriter = plinkProcess.StandardInput;
StreamReader outputReader = plinkProcess.StandardOutput;
StreamReader errorReader = plinkProcess.StandardError;
//send command to cmd prompt and wait for command to execute with thread sleep
inputWriter.WriteLine("C:\\PLINK -ssh root#susehost -pw opensuselinux echo $SHELL\r\n");
Thread.Sleep(2000);
// flush the input stream before sending exit command to end process for any unwanted characters
inputWriter.Flush();
inputWriter.WriteLine("exit\r\n");
// read till end the stream into string
strOutput = outputReader.ReadToEnd();
//remove the part of string which is not needed
int val = strOutput.IndexOf("-type\r\n");
strOutput = strOutput.Substring(val + 7);
val = strOutput.IndexOf("\r\n");
strOutput = strOutput.Substring(0, val);
MessageBox.Show(strOutput);
I explained the code so far..., thanks a lot