Input to processes (batch files) - c#

I'm trying to make some kind of app, in C# Windows Forms Application (not console one, with tab pages, configuration, and console as a list box).
My problem is, that when I am writing some kind of input (to the text box), nothing happens (I'm new to coding).
My code:
Process process = new Process
{
StartInfo =
{
FileName = textBox2.Text,
//Arguments = textBox3.Text,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
CreateNoWindow = false,
}
};
server = process;
process.Start();
...
/* LATER */
...
serverInput = process.StandardInput;
...
serverInput.Write(textBoxInput.Text);
UPDATE - SOLVED: code:
serverInput.WriteLine(...);

Method 1:
This will be enough to run a batch files present in a directory:
string[] arrBatFiles = Directory.GetFiles(textBox2.Text, "*.bat"); //search at directory path
//loop through all batch files
foreach(string sFile in arrBatFiles)
{
Process.Start(sFile);
}
Method 2:
If you want to use ProcessStartInfo members, use following method:
public void ExecuteCommand(string sBatchFile, string command)
{
int ExitCode;
ProcessStartInfo ProcessInfo;
Process process;
string sBatchFilePath = textBox2.Text; //batch file Path
ProcessInfo = new ProcessStartInfo(sBatchFile, command);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = Path.GetDirectoryName(sBatchFile);
// *** Redirect the output ***
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
process = Process.Start(ProcessInfo);
process.WaitForExit();
// *** Read the streams ***
string sInput = process.StardardInput.ReadToEnd();
string sOutput = process.StandardOutput.ReadToEnd();
string sError = process.StandardError.ReadToEnd();
ExitCode = process.ExitCode;
}
UPDATE:
How to call when you have directory of batch files.
string[] arrBatFiles = Directory.GetFiles(textBox2.Text, "*.bat"); //search at directory path
//loop through all batch files
foreach(string sFile in arrBatFiles)
{
ExecuteCommand(sFile, string.Empty); //string.Empty refer optional command args
}

Related

How to take window ID of a created process in c#?

I am working on a C# .net core project.I created a process to run "xdotool windowactivate $windowpid".I should store the windowID which process run on it.The solution could be any property of xdotool which i couldn't find,or Is there any way to take windowId of a process when it is created?
Another Try is that:
I created my pages with this method. I tried to take mainwindowtitle of process;because of single process,i couldn't take the titles.
static List<string> chromeTitles = new List<string>();
public static Process StartChrome(string filePath)
{
string dataDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Chrome-UserData");
filePath += " --user-data-dir=" + dataDirectory;
var Chrome = new Process
{
StartInfo =
{
FileName = "C:/Program/chrome.exe",
Arguments = filePath,
UseShellExecute = false,
CreateNoWindow=true,
WindowStyle = ProcessWindowStyle.Maximized,
}
};
Chrome.Start();
string title = Chrome.MainWindowTitle;
chromeTitles.Add(title);
}
Then I call it :
StartChrome("https://tr.wikipedia.org/wiki/Anasayfa");
Thread.Sleep(2000);
StartChrome("https://tureng.com/");
You can use the Process class for accessing more capabilities.
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "xdotool.exe";
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.Arguments = $"windowactivate $windowpid";
process.StartInfo = startInfo;
process.Start();
To get the PID of the process that got run by the code, you can use Process.ID property:
process.Id;
if you want to read the output, you can add this code:
string output = process.StandardOutput.ReadToEnd();
To get Output, startInfo.RedirectStandardOutput should be True.

Process.Start cannot run batch file properly

I am trying to run a batch file using C#
The batch file for the test purposes contains
msg * Test
It works if I run it manually.
Then I use the following code to run this .bat file
filePath = full path to batch file
var startInfo = new ProcessStartInfo
{
Arguments = "/C \"" + filePath + "\"",
FileName = "cmd.exe",
UseShellExecute = true
};
Process p = Process.Start(startInfo);
and it does not work ->
cannot find msg
What I am doing wrong?
P.S. the batch file should not be changed.
Try this way:
batchfile:
set "msg=%SystemRoot%\System32\msg.exe"
if not exist "%msg%" set "msg=%SystemRoot%\Sysnative\msg.exe"
"%msg%" * Hello
code:
string sFile = <full path to batch file>;
Process.Start("cmd.exe", "/c " + sFile);
Probably need some authorization, you may try the following code:
static void ExecuteCommand(string command)
{
int exitCode;
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
processInfo.CreateNoWindow = true;
processInfo.Domain = "domain"; // Your own domain
processInfo.UserName = "userName"; // Your own user name
System.Security.SecureString s = new System.Security.SecureString();
s.AppendChar('p'); // Your own password
s.AppendChar('a');
s.AppendChar('s');
s.AppendChar('s');
s.AppendChar('w');
s.AppendChar('o');
s.AppendChar('r');
s.AppendChar('d');
processInfo.Password = s;
processInfo.UseShellExecute = false;
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
// Warning: This approach can lead to deadlocks, see Edit #2
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
exitCode = process.ExitCode;
Console.WriteLine("output>>" + (String.IsNullOrEmpty(output) ? "(none)" :
output));
Console.WriteLine("error>>" + (String.IsNullOrEmpty(error) ? "(none)" :
error));
Console.WriteLine("ExitCode: " + exitCode.ToString(), "ExecuteCommand");
process.Close();
}
static void Main()
{
ExecuteCommand(#"C:\displayMsg.bat");
}
You may check your domain in Control Panel >> User Account >> Manage User Accounts
Source of reference: source
The problem is the location of the file (msg.exe) in the different OS versions (32bit/64bit)
I suppose it helps How can I execute msg.exe by C# in windows?
Edited:
It works fine -
class Program
{
static void Main(string[] args)
{
int ExitCode;
try
{
var returnedMsgPath = string.Empty;
if (LocateMsgExe(out returnedMsgPath))
{
var startInfo = new ProcessStartInfo()
{
FileName = returnedMsgPath,
Arguments = #"* /v Hello",
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true
};
var p = Process.Start(startInfo);
p.WaitForExit();
// *** Read the streams ***
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
ExitCode = p.ExitCode;
MessageBox.Show("output >>" + (String.IsNullOrEmpty(output) ? "(none)" : output));
MessageBox.Show("error>>" + (String.IsNullOrEmpty(error) ? "(none)" : error));
MessageBox.Show("ExitCode: " + ExitCode.ToString(), "ExecuteCommand");
p.Close();
}
else
{
MessageBox.Show("Not found");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public static bool LocateMsgExe(out string returnedMsgPath)
{
returnedMsgPath = null;
string[] msgPaths = new string[] { Environment.ExpandEnvironmentVariables(#"%windir%\system32\msg.exe"),
Environment.ExpandEnvironmentVariables(#"%windir%\sysnative\msg.exe") };
foreach (string msgPath in msgPaths)
{
if (File.Exists(msgPath))
{
returnedMsgPath = msgPath;
return true;
}
}
return false;
}
}

.Net Core open external terminal

I'm migrating batch script to .Net core and I'm trying to open another terminal from current terminal and run a command (I don't need stderr o stout).
With batch only needs this command: start cmd /K gulp. I'm trying to do the same with .Net core but only found the way to run the command inside current terminal.
private static string Run (){
var result = "";
try
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.Arguments = $"/c \"gulp browserSync\"";
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
using (Process process = Process.Start(startInfo))
{
result = process.StandardError.ReadToEnd();
process.WaitForExit();
}
}
catch (Exception Ex)
{
Console.WriteLine(Ex.Message);
Console.ReadKey();
}
return result;
}
I'm trying changing this properties in order to open in another terminal:
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardError = false;
startInfo.UseShellExecute = true;
But make an exception:
UseShellExecute must always be set to false.
From the MSDN docs:
UseShellExecute must be false if the UserName property is not null or an empty string, or an InvalidOperationException will be thrown when the Process.Start(ProcessStartInfo) method is called.
startInfo.UserName = null;
edit: I'm not sure why you have to pass in the arguments, but if all you want is a new CMD window try this:
try
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
WorkingDirectory = #"C:/users/replace/where_gulp_is_located",
Arguments = #"/c gulp", // add /K if its required, I don't know if its for gulp for to open a new cmd window
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process proc = new Process();
proc.StartInfo = startInfo;
proc.Start();
if (showOut)
{ ///code }
}catch(Exception ex)
{
Console.WriteLine(ex);
}
You wont need startInfo.UserName in this case because you are specifying a working directory.
Thanks to #bender-bending answer I found a way to solve it. Due security limitations need user/password credentials in order to autorice current terminal to open a new one.
WorkingDirectory, user, password and domain are required.
Create no window, redirect output and redirect error must be false, in order to see command result in new window.
public static void Sample(){
try
{
Console.Write("Password: ");
StringBuilder password = new StringBuilder();
while (true)
{
var key = System.Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter) break;
password.Append(key.KeyChar);
}
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
WorkingDirectory = "C:/path_to/Gulp",
Arguments = $"/c \"gulp browserSync\"",
UseShellExecute = false,
RedirectStandardOutput = false,
RedirectStandardError = false,
UserName = Machine.User(),
PasswordInClearText = password.ToString(),
Domain = Machine.Domain(),
CreateNoWindow = false
};
Process proc = new Process();
proc.StartInfo = startInfo;
proc.Start();
//proc.WaitForExit();
} catch (Exception ex)
{
System.Console.WriteLine(ex);
System.Console.ReadKey();
}
}
.Net Core doesn't have a method to obtain user and domain. We can use this class to get this values from environment variables.
public static class Machine
{
public static string User(){
return Environment.GetEnvironmentVariable("USERNAME") ?? Environment.GetEnvironmentVariable("USER");
}
public static string Domain(){
return Environment.GetEnvironmentVariable("USERDOMAIN") ?? Environment.GetEnvironmentVariable("HOSTNAME");
}
}
Hope it helps!

How to send multiple files to the printer with one Process call

I need to print multiple PDF-files from the hard-drive. I have found this beautiful solution of how to send a file to the printer. The problem with this solution is that if you want to print multiple files you have to wait for each file for the process to finish.
in the command shell it is possible to use the same command with multiple filenames: print /D:printerName file1.pdf file2.pdf
and one call would print them all.
unfortunately simply just to put all the filenames into the ProcessStartInfo doesn't work
string filenames = #"file1.pdf file2.pdf file3.pdf"
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "print";
info.FileName = filenames;
neither does it to put the filenames as Arguments of the Process
info.Arguments = filename;
I always get the error: Cannot find the file!
How can I print a multitude of files with one process call?
Here is an example of how I use it now:
public void printWithPrinter(string filename, string printerName)
{
var procInfo = new ProcessStartInfo();
// the file name is a string of multiple filenames separated by space
procInfo.FileName = filename;
procInfo.Verb = "printto";
procInfo.WindowStyle = ProcessWindowStyle.Hidden;
procInfo.CreateNoWindow = true;
// select the printer
procInfo.Arguments = "\"" + printerName + "\"";
// doesn't work
//procInfo.Arguments = "\"" + printerName + "\"" + " " + filename;
Process p = new Process();
p.StartInfo = procInfo;
p.Start();
p.WaitForInputIdle();
//Thread.Sleep(3000;)
if (!p.CloseMainWindow()) p.Kill();
}
Following should work:
public void PrintFiles(string printerName, params string[] fileNames)
{
var files = String.Join(" ", fileNames);
var command = String.Format("/C print /D:{0} {1}", printerName, files);
var process = new Process();
var startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe",
Arguments = command
};
process.StartInfo = startInfo;
process.Start();
}
//CALL
PrintFiles("YourPrinterName", "file1.pdf", "file2.pdf", "file3.pdf");
It's not necessarily a simple solution, but you could merge the pdfs first and then send then to acrobat.
For example, use PdfMerge
Example overload to your initial method:
public void printWithPrinter(string[] fileNames, string printerName)
{
var fileStreams = fileNames
.Select(fileName => (Stream)File.OpenRead(fileName)).ToList();
var bundleFileName = Path.GetTempPath();
try
{
try
{
var bundleBytes = new PdfMerge.PdfMerge().MergeFiles(fileStreams);
using (var bundleStream = File.OpenWrite(bundleFileName))
{
bundleStream.Write(bundleBytes, 0, bundleBytes.Length);
}
}
finally
{
fileStreams.ForEach(s => s.Dispose());
}
printWithPrinter(bundleFileName, printerName);
}
finally
{
if (File.Exists(bundleFileName))
File.Delete(bundleFileName);
}
}

Running batch file containing NuGet commands from C#

I have a batch file containing the following commands:
cd C:\myfolder
NuGet Update -self
NuGet pack mypackage.nuspec
myfolder contains mypackage.nuspec and NuGet.exe. I try to run this command with C# using the following function:
private static int ExecuteCommand(string path)
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo(path);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
ProcessInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
ProcessInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true";
// *** Redirect the output ***
ProcessInfo.RedirectStandardError = true;
ProcessInfo.RedirectStandardOutput = true;
Process = Process.Start(ProcessInfo);
Process.WaitForExit();
// *** Read the streams ***
string output = Process.StandardOutput.ReadToEnd();
string error = Process.StandardError.ReadToEnd();
int ExitCode = Process.ExitCode;
Process.Close();
return ExitCode;
}
However, my commands are not executed. What is causing this behavior and what is the solution? Those strings will probably be used in the future, I'll update my question then (just to prevent chriticism :)).
This is the final version of the function:
private static ShellCommandReturn ExecuteCommand(string path)
{
ProcessStartInfo processInfo;
Process process;
processInfo = new ProcessStartInfo(path);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
processInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true";
// *** Redirect the output ***
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;
process = Process.Start(processInfo);
process.WaitForExit();
// *** Read the streams ***
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();
int exitCode = process.ExitCode;
process.Close();
return new ShellCommandReturn { Error = error, ExitCode = exitCode, Output = output };
}
ShellCommandReturn is a simple custom class with a few data members where error, output and exit code of a shell command are stored.
Thanks.
EDIT: After a certain amount of collaboration :)
The problem is that this is executing in the context of a web application, which doesn't have the same environment variables set.
Apparently setting:
startInfo.EnvironmentVariables["EnableNuGetPackageRestore"] = "true"
(using the naming of my final code below) fixes the problem.
Old answer (still worth reading)
Look at this code:
ProcessInfo = new ProcessStartInfo(path);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = true;
ProcessInfo.WorkingDirectory = new System.IO.FileInfo(path).DirectoryName;
Process = Process.Start(path);
You're creating a ProcessStartInfo, but then completely ignoring it. You should be passing it into Process.Start. You should also rename your variables. Conventionally local variables start with lower case in C#. Additionally, it's a good idea to initialize variables at the point of first use, where possible. Oh, and import namespaces so you don't fully qualified names such as System.IO.FileInfo in your code. Finally, object initializers are useful for classes like ProcessStartInfo:
var startInfo = new ProcessStartInfo(path) {
CreateNoWindow = false,
UseShellExecute = true,
WorkingDirectory = new FileInfo(path).DirectoryName;
};
var process = Process.Start(startInfo);

Categories

Resources