I am currently working on a program, that can handle Minecraft servers. I am running my batch witch logs the server, and i now want the batch (called batch in my code) to log in my listbox called lg_log.
If it is possible, how can I do that?
I am programming in visual studio - Windows forms in c#.
Edit: This is my code:
Process batch = new Process();
string PathtoRunFile = #"\Servers\Base\start_server.bat";
string current_directory = Directory.GetCurrentDirectory();
string server_base = #"\Servers\Base";
string working_directory = current_directory + server_base;
batch.StartInfo.FileName = current_directory + PathtoRunFile;
batch.StartInfo.Arguments = "";
batch.StartInfo.WorkingDirectory = working_directory;
batch.StartInfo.UseShellExecute = true;
batch.Start();
The Process.StartInfo contains properties like RedirectStandardOutput. By setting this flag to true, you will be able to add an event handler to batch.StartInfo.OutputDataReceived and listen for any events. Somewhat like so:
Edit: You might also want to enable redirecting the ErrorOutput in order to receive error messages.
Edit: As requested, here is a fully working example. Make sure that test.bat exists.
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
public class Program {
public static void Main() {
var form = new Form {ClientSize = new Size(400, 300)};
var button = new Button {Location = new Point(0, 0), Text = "Start", Size = new Size(400, 22)};
var listBox = new ListBox {Location = new Point(0, 22), Size = new Size(400, 278)};
form.Controls.AddRange(new Control[] {button, listBox});
button.Click += (sender, eventArgs) => {
var info = new ProcessStartInfo("test.bat") {UseShellExecute = false, RedirectStandardOutput = true};
var proc = new Process {StartInfo = info, EnableRaisingEvents = true};
proc.OutputDataReceived += (obj, args) => {
if (args.Data != null) {
listBox.Items.Add(args.Data);
}
};
proc.Start();
proc.BeginOutputReadLine();
};
form.ShowDialog();
}
}
Related
I'm trying to implement a button command that launches a new WPF application the first time the user clicks the button and then (when the user clicks the button again) sends it to foreground, if it's already running. The whole thing is running on .Net v4.0
What I've tried to do is working fine, as expected, when the launched process is a normal WPF application, but it doesn't play nice if the launched WPF application has a splash screen. The problem is that SetForegroundWindow fails, because I'm unable to retrieve the correct window handle in that specific case. Can you suggest a fix or a work-around? Assume you can modify the source of both the launcher and the launched WPF.
The relevant code from the View Model of the launcher
private void ClaimRptLogic()
{
if (ClaimRptHandle != IntPtr.Zero)
{
ShowWindow(ClaimRptHandle, SW_RESTORE);
LaunchState = SetForegroundWindow(ClaimRptHandle)? "" : "can't set to foreground";
return;
}
Process rpt = new Process();
rpt.StartInfo = new ProcessStartInfo()
{
WorkingDirectory = ConfigurationManager.AppSettings["ClaimRptPath"],
FileName = ConfigurationManager.AppSettings["ClaimRptexe"]
};
rpt.Start();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler((o, e) => {
rpt.WaitForExit();
});
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, e) => {
ClaimRptHandle = IntPtr.Zero;
LaunchState = "ClaimRpt closed";
});
bg.RunWorkerAsync();
Thread.Sleep(3000);
ClaimRptHandle = rpt.MainWindowHandle;
}
Assume you can modify the source of both the launcher and the launched
WPF.
Based on this assumption, I could determine the correct handle in the Loaded event of the launched WPF application and send it back to the launcher using a Named Pipe.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var callback = new WindowInteropHelper(this).Handle;
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += (s, a) =>
{
WritePipe("at loaded evt: " + callback);
};
bg.RunWorkerAsync();
}
private void WritePipe(string line)
{
using (NamedPipeServerStream server =
new NamedPipeServerStream(Environment.UserName, PipeDirection.InOut))
{
server.WaitForConnection();
using (StreamWriter sw = new StreamWriter(server))
{
sw.WriteLine(line);
}
}
}
and read the correct window handle from the same Named Pipe in another background worker of the launcher
bg.RunWorkerAsync();
Thread.Sleep(3000);
if (rpt.HasExited)
{
return;
}
LaunchedHandle = rpt.MainWindowHandle;
BackgroundWorker bgPipe = new BackgroundWorker();
bgPipe.DoWork += new DoWorkEventHandler((o, e) => {
while (!rpt.HasExited)
{
string testHandle = ReadPipe();
if (testHandle.StartsWith("at loaded evt: "))
{
Debug.WriteLine(testHandle);
Debug.WriteLine("CallBack from Launched Process!");
var handle = testHandle.Replace("at loaded evt: ","");
LaunchedHandle = new IntPtr(int.Parse(handle));
return;
}
LaunchedHandle = rpt.MainWindowHandle;
Thread.Sleep(500);
}
Debug.WriteLine("Process exited!");
});
bgPipe.RunWorkerAsync();
CanLaunchCmd = true;
with
private string ReadPipe()
{
string line = "";
using (NamedPipeClientStream client =
new NamedPipeClientStream(".", Environment.UserName, PipeDirection.InOut))
{
client.Connect();
using (StreamReader sr = new StreamReader(client))
{
line = sr.ReadLine();
}
return line;
}
}
Of course, I'm open to different ideas.
Just another option, if you can't modify the launched WPF app, but you know the title caption of its main window, besides the process id, of course.
In that case the background search would be
LaunchedHandle = rpt.MainWindowHandle;
mainWin = rpt.MainWindowHandle;
BackgroundWorker bgTitle = new BackgroundWorker();
bgTitle.DoWork += new DoWorkEventHandler((o, e) => {
while (!rpt.HasExited)
{
LaunchedHandle = MainWindowHandle(rpt);
Thread.Sleep(500);
}
Debug.WriteLine("Process exited!");
});
bgTitle.RunWorkerAsync();
using a filter based on the process id
private IntPtr MainWindowHandle(Process rpt)
{
EnumWindowsProc ewp = new EnumWindowsProc(EvalWindow);
EnumWindows(ewp, new IntPtr(rpt.Id));
return mainWin;
}
and a callback testing the title caption (in this example it's Launched)
private bool EvalWindow(IntPtr hWnd, IntPtr lParam)
{
int procId;
GetWindowThreadProcessId(hWnd, out procId);
if (new IntPtr(procId) != lParam)
{
return true;
}
StringBuilder b = new StringBuilder(50);
GetWindowText(hWnd, b, 50);
string test = b.ToString();
if (test.Equals("Launched"))
{
mainWin = hWnd;
}
return true;
}
I am working on a C# Form tool that will help me convert all of my phone and DSLR video to HEVC, currently, i have a program that uploads the photos and videos to different directories in my home server each time i connect to the WiFi. Once a month or so, i manually convert all the videos, but thought I would automate the process.. I have the Form working perfectly for processing 1 file. but get into trouble when processing a Directory (with possible sub-directories) all at once..
Sorry, this is long, just want to be thorough. here is the button calls
private void processFile_Click(object sender, EventArgs e)
{
OpenFileDialog file = new OpenFileDialog();
file.InitialDirectory = baseMediaDirectory;
if (file.ShowDialog() == DialogResult.OK)
{
ProcessSinlgeFile(file.FileName);
}
}
(above)for one file and (below) for a directory
private void processDirectory_Click(object sender, EventArgs e)
{
FolderBrowserDialog file = new FolderBrowserDialog();
file.SelectedPath = baseMediaDirectory;
if(file.ShowDialog() == DialogResult.OK)
{
ProcessDirectoryOfFiles(file.SelectedPath);
}
}
private void ProcessDirectoryOfFiles(string selectedPath)
{
List<string> listOfFiles = GetAllFiles(selectedPath);
foreach (string s in listOfFiles)
{
ProcessSinlgeFile(s);
}
}
both ultimately call this method, to do some checks and setup
private void ProcessSinlgeFile(string fileName)
{
if (IsAcceptableMediaFile(fileName))
{
outputWindow.AppendText("File to Process: " + fileName);
processMediaFile =
new MediaFileWrapper(this.outputWindow, new MediaFile(fileName), new NReco.VideoInfo.FFProbe());
if (processMediaFile.OkToProcess)
{
int initialCRFValue = 15;
//ultrafast superfast veryfast faster fast medium slow slower veryslow placebo
string intialSpeed = "veryfast";
try {
ConvertToMPEG(processMediaFile.getFFMPEGCommand(initialCRFValue, intialSpeed), processMediaFile);
}
catch
{
// at somepoint, we'll catch a bad file size (or compression)
// then change the CRF value and/or compression speed
}
}
}
}
ultimately I get to this Method and run into trouble.
private async void ConvertToMPEG(string arguments, MediaFileWrapper processMediaFile)
{
startTime = DateTime.Now;
watch = new Stopwatch();
watch.Start();
progressBar1.Minimum = 0;
progressBar1.Maximum = processMediaFile.GetTotalMilliseconds();
// Start the child process.
p = new Process();
//Setup filename and arguments
outputWindow.AppendText("ffmpeg " + arguments);
p.StartInfo.Arguments = arguments;
p.StartInfo.FileName = "ffmpeg.exe";
p.StartInfo.UseShellExecute = false;
// Redirect the output stream of the child process.
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
// capture the date for stdout and std error
// note FFMPEG uses Stderr exclusively
p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataReceived);
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataReceived);
// Hide Console Window
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
await p.WaitForExitAsync();
}
and WaitForExitAsync is in another class because in can not be in here with a Form
public static Task WaitForExitAsync(this Process process,
CancellationToken cancellationToken = default(CancellationToken))
{
var tcs = new TaskCompletionSource<object>();
process.EnableRaisingEvents = true;
process.Exited += (sender, args) => tcs.TrySetResult(null);
if (cancellationToken != default(CancellationToken))
cancellationToken.Register(tcs.SetCanceled);
return tcs.Task;
}
however, single files work fine, when I call a directory through, it continuously starts processes for each file, trying to run them all at the same time. You can see I tried implementing this
process.WaitForExit() asynchronously
with no luck.
Is it possible to pass FFMPEG video stream to C# window? Now it opens as new process in new window, I just simply want to pass it to my own SessionWindow.
At this moment I execute ffplay like this:
public void ExecuteCommandSync(String command, String args)
{
try
{
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("\"" + command + "\"", args);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
string result = proc.StandardOutput.ReadToEnd();
Debug.WriteLine(result);
}
catch (Exception objException)
{
}
}
private void button2_Click(object sender, EventArgs e)
{
String runPlay = #"C:\FFMPEG\bin\ffplay.exe";
String Random = "udp://127.0.0.1:1234";
this.ExecuteCommandSync(runPlay, Random);
}
PS. I don't want to use Windows Media Player since I want this app to look and work like remote desktop.
Looks like I found answer.
Process ProcFFplay = new Process();
ProcFFplay.StartInfo.FileName = #"C:\FFMPEG\bin\ffplay.exe";
ProcFFplay.StartInfo.Arguments = #"-probesize 32 udp://192.168.88.228:12340";
ProcFFplay.StartInfo.CreateNoWindow = true;
ProcFFplay.StartInfo.RedirectStandardOutput = true;
ProcFFplay.StartInfo.UseShellExecute = false;
ProcFFplay.EnableRaisingEvents = true;
ProcFFplay.OutputDataReceived += (o, k) => Debug.WriteLine(k.Data ?? "NULL", "ffplay");
ProcFFplay.ErrorDataReceived += (o, k) => Debug.WriteLine(k.Data ?? "NULL", "ffplay");
ProcFFplay.Exited += (o, k) => Debug.WriteLine("Exited", "ffplay");
ProcFFplay.Start();
Thread.Sleep(4500);//this is time which you need to wait to get first frames approximately
SetParent(ProcFFplay.MainWindowHandle, this.panel1.Handle);
MoveWindow(ProcFFplay.MainWindowHandle, -5, -30, 1200, 800, true); //these parameteres may look weird but you hide top "stripe" using them.
Enjoy.
I am currently developing an aspx page that calls a winform. The issue at hand is passing the textbox variable from the web page, through the ProcessStartInfo event, to the winform textbox to retrieve an image. The viewer is from a vendor but is only applicable in a winform environment but the other information is coming from a CF page, to an href and to a nonfunctional web image viewer. Is what I am doing possible?
Aspx page code:
namespace ImageView
{
public partial class _Default : System.Web.UI.Page
{
protected void page Load(object sender, EventArgs e)
{
TextBox1.Text = Request.QueryString["DKT_ID"].ToString();
//TextBox2.Text = Request.QueryString["Name"].ToString();
//TextBox3.Text = Request.QueryString["Age"].ToString();
ProcessStartInfo psi = new ProcessStartInfo(#"C:\ImageViewer\ImageView.exe");
psi.WindowStyle = ProcessWindowStyle.Normal;
Process p = new Process();
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(MyExited);
p.StartInfo = psi;
p.Start();
}
Winform code:
//SQL section for returning images
#region "Images Query"
ImageQuery = "SELECT isn AS isn ";
ImageQuery += "FROM bc_bcc_document (NOLOCK) ";
ImageQuery += "WHERE barcode_id = ? ";
DataTable Imagetable = new DataTable();
Imagetable.Columns.Add("ISN", typeof(Int32));
DataRow Imagerows;
//fills table with Images information
OdbcCommand comd = new OdbcCommand(ImageQuery);
string conne = "Dsn=XXXX; uid=XXXXX; pwd=XXXXXX";
using (OdbcConnection connected = new OdbcConnection(conne))
{
comd.Connection = connected;
connected.Open();
comd.Parameters.AddWithValue("barcode_id", txtBarcode.Text);
OdbcDataReader readar = comd.ExecuteReader();
while (readar.Read())
{
isn = Convert.ToInt32(readar["isn"].ToString().TrimEnd());
Imagerows = Imagetable.NewRow();
Imagerows["ISN"] = isn;
}
readar.Close();
Just pass the arguments from the web page like this
var proc = new Process
{
EnableRaisingEvents = false,
StartInfo = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = path,
Arguments = Request.QueryString["DKT_ID"].ToString()
}
};
proc.Start();
and read the command line arguments in your WinForms application like this -
string singleArgument = Environment.GetCommandLineArgs()[1];
P.S - assuming that you are passing a single argument, that's why Environment.GetCommandLineArgs()[1] is used
because at [0]th position, you will get the path and [1]st position in the array would be useful to you.
Did you try to use ClickOnce? I think could be interesting, once if your client doesnt have that application installed, clickOnce will do install before start the application.
And you can pass args also.
http://msdn.microsoft.com/en-us/library/ms172242(v=vs.110).aspx
I am trying to call php-cgi.exe from a .NET program. I use RedirectStandardOutput to get the output back as a stream but the whole thing is very slow.
Do you have any idea on how I can make that faster? Any other technique?
Dim oCGI As ProcessStartInfo = New ProcessStartInfo()
oCGI.WorkingDirectory = "C:\Program Files\Application\php"
oCGI.FileName = "php-cgi.exe"
oCGI.RedirectStandardOutput = True
oCGI.RedirectStandardInput = True
oCGI.UseShellExecute = False
oCGI.CreateNoWindow = True
Dim oProcess As Process = New Process()
oProcess.StartInfo = oCGI
oProcess.Start()
oProcess.StandardOutput.ReadToEnd()
The best solution I have found is:
private void Redirect(StreamReader input, TextBox output)
{
new Thread(a =>
{
var buffer = new char[1];
while (input.Read(buffer, 0, 1) > 0)
{
output.Dispatcher.Invoke(new Action(delegate
{
output.Text += new string(buffer);
}));
};
}).Start();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
process = new Process
{
StartInfo = new ProcessStartInfo
{
CreateNoWindow = true,
FileName = "php-cgi.exe",
RedirectStandardOutput = true,
UseShellExecute = false,
WorkingDirectory = #"C:\Program Files\Application\php",
}
};
if (process.Start())
{
Redirect(process.StandardOutput, textBox1);
}
}
You can use the OutputDataReceived event to receive data as it's pumped to StdOut.
The problem is due a bad php.ini config. I had the same problem and i downloaded the Windows installer from: http://windows.php.net/download/.
After that and commenting out not needed extensions, the conversion process is alĂ Speedy Gonzales, converting 20 php per second.
You can safely use "oProcess.StandardOutput.ReadToEnd()". It's more readable and alomost as fast as using the thread solution. To use the thread solution in conjunction with a string you need to introduce an event or something.
Cheers