Calling a ruby script from c#, process not exit - c#

I try to execute a .rb file from c#. Process never exits.
var result = new Process
{
StartInfo = new ProcessStartInfo()
{
FileName = "ruby \"someRubyFile.rb\"",
UseShellExecute = false,
CreateNoWindow = true
}
};
result.Start();
result.WaitForExit();

Finally I figured it out. If I send .rb filename as an argument, everthing works fine.

Related

Issue WSL commands and read returned value from C#

I am writing a code to execute some commands against wsl, parsing and reading the returned value is important.
Project is a .net core console app 3.1
wsl2 is enabled on the system
for example, listing all the available wsl images on my local machine i am using a snippet found in an answer provided in another "kind of related" SO post.
using (var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"cmd.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
CreateNoWindow = true,
}
})
{
proc.Start();
proc.StandardInput.WriteLine("wsl --list");
System.Threading.Thread.Sleep(500);
proc.StandardInput.Flush();
proc.StandardInput.Close();
proc.WaitForExit(5000);
var c = proc.StandardOutput.ReadToEnd();
Console.WriteLine(c);
Console.ReadLine();
}
now the expected output should be
what i am getting is
if i inspect using breakpoint i get this in "var c"
Ideally i want to be able to have a list that contains the 2 dockers items inside C#, changing the wait time didn't help.
in the ProcessStartInfo you have to set
StandardOutputEncoding = Encoding.Unicode;
StandardErrorEncoding = Encoding.Unicode;
for direct call of wsl use additionally:
FileName = #"wsl.exe";
Arguments = #"-l -v";

Keeping the subprocess environment for the caller process in C#

I'd like to set up a Visual C++ toolchain to be used inside my C# application. For the toolchain it's recommended to call vcvarsall (or some subvariant). My problem is that the calling process - my application - will not get to keep the environment set up by vcvarsall. Can this somehow be achieved?
// First set up the toolchain with vcvarsall
var vcvarsallProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = vcvarsallPath,
Arguments = "x86",
UseShellExecute = false,
}
};
vcvarsallProc.Start();
vcvarsallProc.WaitForExit();
// Invoke the linker for example
var linkerProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "LINK.exe",
Arguments = " foo.obj /OUT:a.exe",
UseShellExecute = false,
}
};
linkerProc.Start();
linkerProc.WaitForExit();
// ERROR: 'LINK' is not recognized as an internal or external command
Turns out, this is pretty much impossible without a hassle.
The best one can do is chain multiple commands, which will run in the same environment. It can be done like so:
var linkerProc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/C ({vcvarsallPath} {vcvarsallArgs}) && (LINK.exe {linkArgs})",
UseShellExecute = false,
}
};
Hiding this behind a function like InvokeWithEnvironment, and it's pretty painless to use.

Output data is not full

I need some help. I have a external app (test.exe with some dll files). In cmd i have run command like this: test.exe parmeters and get a lot of data with some needed info.
I have write app which execute this external app and output is not fully as I exec it with cmd. It's just some firstly lines. I don't mind whats wrong. Please help
using(var process = new Process {
StartInfo = new ProcessStartInfo {
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = orPath,
Arguments = parmeters.ToString(),
}
}) {
process.Start();
process.WaitForExit();
string result = "";
string standard_output;
while ((standard_output = process.StandardOutput.ReadLine()) != null) {
if (standard_output.Contains("xx"))
result = standard_output.Substring(standard_output.Length - 15);
}
Without a concise-but-complete code example that reliably demonstrates the problem, it's hard to say for sure. But it doesn't surprise me that if you try to consume StandardOutput after you have already called WaitForExit(), that not all of the output has been buffered and is available.
Maybe try this instead:
process.Start();
string result = "";
string standard_output;
while ((standard_output = process.StandardOutput.ReadLine())
!= null)
{
if (standard_output.Contains("xx"))
result = standard_output.Substring(
standard_output.Length - 15);
}
Note that I've simply removed the call to WaitForExit(). Reading the StandardOutput TextReader until it returns null will have the same effect as waiting for the end of the process, assuming a normal process (i.e. where the stdout doesn't get closed until the process exits).

Set ProcessStartInfo.EnvironmentVariables when Verb="runas"

I am developing a C# application.
I need to create and pass variables to a new process and I am doing it using ProcessStartInfo.EnvironmentVariables.
The new process must run elevated so I am using Verb = "runas"
var startInfo = new ProcessStartInfo(command)
{
UseShellExecute = true,
CreateNoWindow = true,
Verb = "runas"
};
foreach (DictionaryEntry entry in enviromentVariables)
{
startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
}
The problem is that according to the msdn documentation:
You must set the UseShellExecute property to false to start the process after changing the EnvironmentVariables property. If UseShellExecute is true, an InvalidOperationException is thrown when the Start method is called.
but the runas variable requires UseShellExecute=true
Is there a way to do both: run process as elevated and also set the environment variables?
EDIT
I will try to rephrase my question...
Is there a way to pass arguments securly to another process so only the other process will be able to read the arguments.
It works but on the downside is that it also shows a second command prompt, the enviroment vars are only set in the context of the started process so the settings are not propagating to the whole box.
static void Main(string[] args)
{
var command = "cmd.exe";
var environmentVariables = new System.Collections.Hashtable();
environmentVariables.Add("some", "value");
environmentVariables.Add("someother", "value");
var filename = Path.GetTempFileName() + ".cmd";
StreamWriter sw = new StreamWriter(filename);
sw.WriteLine("#echo off");
foreach (DictionaryEntry entry in environmentVariables)
{
sw.WriteLine("set {0}={1}", entry.Key, entry.Value);
}
sw.WriteLine("start /w {0}", command);
sw.Close();
var psi = new ProcessStartInfo(filename) {
UseShellExecute = true,
Verb="runas"
};
var ps = Process.Start(psi);
ps.WaitForExit();
File.Delete(filename);
}
There's a better answer: you can still call Process.Start() with a ProcessStartInfo that has UseShellExecute = true, provided that the method you call it from has been labeled with the [STAThread] attribute.

Elevating privileges doesn't work with UseShellExecute=false

I want to start a child process (indeed the same, console app) with elevated privileges but with hidden window.
I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
UseShellExecute = true, // !
Verb = "runas",
};
var process = new Process
{
StartInfo = info
};
process.Start();
and this works:
var identity = new WindowsPrincipal(WindowsIdentity.GetCurrent());
identity.IsInRole(WindowsBuiltInRole.Administrator); // returns true
But UseShellExecute = true creates a new window and I also I can't redirect output.
So when I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false, // !
Verb = "runas"
};
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = info
};
DataReceivedEventHandler actionWrite = (sender, e) =>
{
Console.WriteLine(e.Data);
};
process.ErrorDataReceived += actionWrite;
process.OutputDataReceived += actionWrite;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
This doesn't elevate privileges and code above returns false. Why??
ProcessStartInfo.Verb will only have an effect if the process is started by ShellExecuteEx(). Which requires UseShellExecute = true. Redirecting I/O and hiding the window can only work if the process is started by CreateProcess(). Which requires UseShellExecute = false.
Well, that's why it doesn't work. Not sure if forbidding to start a hidden process that bypasses UAC was intentional. Probably. Very probably.
Check this Q+A for the manifest you need to display the UAC elevation prompt.
In my case, it was ok to get the outputs once the elevated child process is done. Here's the solution I came up. It uses a temporary file :
var output = Path.GetTempFileName();
var process = Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/c echo I'm an admin > " + output, // redirect to temp file
Verb = "runas", // UAC prompt
UseShellExecute = true,
});
process.WaitForExit();
string res = File.ReadAllText(output);
// do something with the output
File.Delete(output);
Check this answer.
This seems to provide a workaround. But I recommend to try other methods like Named Pipes when you have access to source code of the child process.

Categories