11

我在理解 .NET 中 ProcessStartInfo 类的输入和输出时遇到了问题。我使用这个类来执行 .exe 程序,如 FFmpeg,没有任何问题。

但是当我使用 ProcessStartInfo 启动一个 .cmd 程序时,比如一个简单的 foo.cmd 只包含@echo Hello world它不会输出任何东西。

    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/>");
}

我看过很多例子,人们使用 cmd.exe 来启动 .cmd 程序,我试过这个,但没有成功。该程序只是无限期地继续加载。

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

在 Windows 和 Mac 上使用命令行工具时,foo.cmd 程序可以成功运行并输出。

有人可以为我揭开这个神秘面纱。

谢谢

编辑

代码在本地执行时行为正确。当我在我们的网站上执行代码时出现问题。要么不允许程序执行,要么以某种方式禁用输出。

只有 cmd.exe 返回输出'"cmd","/c dir"'例如返回有关当前文件夹内容的信息。

这实际上可能是权限问题吗?

4

4 回答 4

18

我自己找到了答案,并将为任何感兴趣的人发布解决方案。

问题的根源相当难以调试,因为问题源于 IIS 如何处理用户和进程。

正如我所想,代码本身没有任何问题。

回答

在 IIS 中,网站在 AppPool 中运行。AppPool 被分配了一个用户身份。默认身份是一个名为ApplicationPoolIdentity的虚拟内置帐户。该用户无权调用任何(据我所知)外部批处理/命令脚本。

在启动新进程时为管理用户提供用户名、密码和域,并没有为我解决任何问题——这可能是我误解了整个概念。

在 webconfig 中使用<identity impersonate="true" userName="domain\user" password="pass" />也没有解决任何问题。这显然是因为分配的 AppPool 用户仍然是所有进程的作者。

真正困扰我的是我可以执行 .exe 文件,但不能执行 .cmd 或 .bat 文件。

对我来说,解决方案是创建一个具有执行批处理脚本权限的新用户,并选择该用户作为 IIS 中的 AppPool 用户。

编辑:正如我在评论中提到的,我正在使用的用户是在Active Directory服务器上创建的,因为这个特定的文件服务器位于网络共享上。该用户是我的网络服务器上本地服务器组IIS_IUSRS的一部分,并且在存储可执行程序的文件夹中具有读/写/执行权限。

Edit2:该解决方案适用于本地用户帐户,只要用户是本地服务器组IIS_IUSRS的一部分并且在存储可执行程序的文件夹中具有读/写/执行权限。

于 2012-07-23T13:37:54.327 回答
1

这是稍微修改过的代码,但它应该让您对课程有更好的了解

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);
    } 
} 

这会将 cmd 窗口的输出重定向到控制台,只需根据需要进行调整即可。

于 2012-07-20T08:15:22.257 回答
1

您将需要以这种方式使用它

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

原因是因为 Process.Start() 和 Process.Star(startinfo) 工作方式略有不同

Process.Start() - 启动(或重用)由该 Process 组件的 StartInfo 属性指定的流程资源,并将其与该组件相关联。

返回值

类型:System.Boolean 如果进程资源已启动,则为 true;如果没有启动新的进程资源(例如,如果重用现有进程),则为 false。

Process.Start(StartInfo) - 启动由包含进程启动信息(例如,要启动的进程的文件名)的参数指定的进程资源,并将该资源与新的 Process 组件相关联。

返回值

类型:System.Diagnostics.Process 与流程资源关联的新流程组件,如果没有启动流程资源(例如,如果重用现有流程),则为 null。

于 2012-07-20T08:17:18.210 回答
0

对我有用(而不是为应用程序池创建新用户)是切换到内置帐户“本地系统”(在 Windows Server 2012 R2 上的 IIS 8.5 上)。

于 2020-11-21T19:32:20.713 回答