简体   繁体   中英

appcmd.exe and dnscmd.exe behaving different when returning StandardOutput

I'am developing something which should work like a hosting panel for a self deploying application. I created a method which has file name and arguments as parameters and should give the output on the panel web page when executed.

Here is my method;

private string ExecuteCmd(string sysUser, SecureString secureString, string argument, string fileName)
{
    using (Process p = new Process())
    {
        p.StartInfo.FileName = fileName;
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardOutput = true;

        p.StartInfo.UserName = sysUser;
        p.StartInfo.Password = secureString;

        p.StartInfo.Arguments = argument;
        p.Start();
        p.WaitForExit();

        StreamReader sr = p.StandardOutput;

        p.Close();

        string message = sr.ReadToEnd();

        return message;
    }
}

When I use this method to create sites on IIS (with appcmd.exe) I get all the output as I execute this executable on command promt. But when it comes to dnscmd.exe to create entries on DNS, I get nothing! StandardOutput just comes out empty. I use administrator credentials to execute these executables. By the way, I am on Windows Server 2012 Standart. I didn't test this method on Server 2008 R2 yet, but I believe the result would be the same, anyway.

It's kind of strange for me to see appcmd and dnscmd executables behave differently on the same method.

What is it I am missing here?

Thanks!

Edit: Both StandardOutput and StandardError are returning error for dnscmd.exe.

Edit2: I changed ReadLine() to ReadToEnd(). That was something I changed when I was playing around, trying things. The original code had ReadToEnd().

Edit3: Full methods with filepath and arguments. That is for IIS, which shows output with no problems;

private string ExecuteAppCmd(string sysUser, SecureString secureString)
{
    using (Process p = new Process())
    {
        p.StartInfo.FileName = @"C:\Windows\System32\inetsrv\APPCMD.EXE";
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardInput = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

        p.StartInfo.UserName = sysUser;
        p.StartInfo.Password = secureString;

        p.StartInfo.Arguments = " list site domain.com";
        p.Start();
        p.WaitForExit();

        StreamReader sr = p.StandardOutput;

        p.Close();

        string message = sr.ReadToEnd().Replace("\n", "<br />");

        return message;
    }
}

"appcmd list site domain.com" shows the iis site configuration for domain.com on the command promt. If the domain.com is not in iis, it shows an error. Either way, there is an output and it works fine with this code.

And this is for dnscmd. This one does the job on asp.net page, but does not show it's output with StandardOutput. However, the output is shown on command prompt.

private string ExecuteDnsCmd(string sysUser, SecureString secureString)
{
    using (Process p = new Process())
    {
        p.StartInfo.FileName = @"C:\Windows\System32\DNSCMD.EXE";
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.RedirectStandardInput = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

        p.StartInfo.UserName = sysUser;
        p.StartInfo.Password = secureString;

        p.StartInfo.Arguments = " /zoneadd domain.com /primary";
        p.Start();
        p.WaitForExit();

        StreamReader sr = p.StandardError;

        p.Close();

        string message = sr.ReadToEnd().Replace("\n", "<br />");

        return message;
    }
}

I don't have a box where I can run dnscmd.exe but because you claim everything else works, I can only imagine the handling of the error and output streams get somehow tangled up. If you try this code, the only diference is that this code handles both error and output streams on separate threads and collect their output during the run. If this works, your code should be fine as well.

        // as there will not be an input stream so don't Redirect 
        p.StartInfo.RedirectStandardInput = false;

        // use a stringbuilder to capture everything
        var sb = new StringBuilder();
        // raise events on stdout and stderr and handle them
        p.EnableRaisingEvents = true;
        p.OutputDataReceived += (sender, args) => sb.AppendFormat("Out: {0}<br />",args.Data);
        p.ErrorDataReceived  += (sender, args) => sb.AppendFormat("Err: {0}<br />",args.Data);

        p.Start();

        // start consuming
        p.BeginOutputReadLine();
        p.BeginErrorReadLine();

        // wait for process to exit
        p.WaitForExit();

        p.Close();

        // obtain out stringbuilder value
        string message = sb.ToString();

        return message;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM