Foo.cmd won't output lines in process (on webs

2020-01-27 06:56发布

问题:

I've a problem understanding the in's and out's of the ProcessStartInfo class in .NET. I use this class for executing .exe programs like FFmpeg with no issues whatsoever.

But when I use ProcessStartInfo to start a .cmd program like a simple foo.cmd containing only @echo Hello world it doesn't output anything.

    ProcessStartInfo oInfo = new ProcessStartInfo(@"C:\Program Files (x86)\itms\foo.cmd")
    {
        UseShellExecute = false,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        CreateNoWindow = true
    };

    using (Process p = new Process())
    {
        p.StartInfo = oInfo;
        p.OutputDataReceived += new DataReceivedEventHandler(transporter_OutputDataReceived);

        p.Start();

        p.BeginOutputReadLine();

        p.WaitForExit();
    }

private void transporter_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Response.Write(e.Data + " - line<br/>");
}

I've seen a bunch of examples, where people use cmd.exe to start the .cmd program and I've tried this, but with no success. The program just keeps loading indefinitely.

    ProcessStartInfo oInfo = new ProcessStartInfo("cmd", "/c start foo.cmd")
    {
        UseShellExecute = false,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        CreateNoWindow = true,
        WorkingDirectory = @"C:\Program Files (x86)\itms"
    };

The foo.cmd program works and outputs successfully when using a command line tool in Windows and on Mac.

Could someone please demystify this for me.

Thanks

EDIT

The code behaves correctly when executed locally. The problem arises when I execute the code on our website. Either the program isn't allowed to execute or the output is somehow disabled.

Only cmd.exe is returning output ´"cmd", "/c dir"´ is e.g. returning information about the current folder content.

Could this actually be a permission issue?

回答1:

I found the answer myself and will post a solution for anyone interested.

The source of the issue is fairly hard to debug, because the problem originated in how IIS handles users and processes.

As I thought, there was nothing wrong with the code itself.

Answer

In IIS, a website is running in a AppPool. An AppPool is assigned an user identity. The default identity is a virtual built-in account named ApplicationPoolIdentity. This user does not have the privilege to call any (as far as I know) external batch/command scripts.

Providing a username, password and domain for a administrative user when starting a new process, didn't solve anything for me - It might be that I'm just misunderstanding the whole concept.

Using <identity impersonate="true" userName="domain\user" password="pass" /> in the webconfig didn't solve anything either. This is apparently because the assigned AppPool user is still the author of all processes.

What really bugged me out, was that I could execute .exe files, but not .cmd or .bat files.

The solution for me, was to create a new user with privileges to execute batch scripts and select that user as the AppPool user in IIS.

Edit: As I have mentioned in the comments, the user I'm working with is created on an Active Directory server as this particular file server is on a network share. The user is part of the local server group IIS_IUSRS on my webserver and has read/write/execute privileges in the folder where the executable programs are stored.

Edit2: The solution works for local user accounts as well as long as the user is part of the local server group IIS_IUSRS and has read/write/execute privileges in the folder where the executable programs are stored.



回答2:

This is slightly modified code but it should give you a better idea on the class

ProcessStartInfo info = new ProcessStartInfo(); 
info.Arguments = "/C C:\Program Files (x86)\itms\foo.cmd"; 
info.WindowStyle = ProcessWindowStyle.Hidden; 
info.CreateNoWindow = true; 
info.FileName = "cmd.exe"; // or C:\Program Files (x86)\itms\foo.cmd with no info.Arguments 
info.UseShellExecute = false; 
info.RedirectStandardOutput = true; 
using (Process process = Process.Start(info)) 
{ 
    using (StreamReader reader = process.StandardOutput) 
    { 
        string result = reader.ReadToEnd(); 
        Console.WriteLine(result);
    } 
} 

This will redirect the output of the cmd window to the Console, just adjust as needed.



回答3:

you will need to use it this way

   using (Process p = Process.Start(oInfo))
    {
.....

Reason is becuase Process.Start() and Process.Star(startinfo) works slightly different

Process.Start() - Starts (or reuses) the process resource that is specified by the StartInfo property of this Process component and associates it with the component.

Return Value

Type: System.Boolean true if a process resource is started; false if no new process resource is started (for example, if an existing process is reused).

Process.Start(StartInfo) - Starts the process resource that is specified by the parameter containing process start information (for example, the file name of the process to start) and associates the resource with a new Process component.

Return Value

Type: System.Diagnostics.Process A new Process component that is associated with the process resource, or null if no process resource is started (for example, if an existing process is reused).