I have a C# GUI wrapper that is using Process.Start() to call various third party python scripts (these scripts cannot be changed). The relevant code:
Process process = new Process();
process.StartInfo.FileName = FileParserConstants.PYTHON_PATH; // Path to python exe
process.StartInfo.Arguments = FileParserConstants.SCRIPT_PATH + sInput; // Path to scripts with input script + arguments
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.WaitForExit();
return process.StandardOutput.ReadToEnd().Trim();
This seems relatively simple, and it works when my C# project type is set to "Windows Application." However, upon switching to a "Console Application," though there is no error, the python scripts fail to function as intended.
Namely, there is a python script that accepts arguments and calls an exe to change settings given the arguments. When the python script is called from the project set as Console Application, a "OSError: [WinError 6] The handle is invalid" occurs within the exe called from within the python (this error is handled within the python scripts and logged). Again, when the script is called from the project set as Windows Application, it functions correctly.
For reasons unreleated to the issue, I am being forced to have this project be a Console Application, and am having trouble finding the documentation of the differences between the two project types and what may be causing Process to behave differently.
Related
Our test and development team has been working to implement and test a new version of our software using .NET 6 that we will be releasing early next year. For test's part, we have had to upgrade all of our C# automated testing to be cross-platform compatible. One set of tests requires starting a new process and executing a console application we have as a supporting library. Below is the following code I am using:
#if (WINDOWS)
string strCmdText = "/C \" SAT.FCCHandling -l" + password + " -i" + ip + " -xDeleteFCC\"";
#endif
#if (LINUX)
string strCmdText = "-c \" .//SAT.FCCHandling -l" + password + " -i" + ip + " -xDeleteFCC\"";
#endif[![enter image description here](https://i.stack.imgur.com/6SYCA.png)](https://i.stack.imgur.com/6SYCA.png)
using (var proc = Process.Start(new ProcessStartInfo
{
#if (WINDOWS)
FileName = "cmd.exe",
#endif
#if (LINUX)
FileName = "/bin/bash",
#endif
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
Arguments = strCmdText,
WindowStyle = ProcessWindowStyle.Hidden
}))
{
proc.WaitForExit();
if (proc.ExitCode != 0)
{ Assert.Fail("There was an issue Inserting an invalid FCC."); }
}
I have been using VSCode in Linux to debug problems that arise. When the code above is executed in Linux (specifically in Ubuntu 20.04), in the developer console it's indicating that the location for .NET is not found:
We also use TShark for packet capture in some testing. The methodology of code is used in launching TShark as a new process. I bring this up not because TShark is written in C# with .NET 6, but merely to point out that this approach for launching an application does work.
The problem, as I perceive it, is this child process being launched has no knowledge of environment variables. If I launch the application directly from the terminal in the binary directory, the application runs just fine.
I have done some digging and research online before posing this question. I haven't found anything that is exactly similar to my problem. I have tried a couple avenues based upon what I have read online, but these don't seem to be the right solution:
Assigning all variables to the StartInfo of the new process.
Assigning specifically EnvironmentVariables["TEST_ENV"] = "From parent"; to the Process.StartInfo. I saw something online that seemed to indicate that passing environment variables down to the child process was a necessity as the variables are instances.
Using the StartInfo.Verb = “runas” to elevate the privileges of the process.
I know that the solution is probably an obvious one that I'm just not seeing. I am not a Linux expert by any stretch of the imagination and have been having to learn as I go through this release. Any guidance is much appreciated and I'm happy to provide further information if necessary. Thank you.
I spent some time during the holidays researching, debugging, and testing. I was able to find a solution to my problem. Several things:
In order to know exactly what was happening to my process on launch, I attached two threads to the standard output and standard error of the process I start.
This lead me to discover that the file location was unknown to bash when calling it.
With this resolved, I discovered that I needed to execute 'chmod +x ' before executing the console application.
It's worthy of note that by trying to debug my problem from a .NET 6 console app (launching another console app) was injecting a different kind of problem into my existing problem. Using my existing NUnit testing that calls the console app was what lead me to the discovery noted above.
Lesson learned: If trying to call a .exe in Linux from your .NET 6 application by starting a process, be sure to make the file executable before you try calling it with bash or another shell.
I want to run a CmdLet (in particular Write-Output) from a Process class, but I am getting a The system cannot find the file specified. error. Which makes sense, as Write-Output is not a file:
using (Process process = new Process()) {
process.StartInfo.FileName = "Write-Output";
process.StartInfo.Arguments = "hello";
process.Start();
await process.WaitForExitAsync();
}
But, how could I use this correctly? I want to grab hello from standardoutput later and return it.
The Process class runs executables native to the OS. A commandlet is not an executable to the OS, but a concept of PowerShell ("inside" PowerShell). You have to invoke PowerShell, then make it run the commandlet you need.
But first you need to decide (you didn't say in the question) if you want to use Windows PowerShell or PowerShell (core). The Windows PowerShell is installed in "%windir%\System32\WindowsPowerShell\v1.0\powershell.exe". PowerShell (Core) is installed in various locations. On my system I have it (installed) in C:\Program Files\PowerShell\7-preview\pwsh.exe. YMMV, for more details on the different PowerShell Versions check this.
The command line won't differ, so I use pwsh.exe as an example.
process.StartInfo.FileName = "<path>\pwsh.exe";
process.StartInfo.Arguments = "-Command \"Write-Output 'Hello'\""
Redirecting/reading the output of PowerShell invoked like this does not differ from doing this for any other command. There are already lots of Q&A about this on SO (for example this one).
One more thing. For any nontrivial use, I would think about running the commandlet inside your own process. You can host powershell in your own application. Certainly more advanced, but might be worth it, if you need to run specific PowerShell commands a lot.
I am building a Windows Forms Application that uses a MATLAB back end. What is the best, possibly cleanest, way to do this? Furthermore, I need to grab the MATLAB output data and work with it in the Windows Forms program.
What I have done so far:
Using the Process class I ran a batch file that calls the MATLAB
program:
Process process = new Process();
process.StartInfo.FileName = #"----\run.bat";
process.StartInfo.WorkingDirectory = #"----";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
Executing Batch File in C#.
I am able to run the script without a problem. However, I cannot receive the output stream from MATLAB. I tried all of the solutions in the link above, but it does not work in my particular situation. I assume this has something to do with MATLAB.
The best way is probably to use the COM interface supplied by Matlab.
You'll need to add a reference to your C# project referencing the Matlab Application, then instantiate an object of type MLApp, which will allow you to call Matlab. For a simple example refer to http://www.mathworks.com/help/matlab/matlab_external/call-matlab-function-from-a-c-client.html.
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.
I have a C# Winforms application which is calling a simple powershell script using the following method:
Process process = new Process();
process.StartInfo.FileName = #"powershell.exe";
process.StartInfo.Arguments = String.Format("-noexit \"C:\\Develop\\{1}\"", scriptName);
process.Start();
The powershell script simply reads a registry key and outputs the subkeys.
$items = get-childitem -literalPath hklm:\software
foreach($item in $items)
{
Write-Host $item
}
The problem I have is that when I run the script from the C# application I get one set of results, but when I run the script standalone (from the powershell command line) I get a different set of results entirely.
The results from running from the c# app are:
HKEY_LOCAL_MACHINE\software\Adobe
HKEY_LOCAL_MACHINE\software\Business Objects
HKEY_LOCAL_MACHINE\software\Helios
HKEY_LOCAL_MACHINE\software\InstallShield
HKEY_LOCAL_MACHINE\software\Macrovision
HKEY_LOCAL_MACHINE\software\Microsoft
HKEY_LOCAL_MACHINE\software\MozillaPlugins
HKEY_LOCAL_MACHINE\software\ODBC
HKEY_LOCAL_MACHINE\software\Classes
HKEY_LOCAL_MACHINE\software\Clients
HKEY_LOCAL_MACHINE\software\Policies
HKEY_LOCAL_MACHINE\software\RegisteredApplications
PS C:\Develop\RnD\SiriusPatcher\Sirius.Patcher.UI\bin\Debug>
When run from the powershell command line I get:
PS M:\> C:\Develop\RegistryAccess.ps1
HKEY_LOCAL_MACHINE\software\ATI Technologies
HKEY_LOCAL_MACHINE\software\Classes
HKEY_LOCAL_MACHINE\software\Clients
HKEY_LOCAL_MACHINE\software\Equiniti
HKEY_LOCAL_MACHINE\software\Microsoft
HKEY_LOCAL_MACHINE\software\ODBC
HKEY_LOCAL_MACHINE\software\Policies
HKEY_LOCAL_MACHINE\software\RegisteredApplications
HKEY_LOCAL_MACHINE\software\Wow6432Node
PS M:\>
The second set of results match what I have in the registry, but the first set of results (which came from the c# app) don't.
Any help or pointers would be greatly apreciated :)
Ben
This is actually not a particularly good way to embed PowerShell within a C# api. There are APIs for that.
You can find an example of them on MSDN, but in your case the could would look something like
PowerShell.Create().AddScript("get-childitem -literalPath hklm:\software").Invoke()
You can also check out this blog post, which will show you how to dot source inside of the API and how to use this API to get at the other data streams in PowerShell.
Hope this helps
Are you running a 64bit version of Windows by chance? It could be a difference in how the two "hives" are being shown. Try forcing your C# app to compile to x86/x64 instead of "Any" in the Project properties. See if that makes any difference.
Also, your command line syntax is a little strange, see the following thread for better details, but you may want to adjust your syntax:
String cmd = "-Command "& { . \"" + scriptName + "\" }";
Process process = new Process();
process.StartInfo.FileName = #"powershell.exe";
process.StartInfo.Arguments = cmd;
process.Start();
Calling a specific PowerShell function from the command line
I did look at alternative methods for calling Powershell and came across that API.
Am I right in thinking though that they rely on a Microsoft SDK??
I'm not really a fan of dependencies on external SDKs. I work in a rather large company and ensuring that the SDK is installed on all of the developers machines would be a nightmare.
If I'm wrong in my thinking, I am open to a better way of calling Powershell. I didn't particularly like calling the script as a separate process and would like the ability to have values returned from the script.
Ben