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
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.
mates. I'm working on an ASP.NET MVC application like Hackerrank / Leetcode etc. It provides a set of problems and the user has to solve these problems on his machine and then submit the source code. The app will support solutions in C#, Java, C++ and Pascal. Everything was nice until I realised there is no security.
Here is how my application works:
The user submits his source-code to the server (program.cs, main.cpp etc)
The server compiles the code. If an executable in created, the app thinks that "Everything is Ok and we can run this program.exe / main.exe guy".
The server runs the .exe file for N times, where N is the number of tests for each problem. Then it compares the output of the .exe file with the correct output for that specific test case.
The problem is that I can't find a way to run that code safely. The user can submit some malicious code. The server will 100% run it. What to do?
Initially I've runned the .exe this way
Process p = new Process();
p.StartInfo.FileName = (.exe file path goes here)
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
//Send the imput, get the output
p.WaitForExit();
Then I've tried to use SandBox.
AppDomain ap = AppDomain.CreateDomain("ProgramEXE",null);
string assemblyPath = (.exe location);
ap.ExecuteAssembly(assemblyPath, Imput);
AppDomain.Unload(ap);
Maybe I have to give a set of permissions, But how to do that? How to stop the program if it is malicios, How to run it in a Virtual Machine or something? Just give me a hint, please.
For example, if the untrusted code looks like this:
using System;
using System.Threading.Tasks;
using System.Diagnostics;
namespace TurnOffPcProgram
{
class Program
{
static void Main(string[] args)
{
//Code that will turn your PC off.
var psi = new ProcessStartInfo("shutdown", "/s /t 0");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi);
}
}
}
The server will run it and will turn itself off (Lol).
If you know a way how to solve this problem I will be grateful. If you got some documentation about this, send it here.
I'm looking for a way on how to restrict executable's abilities, how to say to him that "No-no, you are a bad guy, you want System.IO.Directory.Delete(#"C:\Temp", true); that is not ok. I won't run you. "
Or maybe to create a closed environment and say to him "Do whatever you want, you can't damage anything."
Please, give me some hints on how to get out of this problem, some documentation, some examples, some code, whatever you got is welcomed <3. Thanks.
Well, If you want to have full control over how the code runs, you have to build your own compiler, but if you just want to run SAFELY a piece of code - There are plenty of APIs out there who are willing to do your job, both free and paid, so you don't have to worry about security and all that stuff.
I ended up using this absolutely amazing API - https://ce.judge0.com
Strengths:
A lot of supported languages
You don't have to worry too much about security (kinda)
It returns a lot of information about the solution (stdout, time, memory etc.)
Weaknesses:
Nope.
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 launch a game server application from a .bat file currently using this...
java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui
I would like to be able to check if it is running or not from a C# .NET application. Is there any simple way to do this?
Using the Process class, you can find the java processes but you won't be able to tell what they are executing. You'd need to be able to inspect their command line at least to determine this. If all your Minecraft servers are started with the same command line arguments, you could use WMI to find. You could get the associated associated Process objects if you want to do what you need.
// find all java processes running Minecraft servers
var query =
"SELECT ProcessId "
+ "FROM Win32_Process "
+ "WHERE Name = 'java.exe' "
+ "AND CommandLine LIKE '%minecraft_server%'";
// get associated processes
List<Process> servers = null;
using (var results = new ManagementObjectSearcher(query).Get())
servers = results.Cast<ManagementObject>()
.Select(mo => Process.GetProcessById((int)(uint)mo["ProcessId"]))
.ToList();
You'll need to add a reference to the System.Management.dll library and use the System.Management namespace.
The existence of such processes is enough to know it is running. You could also determine if and when it ends by inspecting each process' properties such as HasExited or wait for it using WaitForExit().
Instead of using a .bat file to launch java, you might consider looking into the System.Diagnostics.Process from .Net. It has all sorts of goodies for dealing with processes.
If you want to be checking on the game server constantly over a stretch of time, you probably will have to use it with some sort of Thread or BackgroundWorker, but the Process class ought to lead you in the right direction.
Hope this helps.