Retrieve results of regsvr32 execution from command line (without message boxes/popups) - c#

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.

Related

How to run R.exe without args in WPF

I create a process to run 'cmd.exe' and redirect the stdout,stderr,stdin.
It seems work good. But when I input 'R' and try to run 'R.exe', it doesn't work and show the message below.
Fatal error: you must specify '--save','--no-save' or '--vanilla'
Process _process = new Process();
ProcessStartInfo _startInfo = new ProcessStartInfo();
_startInfo.FileName = "cmd.exe";
_startInfo.Arguments = "";
_startInfo.RedirectStandartInput = true;
_startInfo.RedirectStandartOutput = true;
_startInfo.RedirectStandartError = true;
_startInfo.CreateNoWindows = true;
I expect that the process which is runing 'cmd.exe' can run R.exe after inputting 'R' without another args.
Current Output
Expected Output
I have try to input the full path to the executable. It show the same result.
But if the ProcessStartInfo is changed, it does work as I expect.
_startInfo.CreateNoWindows = false;
And I find an interesting appearance.
When CMD is running in system process, I try to redirect the output of R.exe and input "R > D:\a.txt". And the fatal error message will be alse shown.

Running cmd programmatically only returns some outputs [duplicate]

This question already has answers here:
Trying to get StandardOutput after running a console process
(2 answers)
Closed 5 years ago.
I'm running a CMD in c# programmatically and everything works fine, it will run whatever command I input but when I ask to return the output to the console, only some commands output gets returned.
For example if I write "ipconfig" as the command, it will return my ip info but if I write "/ipconfig", it will just return "/ipconfig" as appose to "/ipconfig is not recognized as an interal ..." you get the idea. I also tried it with deleting files, the file deletes just fine but if the file doesnt exist, it should output "could not find file" like it does it normal cmd. It seems it only gives me the output of a command thats runs and not one that doesnt even though I read the cmd till the end of anything it outputs. If this is the case is there anyway to get around this?
Code:
private void Form1_Load(object sender, EventArgs e)
{
string command = "/ipconfig";
string root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Console.WriteLine(Command(command, root));
}
public string Command(string command, string directory)
{
string commandOutput;
commandOutput = DateTime.Now + Environment.NewLine + command + Environment.NewLine;
ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WorkingDirectory = directory;
procStartInfo.RedirectStandardError = true;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
commandOutput += proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
return commandOutput;
}
I'm pretty sure cmd.exe will write that to standard error, not standard output. If that is the case, you'll need to use proc.StandardError.ReadToEnd() to get the error message.

Embedding a CMD terminal in a C# Winforms application

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.

Process exits when Startinfo.RedirectStandardOutput is set true

I am trying to develop a console application which performs tfs merge.Command to execute is:
tf merge $/CodeBase/ALPHA $/CodeBase/BETA /recursive /version:C4~C6
When there is a conflict in the merge operation , an error is raised first and a resolve conflict window appeas where i resolve conflicts . After i resolve conflicts , final output is displayed.
c# code is as follows for console application which performs tf merge:
Process proc = new Process();
proc.StartInfo.FileName = "tf.exe";
proc.StartInfo.Arguments = " merge $/CodeBase/ALPHA $/CodeBase/BETA /version:C4~C6";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.Start();
proc.WaitForExit();
string line1 = proc.StandardOutput.ReadLine();
string line2 = proc.StandardError.ReadToEnd();
Problem :
The process exits abrupty showing only the initial error message . Ideally it should wait until i resolve all the conflicts and the process will wait until then.
But since i am setting proc.StartInfo.RedirectStandardError = true; ,application is not waiting for resolve conflict window (not even starting it). But if i comment this line ,it is working correctly , but i am not able to read final output.
Help me please.
EDIT : 7/23
I believe i am doing asynchronous read operation (may be that could be problem). Is there a way by which i can perform synchronous read???
You have to attach eventhandlers
proc.ErrorDataReceived += (s, e) => ErrorLine(e.Data);
proc.OutputDataReceived += (s, e) => OutputLine(e.Data);
void ErrorLine(string text)
{
Console.ForegroundColor = ConsoleColor.White;
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.Error.WriteLine(text);
Console.ResetColor();
}
void OutputLine(string text)
{
Console.Error.WriteLine(text);
}
Problem is with TFS which executes with '/noprompt' if it detects a stdout redirection.
Problem was solved by setting environment variable TFS_IGNORESTDOUTREDIRECT=1.

C# command line SetACL

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 ...

Categories