I'm trying to fix an issue with the owner on a folder. I am using SetACL. I can use cmd and make the arguments work, but when I try adding it to a program...it doesn't work. I've set a break point to ensure the argument is passed right and it was. Any help is welcome.
Process p = new Process();
if (Wow.Is64BitOperatingSystem == true)
{
p.StartInfo.FileName = "SetACLx64.exe";
}
else
{
p.StartInfo.FileName = "SetACLx86.exe";
}
string command = #" -on """ + path +
#""" -ot file -actn setprot -op ""dacl:np;sacl:nc"" -actn setowner -ownr ""n:" + account + #";"" -rec cont_obj";
p.StartInfo.Arguments = command;
p.Start();
I have got this to work in the same program for a registry issue without trouble. Just can't get this example to work. Folder I'm try to set is the %temp% folder.
If it is running as admin as Sanjeevakumar asked then
Try removing the first space in your command variable. The Arguments parameter does not require that you provide an initial space for the arguments. May be that causes the problem.
Also try tapping into the error data of your process by adding the following lines before calling the Start() method.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardError = true;
p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
And then define the event handler.
private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs e)
{
//using the DataReceivedEventArgs see if there is an error.
//If it comes there there is most likely an error.
}
So your code does not work when path is "%temp%"? In that case the solution is simple: variable expansion is not done by SetACL but the command shell before SetACL is even started. If you start SetACL directly without invoking cmd.exe then variable expansion never takes place.
You have two options:
Expand "%temp%" in C# code with Environment.GetEnvironmentVariable.
Call SetACL via cmd like this: cmd /c SetACL -on %temp% -ot file ...
Related
What I intend to do is build an application which, among other things, will have a command line embedded in it just like some IDEs do (something I find extremely useful).
This is the code that I have so far, do note that it's a Winforms project:
public partial class Form1 : Form
{
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.CreateNoWindow = true;
p.StartInfo = info;
p.Start();
}
private void button1_Click(object sender, EventArgs e) {
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
textBox2.Text = p.StandardOutput.ReadToEnd();
textBox3.Text = p.StandardError.ReadToEnd();
p.WaitForExit();
}
}
}
As you can see there are 3 textboxes and one button:
textbox1 is for entering the command
textbox2 is for stdout
textbox3 is for stderr
On to my problem:
I can only input one command because after executing it, my CMD window vanishes. I know it dies off because I've set info.CreateNoWindow = false; and it indeed vanishes and if I try to enter another command I get an exception.
How would I go on about keeping my CMD window 'alive' so that I can use it as much as I please? In short I want to truly mimic CMD behavior.
Feel free to ask for more information if something is not clear.
Extra info/What I tried:
I've tried adding info.Attributes = "/K"; since I know that /K should keep the CMD alive. I've also read that p.WaitForExit(); should keep the CMD alive, but from what I figured this is only for the purpose of reading the output. Needless to say, I do not need that since I'm already redirecting its output. Neither of these solutions work but it is entirely possible that I'm using them the wrong way.
I need that process alive so I can easily navigate using cd and executing a sequence of commands when needed, such as when accessing ftp or mysql. I know I can work around these two examples with parameters, but not for every application. In short, spawning a new process every time is not something I want. I want that CMD interface to be up at all times.
The cmd process dies after
using(StreamWriter sw = p.StandardInput) {
if(sw.BaseStream.CanWrite) {
sw.WriteLine(textBox1.Text);
}
}
But I cannot pinpoint why.
What CMD console provides is an interface to execute predefined functions (in System32 or in %PATH%). Process class also have same capabilities ,what you can do is as the user enters command text and presses return key in textbox2 (which can be multi-lined, black-background, white text) you can pass the command text to Process p = new Process();and append the result so it looks like single cmd session. Now before passing the whole command text we need to separate arguments (if any) which is text appearing after first space. Example:
SHUTDOWN /S /T 10
where Shutdown will be filename and /S /T 10 will be arguments.
Before executing set default directory of ProcessStartInfo:-
_processStartInfo.WorkingDirectory = #"%Path%";
Otherwise default will be System32 folder.
C# WinForms application (.NET 4)
Directory path is selected from a combo(dropdown) menu and it is then included in the following command line statement.
for %f in ("< path >\*.ocx" "< path >\*.dll") do regsvr32 /s "%f"
where < path > is the directory path.
This executes fine. I would like to retrieve the registration successful messages (or errors) without the user having to click OK a thousand times to the popup / message box that displays. Obviously the silent (/s) switch gets rid of the popups.
What would be the best way to retrieve the results without the user seeing anything on their screen (besides the application itself)?
This is what I have right now,
public void reg_in_source_2()
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = #"C:\Windows\System32\cmd.exe";
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;
Process cmdProcess = new Process();
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.OutputDataReceived += cmd_DataReceived;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
cmdProcess.StandardInput.WriteLine(#"for %%f in (""" + reference.source_folder + #"\*.ocx"" " + reference.source_folder + #"\*.dll"") do regsvr32 ""%%f""");
cmdProcess.StandardInput.WriteLine("exit");
cmdProcess.WaitForExit();
}
public void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
reference.cmd_replies.Add(e.Data);
}
public void cmd_Error(object sender, DataReceivedEventArgs e)
{
reference.cmd_replies_errors.Add(e.Data);
}
Instead of trying to write out a batch script to a cmd process, use Directory.GetFiles("c:\\somepath\\", "*.dll;*.ocx") to get the files you want to register - then use process.start to start regsvr32 processes (with the /silent argument) and check the return code to know if you were successful or not.
If you try and do it in the script, you'll only get the return code of the cmd process, not of the regsvr32 processes which is what you're interested in.
Please return the exit code from the console application by setting the Environment.Exit(code).
You can set the exit code as linked in this stackoverflow answer
The default value is 0 (zero), which indicates that the process completed successfully.
Use a non-zero number to indicate an error. In your application, you can define your own error codes in an enumeration, and return the appropriate error code based on the scenario
All the error code can have status messages mapped to them, these messages can then be logged.
Here is my code:
try
{
ProcessStartInfo procStartInfo = new ProcessStartInfo(
"cmd.exe",
"/c " + command);
procStartInfo.UseShellExecute = true;
procStartInfo.CreateNoWindow = true;
procStartInfo.Verb = "runas";
procStartInfo.Arguments = "/env /user:" + "Administrator" + " cmd" + command;
///command contains the command to be executed in cmd
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I want to keep
procStartInfo.UseShellExecute = true
procStartInfo.RedirectStandardInput = false;
Is it possible to execute the command without using process.standardinput?
I try to execute command I've passed in argument but the command does not executes.
As #mtijn said you've got a lot going on that you're also overriding later. You also need to make sure that you're escaping things correctly.
Let's say that you want to run the following command elevated:
dir c:\
First, if you just ran this command through Process.Start() a window would pop open and close right away because there's nothing to keep the window open. It processes the command and exits. To keep the window open we can wrap the command in separate command window and use the /K switch to keep it running:
cmd /K "dir c:\"
To run that command elevated we can use runas.exe just as you were except that we need to escape things a little more. Per the help docs (runas /?) any quotes in the command that we pass to runas need to be escaped with a backslash. Unfortunately doing that with the above command gives us a double backslash that confused the cmd parser so that needs to be escaped, too. So the above command will end up being:
cmd /K \"dir c:\\\"
Finally, using the syntax that you provided we can wrap everything up into a runas command and enclose our above command in a further set of quotes:
runas /env /user:Administrator "cmd /K \"dir c:\\\""
Run the above command from a command prompt to make sure that its working as expected.
Given all that the final code becomes easier to assemble:
//Assuming that we want to run the following command:
//dir c:\
//The command that we want to run
string subCommand = #"dir";
//The arguments to the command that we want to run
string subCommandArgs = #"c:\";
//I am wrapping everything in a CMD /K command so that I can see the output and so that it stays up after executing
//Note: arguments in the sub command need to have their backslashes escaped which is taken care of below
string subCommandFinal = #"cmd /K \""" + subCommand.Replace(#"\", #"\\") + " " + subCommandArgs.Replace(#"\", #"\\") + #"\""";
//Run the runas command directly
ProcessStartInfo procStartInfo = new ProcessStartInfo("runas.exe");
procStartInfo.UseShellExecute = true;
procStartInfo.CreateNoWindow = true;
//Create our arguments
string finalArgs = #"/env /user:Administrator """ + subCommandFinal + #"""";
procStartInfo.Arguments = finalArgs;
//command contains the command to be executed in cmd
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
{
proc.StartInfo = procStartInfo;
proc.Start();
}
why are you initializing the process object with arguments and then later on override those Arguments? and btw: the last bit where you set Arguments you concatenate 'command' right upto 'cmd', that doesn't make much sense and might be where it fails (looks like you're missing a space).
Also, you are currently using the standard command line, you might want to look into using the runas tool instead. you can also call runas from command line.
Also, why are you running 'command' from the command line? why not start it directly from Process.Start with admin privileges supplied then and there? here's a bit of pseudocode:
Process p = Process.Start(new ProcessStartInfo()
{
FileName = <your executable>,
Arguments = <any arguments>,
UserName = "Administrator",
Password = <password>,
UseShellExecute = false,
WorkingDirectory = <directory of your executable>
});
I am writing a program that needs to run a java.jar server. I need to run the process directly so I can rewrite the output to a textbox and all-in-all have complete control of it. I tried just doing it through CMD.exe, but that wouldnt work because CMD.exe would just call a new process java.exe and I wouldn't have control of it. I need to call java.exe directly so I can have the control and get the output. Can any of you tell me how to convert this command so I could create a process in C# and call it?
I need this CMD command converted:
"java -Xmx1024m -cp ./../libs/*;l2jserver.jar net.sf.l2j.gameserver.GameServer"
into
a command line I can put into the Process.Arguments so I can call Java.exe directly.
I've tried to do it... and it just won't work.
I've been looking at this for hours and hours... please someone help!
Part of the problem might be that despite what the Framework documentation says using Process doesn't always resolve things against the PATH environment variable properly. If you know the name of the folder Java is in then use the full path to Java.exe, otherwise use a function like the following:
private void LocateJava()
{
String path = Environment.GetEnvironmentVariable("path");
String[] folders = path.Split(';');
foreach (String folder in folders)
{
if (File.Exists(folder + "java.exe"))
{
this._javadir = folder;
return;
}
else if (File.Exists(folder + "\\java.exe"))
{
this._javadir = folder + "\\";
return;
}
}
}
It's somewhat hacky but it will find java.exe provided the Java Runtime is installed and it's folder is in the windows PATH variable. Make a call to this function the first time your program needs to find Java and then subsequently start Java using the following:
//Prepare the Process
ProcessStartInfo start = new ProcessStartInfo();
if (!_javadir.Equals(String.Empty)) {
start.FileName = this._javadir + "java.exe";
} else {
start.FileName = "java.exe";
}
start.Arguments = "-Xmx1024m -cp ./../libs/*;l2jserver.jar net.sf.l2j.gameserver.GameServer";
start.UseShellExecute = false;
start.RedirectStandardInput = true;
start.RedirectStandardOutput = true;
//Start the Process
Process java = new Process();
java.StartInfo = start;
java.Start();
//Read/Write to/from Standard Input and Output as required using:
java.StandardInput;
java.StandardOutput;
System.Diagnostics.Process proc0 = new System.Diagnostics.Process();
proc0.StartInfo.FileName = "cmd";
proc0.StartInfo.WorkingDirectory = Path.Combine(curpath, "snd");
proc0.StartInfo.Arguments = omgwut;
And now for some background...
string curpath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
omgwut is something like this:
copy /b a.wav + b.wav + ... + y.wav + z.wav output.wav
And nothing happens at all. So obviously something's wrong. I also tried "copy" as the executable, but that doesn't work.
Try the prefixing your arguments to cmd with /C, effectively saying cmd /C copy /b t.wav ...
According to cmd.exe /? using
/C <command>
Carries out the command specified by
string and then terminates
For your code, it might look something like
// ..
proc0.StartInfo.Arguments = "/C " + omgwut;
Notes:
A good way to test whether your command is going to work is to actually try it from a command prompt. If you try to do cmd.exe copy ... you'll see that the copy doesn't occur.
There are limits to the length of the arguments you can pass as arguments. From MSDN: "The maximum string length is 2,003 characters in .NET Framework applications and 488 characters in .NET Compact Framework applications."
You can bypass the shelling out to command by using the System.IO classes to open the files and manually concatenate them.
Try this it might help you.. Its working with my code.
System.Diagnostics.ProcessStartInfo procStartInfo =
new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();
// Display the command output.
Console.WriteLine(result);
}
catch (Exception objException)
{
// Log the exception
}
Even you can try this.. this is even better.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.EnableRaisingEvents=false;
proc.StartInfo.FileName="iexplore";
proc.StartInfo.Arguments="http://www.microsoft.com";
proc.Start();
proc.WaitForExit();
MessageBox.Show("You have just visited " + proc.StartInfo.Arguments);
Daniels cmd /c idea will work. Keep in mind there is a limit to the length of a command line probably 8k in your case see this for details.
Since you are in a .Net app anyway, File.Copy may be quite a bit easier/cleaner than this approach.