C# using Process in Linux. Problem with passing arguments - c#

My program should start a Linux program and pass arguments to it. For debugging I print FileName and Arguments to the console.
private static void StartRecording(string channelName)
{
Console.WriteLine($"Starting recording of the channel {channelName}");
if (RecordingProcesses.ContainsKey(channelName)) return;
Process recordingProcess = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
FileName = RecorderPath,
Arguments = $"--appId {AppId} --channel {channelName} --uid {RecordingUid} --channelProfile 0 " +
$"--appliteDir {AppliteDir} --channelKey {GetToken(channelName)}",
}
};
recordingProcess.Exited += delegate { OnProcessExited(channelName); };
Console.WriteLine($"Starting process. FileName = {recordingProcess.StartInfo.FileName}, Arguments = {recordingProcess.StartInfo.Arguments}");
recordingProcess.Start();
RecordingProcesses.Add(channelName, recordingProcess);
}
That programs raises an error and says that I use wrong arguments. After that I close the program and try to launch that process manualy through the terminal by copy-pasting the FileName and then Arguments from the debug message to the terminal and the program runs ok. Why does that happen? How can I start the process from my program with the same result as when I start it from the terminal?

I found the reason. It was because one of the argments contained a tilde. When running the program from terminal it was replaced by "/root". And when I used Process, it didn't replace tilde.

Related

Error when creating a process in C# and using relative path with folders

I'm writing a program in C# that has to call an external process (a jar file in this case).
If I put the jar and all the files that it uses in the same folder of my exe the call succeed, but if I place all these files in a folder (even on the same level of the exe) the arguments that I pass the the jar seem to be off.
This is a small program I used for testing:
private static async Task Main()
{
// Build the process arguments
string arguments = $"-jar {JarPath}";
// Create the process
Process process = CreateProcess(ExePath, arguments, "1", "file.json", "file");
Console.WriteLine($"Process arguments: {process.StartInfo.Arguments}");
// Then wait for it to complete (custom made extension method)
await process.WaitForExitAsync();
// Print process generated output
string standardOutput = await process.StandardOutput.ReadToEndAsync();
Console.WriteLine(standardOutput);
Console.Write("Press 'ENTER' to exit: ");
Console.ReadLine();
}
private static Process CreateProcess(string exePath, params string[] arguments)
{
string args = string.Empty;
for (int i = 0; i < arguments.Length; i++)
args += $"{arguments[i]} ";
ProcessStartInfo startInfo = new ProcessStartInfo
{
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = exePath,
Arguments = args.TrimEnd(),
RedirectStandardOutput = true
};
Process process = Process.Start(startInfo);
return process;
}
In the code above ExePath is the path of java.exe and JarPath is the path to the jar file that I have to execute.
If it is something like "jarFileName.jar" the code works fine and I have a certain standard output, but if it's something like "folder\jarFileName.jar" the output changes from the previous case and the jar actually doesn't work as intended.
Any ideas? I'm not that familiar with cdm arguments, maybe is something related to that

How can I programmatically turn off or on 'Windows Features'

When I try to update Windows features; When I update UseShellExecute to "true"; "The Process object must have the UseShellExecute property set to false in order to redirect IO streams." I get an error. When I set it to False; Unable to update. How can I do it ? Do you have any other suggestions?
static void InstallIISSetupFeature()
{
var featureNames = new List<string>() {
"IIS-WebServerRole",
"IIS-WebServer",
"IIS-CommonHttpFeatures",
"IIS-HttpErrors",
"IIS-HttpRedirect",
"IIS-ApplicationDevelopment",
"IIS-Security",
"IIS-RequestFiltering",
"IIS-NetFxExtensibility",
"IIS-NetFxExtensibility45",
"IIS-HealthAndDiagnostics",
"IIS-HttpLogging",
"IIS-LoggingLibraries",
"IIS-RequestMonitor",
"IIS-HttpTracing",
"IIS-URLAuthorization",
"IIS-IPSecurity",
"IIS-Performance",
"IIS-HttpCompressionDynamic",
"IIS-WebServerManagementTools",
"IIS-ManagementScriptingTools",
"IIS-IIS6ManagementCompatibility",
"IIS-Metabase",
"IIS-HostableWebCore","IIS-StaticContent",
"IIS-DefaultDocument",
"IIS-DirectoryBrowsing",
"IIS-WebDAV",
"IIS-WebSockets",
"IIS-ApplicationInit",
"IIS-ASPNET",
"IIS-ASPNET45",
"IIS-ASP",
"IIS-CGI",
"IIS-ISAPIExtensions",
"IIS-ISAPIFilter",
"IIS-ServerSideIncludes",
"IIS-CustomLogging",
"IIS-BasicAuthentication",
"IIS-HttpCompressionStatic",
"IIS-ManagementConsole",
"IIS-ManagementService",
"IIS-WMICompatibility",
"IIS-LegacyScripts",
"IIS-LegacySnapIn",
"IIS-FTPServer",
"IIS-FTPSvc",
"IIS-FTPExtensibility",
"IIS-CertProvider",
"IIS-WindowsAuthentication",
"IIS-DigestAuthentication",
"IIS-ClientCertificateMappingAuthentication",
"IIS-IISCertificateMappingAuthentication",
"IIS-ODBCLogging",
"NetFx4-AdvSrvs",
"NetFx4Extended-ASPNET45",
"NetFx3",
"WAS-WindowsActivationService",
"WCF-HTTP-Activation",
"WCF-HTTP-Activation45",
"WCF-MSMQ-Activation45",
"WCF-NonHTTP-Activation",
"WCF-Pipe-Activation45",
"WCF-TCP-Activation45",
"WCF-TCP-PortSharing45",
"WCF-Services45",
};
ManagementObjectSearcher obj = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
foreach (ManagementObject wmi in obj.Get())
{
string Name = wmi.GetPropertyValue("Caption").ToString();
Name = Regex.Replace(Name.ToString(), "[^A-Za-z0-9 ]", "");
if (Name.Contains("Server 2008 R2") || Name.Contains("Windows 7"))
{
featureNames.Add("IIS-ASPNET");
featureNames.Add("IIS-NetFxExtensibility");
featureNames.Add("WCF-HTTP-Activation");
featureNames.Add("WCF-MSMQ-Activation");
featureNames.Add("WCF-Pipe-Activation");
featureNames.Add("WCF-TCP-Activation");
featureNames.Add("WCF-TCP-Activation");
}
string Version = (string)wmi["Version"];
string Architecture = (string)wmi["OSArchitecture"];
}
foreach (var featureName in featureNames)
{
Run(string.Format("dism/online/Enable-Feature:{0}", featureName));
}
}
static void Run(string arguments)
{
try
{
string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");
var dism = new Process();
dism.StartInfo.WorkingDirectory = systemPath;
dism.StartInfo.Arguments = arguments;
dism.StartInfo.FileName = "dism.exe";
dism.StartInfo.Verb = "runas";
dism.StartInfo.UseShellExecute = true;
dism.StartInfo.RedirectStandardOutput = true;
dism.Start();
var result = dism.StandardOutput.ReadToEnd();
dism.WaitForExit();
}
catch (Exception ex)
{
}
}`
I tried to update the feature with dism.exe and cmd.exe, when it gave an authorization error, I used the Verb property
`
Since the use of .Verb = "RunAs" requires .UseShellExecute = true, and since the latter cannot be combined with RedirectStandardOutput = true, you cannot directly capture the elevated process' output in memory.
It seems that the system itself, by security-minded design, prevents a non-elevated process from directly capturing an elevated process' output.
The workaround is to launch the target executable (dism.exe, in your case) indirectly, via a shell, and then use the latter's redirection feature (>) to capture the target executable's output (invariably) in a file, as shown below.
string systemPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32");
// Create a temp. file to capture the elevated process' output in.
string tempOutFile = Path.GetTempFileName();
var dism = new Process();
dism.StartInfo.WorkingDirectory = systemPath;
// Use cmd.exe as the executable, and pass it a command line via /c
dism.StartInfo.FileName = "cmd.exe" ;
// Use a ">" redirection to capture the elevated process' output.
// Use "2> ..." to also capture *stderr* output.
// Append "2>&1" to capture *both* stdout and stderr in the file targeted with ">"
dism.StartInfo.Arguments =
String.Format(
"/c {0} {1} > \"{2}\"",
"dism.exe", arguments, tempOutFile
);
dism.StartInfo.Verb = "RunAs";
dism.StartInfo.UseShellExecute = true;
dism.Start();
dism.WaitForExit();
// Read the temp. file in which the output was captured...
var result = File.ReadAllText(tempOutFile);
// ... and delete it.
File.Delete(tempOutFile);
First, you can use WindowsPrincipal::IsInRole() to check if you're running elevated.
See Microsoft Learn for details.
Second, this may be one of those cases where using native PS is easier than the cmdlet approach (admittedly, still not great).
If the script is supposed to run on clients as well as server operating systems: use Get-WmiObject or Get-CimInstance to get a reference to what you're running on. ActiveDirectory also has that information (in operatingSystem attribute).
For servers use Get-WindowsFeature in ServerManager module.
For clients use Get-WindowsOptionalFeature with switch -Online in DISM module which, if you indeed need to support OSes older than 6.3.xxxx, can be copied over from a machine that has it and added to $Env:Path before C:\Windows and C:\Windows\System32.
For either platform just pass the list of features to configure.
If in a (binary) cmdlet you have to call external tools then the advantage of them is mostly gone. It may be possible to access Windows CBS using a managed API to avoid this but even then the script based approach gets more results faster, especially since you can just just put together a quick wrapper around dism.exe .

How to avoid avoid broken pipes using c# and Python?

When trying to use stdin and stdout in C# (Unity) to pipe to a Python process, I get about a dozen or so transactions and the process breaks and the error "ObjectDisposedException: The object was used after being disposed."
After trying several of the more obvious things, I'm bringing the problem here perhaps someone know just the right technique. Thanks in advance.
Here's the C# Startup code:
Process pyProcess; // <=== fixed
ProcessStartInfo pyStartInfo;
public StreamReader pyStreamReader;
public StreamWriter pyStreamWriter;
public void startPython()
{
// Create new process start info
pyStartInfo = new ProcessStartInfo(pyPath)
{
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
Arguments = pyApp + " " + pyArgs
};
pyProcess = new Process { StartInfo = pyStartInfo };
pyProcess.Start();
pyStreamReader = pyProcess.StandardOutput;
pyStreamWriter = pyProcess.StandardInput;
pyStreamWriter.WriteLine("Hello!");
string str = pyStreamReader.ReadLine();
Debug.LogFormat(str + "\n");
}
void Start()
{
if(testPython == true)
startPython();
Here is the fragment that generates data sent to python at each update...
if (controller.testPython)
{
string str, python;
str = String.Format("data to send");
pyStreamWriter.DiscardBufferedData(); #<==== fixed
pyStreamWriter.WriteLine(str);
python = pyStreamReader.ReadLine();
Debug.LogFormat("python says: " + python + "\n");
}
And here is the simplified python process that's echoing the data
while True:
cmd = input() # read a command from c#
print(cmd) # process the cmd, here we just echo it back to c#
After a little experimentation, I discovered that adding
pyStreamReader.DiscardBufferedData();
before
pyStreamWriter.WriteLine(str);
solves the main problem and this simple form of piping seems to work, at least for hundreds of transactions that I observed.
I also had to declare pyProcess outside the scope so the code so that its handle is not released. That resolved the ObjectDisposed exception.

Why does the batch file not copy files when invoked from Windows Forms app but it works from Console app?

I have a Console app and a Winforms app that do the same. The common functionality is in a class being reused by both.
The CopyRequiredFile, starts a windows batch file which uses xcopy to copy files from a network folder to a local drive. But, when called from the Windows Forms app it doesn't copy the files.
I am a novice developer trying to develop framework and some internal tools for UI automation.
Why does it work to copy the files when I invoke the functionality from the Console application, but not from the Windows Forms Application?
My Console App:
public class Program
{
private static readonly Action<string> OutputAction = s => Console.WriteLine(s);
private static readonly IProgress<string> Progress = new Progress<string>(OutputAction);
public static void Main(string[] args)
{
HelpersCopy.CreateRequiredDirectories(Progress);
HelpersCopy.CopyRequiredFiles(Progress, true);
HelpersCopy.StartHub(Progress);
HelpersCopy.StartNode(Progress);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
My Windows Forms app: Only code that is relevant to this question.
private void button1_Click(object sender, EventArgs e)
{
Action<string> outputAction = s => txtOutput.InvokeEx(t => t.Text += s + Environment.NewLine);
IProgress<string> progress = new Progress<string>(outputAction);
txtOutput.Clear();
HelpersCopy.CreateRequiredDirectories(progress);
HelpersCopy.CopyRequiredFiles(progress, true);
HelpersCopy.StartHub(progress);
HelpersCopy.StartNode(progress);
}
InvokeEx is an extension method to invoke the action if required. Help from stackoverflow!
Unfortunately, I cannot post images because I don't have the required points. So, please see the output images here: https://www.flickr.com/photos/61600076#N05/sets/72157649781440604/
Helpers Class Code Please let me know if this code is not required in the question.
public class HelpersCopy
{
public static void CopyRequiredFiles(IProgress<string> progress, bool hideWindow = false)
{
progress.Report(string.Format("Copying latest version of executables...{0}", Environment.NewLine));
var currentDirectory = Directory.GetCurrentDirectory();
ExecuteCommand(String.Format(#"{0}\Copy latest Selenium files.bat", currentDirectory), progress, hideWindow: hideWindow);
progress.Report(string.Format("\r\nLatest version of executables copied successfully{0}", Environment.NewLine));
}
private static void ExecuteCommand(string fileName, IProgress<string> progress, string command = null, bool hideWindow = true)
{
if (hideWindow)
{
var processInfo = new ProcessStartInfo(fileName, command)
{
CreateNoWindow = true,
UseShellExecute = false,
// *** Redirect the output ***
RedirectStandardError = true,
RedirectStandardOutput = true
};
var process = new Process { StartInfo = processInfo, EnableRaisingEvents = true };
process.ErrorDataReceived += (sender, args) => progress.Report(args.Data);
process.OutputDataReceived += (sender, args) => progress.Report(args.Data);
var started = process.Start();
progress.Report(string.Format("process started: {0}", started));
progress.Report(string.Format("process id: {0}", process.Id));
progress.Report(string.Format("process start info: {0} {1}", process.StartInfo.FileName, process.StartInfo.UserName));
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
int ExitCode = process.ExitCode;
progress.Report(string.Format("ExitCode: {0}{1}", ExitCode, Environment.NewLine));
process.Close();
}
else
{
var process = Process.Start(fileName, command);
if (process.HasExited)
{
throw new Exception(string.Format("Process exited. Exit code: {0}", process.ExitCode));
}
}
}
}
My batch file
#echo off
echo Deleting existing mappings...
net use z: /delete /yes
echo Mapping network drives...
net use z: \\company-filestore\Selenium /user:company-filestore\Automation Selen1um
z:
cd "Hub and Node Executables"
echo Copying latest Selenium jars...
xcopy "z:\Hub and Node Executables" "C:\Selenium\" /R /Y /S /Z
echo Finished copying latest Selenium jars...
echo All done
The issue in winforms app (although, there was never any issue in console app using the same code) was being caused by a bug in xcopy in that when you redirect its output you need to redirect its input as well. So, adding this line to the ProcessInfo object in my code, fixed the issue.
RedirectStandardInput = true
More information on the issue is here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/ab3c0cc7-83c2-4a86-9188-40588b7d1a52/processstart-of-xcopy-only-works-under-the-debugger?forum=netfxbcl
Hope this helps someone.
Have you tested placing the batch file in the same folder where the Windows Forms exe is available?
Have you tried running the windows forms app with Admin rights?
Just to knockout the possibility of insufficient permissions.
have you also verified the user context with which the code is executed and if the folder has permissions for the user context?

Why can't I get the output of a C++ program using Process in C#?

I've got a C++ program which uses wprintf_s function to print the results into the command line. But when I uses Process in C# to read the output of the program, I cannot get any words of it. However when I added a fflush(stdout) after the wprintf_s statement, I can finally read the standard output in my C# program.
The code I use to start the progress is:
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "FILENAME",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
StringCollection values = new StringCollection();
proc.OutputDataReceived += (s, args) => {
lock (values) {
values.Add(args.Data);
}
};
proc.BeginOutputReadLine();
proc.WaitForExit();
Can anybody tell me why a fflush(stdout) would work?
The output is being buffered in the C++ process and will remain so until the buffer is full or it is flushed, e.g. by calling fflush(), by closing the stream, or other OS dependant reasons.
fflush() just causes any data in the output buffer to be written to the stream.
If you don't want to explicitly call fflush(), you can consider setting unbuffered mode on the output stream by calling setbuf() with a NULL pointer for the second argument:
#include <stdio.h>
#include <unistd.h>
int main()
{
setbuf(stdout, (char *)NULL);
while (1)
{
fputs("hi there\n", stdout);
sleep(1);
}
}
Now the output will appear immediately.
Note that if stdout is a terminal, setbuf(f, NULL) is unnecessary as this is the default behaviour for terminal devices. If stdout is a pipe, then setbuf(f, NULL) will make it unbuffered.

Categories