External Console Application Control - c#

I am trying to solve a difficult task, for part of which a console application was written several years ago. The source code of the console application has been lost and to rewrite it would most likely take several hundred hours. I have been able to use the console application by hacking it into the top level application using the following snippet:
solver = new Process();
solver.StartInfo.FileName = "HPTSolver.exe";
solver.StartInfo.UseShellExecute = false;
solver.StartInfo.RedirectStandardInput = true;
solver.Start();
I can then trigger the console application by providing the necessary input parameters via
solver.StandardInput.WriteLine("RunCalculation");
The result of the console application is saved out to a text file and in the top level application I currently wait for the file to be created before continuing:
while (!File.Exists("result.txt"))
Thread.Sleep(50);
So finally my question is related to this final part where I wait for the results file to be created. Although my current solution works it is clearly the most naive solution. Is there a better solution to detect the creation of the result file and allow the top level application to continue? I have already tried reading the standard output of the console application but data is only output when the application closes, so thats not an option.

Have you thought about using the FileSystemWatcher?
MSDN Link
Hope that helps
Paul

What about waiting for the console application to exit?
solver = new Process();
solver.StartInfo.FileName = "HPTSolver.exe";
solver.StartInfo.UseShellExecute = false;
solver.StartInfo.RedirectStandardInput = true;
solver.Start();
solver.WaitForExit()
Or you could grab the StdOut and watch for specific messages.

Related

Run an External Program or Batch File From a C# Xamarin Program

I want to have my C# (Xamarin) program run an EXE or batch (BAT) file. The user will be running my program, and will click on one of several buttons, some of which open Web pages and others of which run external programs. These files will be on the same computer as the one running the main program and don't need greater permissions. The overall program will be in Windows, UWP.
I already have code to pull info from the database saying "the button the user clicked references a program and it's (eg) C:\Tools\MyTool.exe". (Real path more like (C:\Users\Me\source\repos\ProductNameV2\ProductName\ProductName.UWP\Assets\EXE\whatever.exe".) I used a "demo.bat" file containing nothing but echo and pause statements, or references to a built-in Windows program like Notepad or Calc that an ordinary command prompt can recognize without an explicit path (ie. that's part of the recognized system Path). Yes, the real path to the dummy file does exist; I checked. I've also explicitly added files demo.bat and dummy.txt to my C# project.
Here's roughly what I've tried so far to actually run a batch file, or an EXE, or just to try opening a text file. Nothing works.
1)
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\demo.bat"); // Returns false.
bool check = await Launcher.CanOpenAsync(#"file:///C:\Tools\dummy.txt"); // Returns true.
await Launcher.OpenAsync(#"file:///C:\Tools\demo.bat") // Seems to do nothing; silently fails.
await Launcher.OpenAsync(#"file:///C:\Tools\dummy.txt") // Same.
2)
Process batchProcess = new Process();
batchProcess.StartInfo.FileName = #"file:///C:\Tools\demo.bat"; // Same result with notepad.exe
batchProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
batchProcess.Start();
batchProcess.WaitForExit();
// Result: "Access is denied" error during Start().
3)
var otherProcessInfo = new ProcessStartInfo(#"file:///C:\Tools\demo.bat")
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
// Result: "The system cannot find the file specified" despite it being the same path as in previous examples.
// Also tried literally using the path C:\Tools\demo.bat, without adding that to the C# project.
// One thing that slightly works is to use:
var otherProcessInfo = new ProcessStartInfo("cmd.exe", "/c echo Hello world!");
// This version opens a window and instantly closes it again. With "/c pause" instead, it opens, saying "press any key to continue".
// Chaining multiple commands with newline or semicolon characters doesn't work as a form of batch file.
So: the only tiny success I've had here is to run cmd.exe, to run a one-line command. I suppose that depending on what the batch file must do, there's some possibility of receiving a string, breaking it into lines, then running cmd.exe using method 3 to call them one at a time. Which is ugly at best.
Is there some better way to do this -- to run a batch file or an EXE from within my program?
EDIT: Yes, I did in fact look at documentation before asking. Why did I use URIs? Because of multiple errors telling me that the simple path strings ("C:\this\that") I was using were in an "Invalid URI format". Using Process.Start("notepad.exe") silently fails, doing nothing. Using a method involving System.Diagnostics.Process (found at How to run external program via a C# program? and yes I saw that before) fails with an error of "Access denied" when using my batch file reference, or silently failing (no window opens) using plain old notepad.exe. I avoided setting Process options that say hide the window.
So to rephrase: Is there a way to make my program run some EXE somewhere on the computer, or to run a batch file that has more than one command in it? What is that way?
Using the data you collected, I was able to run a batch file by doing the following:
var strPathToExeOrBat = System.IO.Path.Combine("C:\\Tools", "demo.bat");
var otherProcessInfo = new ProcessStartInfo("cmd.exe", $"/c call \"{strPathToExeOrBat\"");
var otherProcess = Process.Start(otherProcessInfo);
otherProcess.WaitForExit();
otherProcess.Close();
I also think it would be helpful to review the capabilities of the cmd.exe application.
I found this post to be helpful:
https://stackoverflow.com/questions/515309/what-does-cmd-c-mean#:~:text=%2FC%20Carries%20out%20the%20command%20specified%20by%20the%20string%20and,switches%20by%20typing%20cmd%20%2F%3F%20.
In particular the /k option will leave the window open, if you don't want it to close after running a script.
Thank you very much for your question! It really helped me find the answer to this! (at least for my situation of a .NET MAUI windows app, but MAUI is built off of Xamarin.Forms, so you shouldn't have a problem doing the same thing)
EDIT: Updated to use file path from question and string interpolation with System.IO.Path.Combine for slightly greater cross platform capability

How to invoke the "Print Pictures" application that comes with Windows 7 from .NET

I'm making a C# application that creates .JPGs. I really like the system where you can right-click on a .JPG in Windows 7 and select Print. I would like to invoke that from my C# application in order to replicate the process. How can it be done? I would rather not go through the lengthy process of creating my own dialogs when the functionality is already built into Windows 7.
No answer for ages, and I finally found it. So simple. Here it is:
Process p = new Process();
p.StartInfo.FileName = "file_name.jpg";
p.StartInfo.Verb = "Print";
p.Start();
Note that the files needs to be a .jpg otherwise it might do something completely different or, most likely, nothing at all.

Capture ALL (stdout, stderr AND CON) output of cmd executing plink with C# (std out+err ok, CON not working)

I want to open SSH connections from c# via opening a process and running plink. All output should be collected and depending on the results the program will fire actions to the ssh.
My big problem is, that i am using a couple if different scripts and i need (automated) user interaction. Therefore i have to capture ALL output data (standard output, standard error AND CONSOLE).
Looking at the following test batch should make the case more clear:
1: #ECHO OFF
2: ECHO StdOut
3: ECHO StdErr 1>&2
4: ECHO Cons>CON
The code is like:
Process process;
Process process;
process = new Process();
process.StartInfo.FileName = #"cmd.exe";
process.StartInfo.Arguments = "/c test.bat";
process.StartInfo.UseShellExecute = false;
process.StartInfo.ErrorDialog = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.Start();
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.ErrorDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
StreamWriter inputWriter = process.StandardInput;
[...]
I am able to capture lines 2+3, but not 4 (used by some programs).
I have also tried powershell (or directly plink) instead of cmd.exe as starting point, but same result.
Is there any way in c# to capture the console out as well or do you know any third party command line being able to redirect CON out to stdout or something like this?
I'm not sure that is even possible - well at least not using some plain simple API.
Please note that I have not found (although I tried) any information on the web that directly confirms that, but here is how I come to this conclusion.
The console in Windows is its own subsystem, managed by csrss.exe (and starting with Windows Vista also conhost.exe, but I digress). It has it's own set APIs (AttachConsole, WriteConsole, etc.) and you can only have one "console" per process.
CMD.EXE on the other hand is just another console mode application, that just happens to use the console and being launched it a console window. You can observe this effect, by launching a different console mode application and watch the process tree in e.g. Process Explorer: there is no CMD.EXE parent process (but rather it is Explorer or whatever you used to start it - including of course CMD.EXE).
Thus far I was trying to show the difference between "the console" and CMD.EXE, batch files, or console mode applications in general.
So when in CMD.EXE you use > CON you are actually causing the same effect as doing a write to CONOUT$ in native applications (or your typical write to /dev/console on a UNIX-like OS). There doesn't seem to be a direct equivalent for managed code, as Console.Out and Console.Error equal stdout and stderr in native applications (or 1 and 2 as file descriptors in CMD.EXE).
Having all that said, when you start a process you're only enabled to redirect it's standard output and standard error streams, but not (per se) the messages it writes to the console (or CONOUT$ handle). I guess that would be the same as trying to redirect output that a process writes to some file, which is also not (generically) possible.
You could possible achieve this using some hooking or injecting something inside the child process to grab the console output.
Being not able to easily do this, is also (one of) the reason(s) why writing a complete terminal (i.e. console window, not CMD.EXE!) replacement for Windows is not easily possible and requires some hacks or workarounds.
AFAIK know what you want (redirecting CON) is only possible by hooking/injecting which is rather complex and basically not necessary to do SSH.
For SSH you can use any number of C# libraries out there (free and commercial):
http://www.chilkatsoft.com/ssh-sftp-component.asp (commercial)
http://www.tamirgal.com/blog/page/SharpSSH.aspx (free)
http://sshnet.codeplex.com/ (free)
http://www.rebex.net/ssh-pack/default.aspx (commercial)
http://www.eldos.com/sbb/desc-ssh.php (commercial)
The main advantage of using a library is that you have much greater control over the SSH session than you could ever achieve via some redirected console AND it is much easier to implement...
UPDATE - as per comments:
To get all output from the remotely running program you need to use a library which comes with an "interactive/terminal" class for SSH - for example the one at http://www.rebex.net/ssh-pack/default.aspx comes with such a class and works really fine (not affilliated, just a happy customer), it can be used as a code-only component or as a visual control (whatever suits your needs).
Here are some CodeProject projects which do this (via native code), which AFAICT better handle redirecting all console output:
Universal Console Redirector
Redirecting an arbitrary Console's Input/Output
Also, the command freopen allows a program to redirect its streams. However, if a program does an explicit WriteConsole() I'm not sure if it is possible to redirect that.

Java and .net interoperability

I have a c# program through which i am opening cmd window as a a process. in this command window i am running a batch file. i am redirecting the output of that batch file commands to a Text File. When i run my application everything seems to be ok.
But few times, Application is giving some error like "Can't access the file. it's being used by another application" at the same time cmd window is not getting closed. If we close the cmd process through the Task Manager, then it's writing the content to the file and getting closed. Even though i closed the cmd process, still file handle is not getting released. so that i am not able to run the application next time onwards.Always it's saying Can't access the file. Only after restarting the system, it's working.
Here is my code:
Process objProcess = new Process();
ProcessStartInfo objProInfo = new ProcessStartInfo();
objProInfo.WindowStyle = ProcessWindowStyle.Maximized;
objProInfo.UseShellExecute = true;
objProInfo.FileName = "Batch file path"
objProInfo.Arguments = "Some Arguments";
if (Directory.Exists(strOutputPath) == false)
{
Directory.CreateDirectory(strOutputPath);
}
objProInfo.CreateNoWindow = false;
objProcess.StartInfo = objProInfo;
objProcess.Start();
objProcess.WaitForExit();
test.bat:
java classname argument > output.txt
Here is my question:
I am not able to trace where the problem is..
How we can see the process which holding handle on ant file.
Is there any suggestions for Java and .net interoperability
In situations like this I start up the Process Explorer ( by Sysinternals, awesome tool btw ) click Ctrl+F, and enter the name of the file. It will search across all running processes and will list the file handles to this file by the applications that have it open.
You can then either drop the handle, or kill the app - whatever you think is better )
You can try forking and attaching file descriptor from C# rather than launching a bat file.
I think the problem is because the java program is accessing the text file when the C# program is writing something on it, and hence a "file cannot access" problem.
If I were you, I would do everything in C#-- I won't use Java to read the state of the C# program. And I would access the file only after I've completed whatever the C# needs to do.
As for to see what process is locking up your file, you can use Process Explorer for this purpose.

From C#, open an arbitrary application

Related question [stackoverflow] here.
I'm trying to do the above, but I want to take the process one step further. I want to open an arbitrary file using the default editor for the file type. From that point, I want to allow my user to interact with the file as they would normally, or continue to work in my application. The extension is what happens after the user finishes editing. Is there a way I can capture a close (and ideally save) event from the external application and use that as a trigger to do something else? For my purposes, tracking the closing of the external application would do.
I can do this in the specific case. For example, I can open a Word instance from my application and track the events that interest my application. However, I want to de-couple my application from Word.I want to allow my users to use any document editor of their choice, then manage the storage of the file being edited discretely behind the scenes.
You can do this in a manner similar to the referenced question, but the syntax is slightly different:
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo =
new System.Diagnostics.ProcessStartInfo("C:\...\...\myfile.html");
process.Start();
process.WaitForExit(); // this line is the key difference
The WaitForExit() call will block until the other application is closed. You would use this code in a separate thread so that the user can keep using your application in the meantime.
Use the FileSystemWatcher class to watch for changes to the file.
EDIT: You can also handle the Exited event of the Process object to find out when the program is exited. However, note that that won't tell you of the user closes your file but doesn't exit the process. (Which is especially likely in Word).
To listen for file change, you can use the FileSystemWatcher and listen for a change in the last modified date.
You can also monitor the process and check then file when the process close.
I found this useful tip online just now. It seems to be what you are looking for. This article (link broken) has some more detail and useful, to-the-point tips on C# programming.
string filename = "instruction.txt";
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(#filename);
System.Diagnostics.Process rfp = new System.Diagnostics.Process();
rfp = System.Diagnostics.Process.Start(psi);
rfp.WaitForExit(2000);
if (rfp.HasExited)
{
System.IO.File.Delete(filename);
}
//execute other code after the program has closed
MessageBox.ShowDialog("The program is done.");

Categories