我在理解.NET中ProcessStartInfo类的in和out时遇到了问题。 我使用这个类来执行像FFmpeg这样的.exe程序,没有任何问题。
但是,当我使用ProcessStartInfo启动.cmd程序时,就像只包含@echo Hello world
的简单foo.cmd一样,它不会输出任何内容。
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”'是例如返回有关当前文件夹内容的信息。
这实际上是一个权限问题吗?
答案 0 :(得分:14)
我自己找到了答案,并会为有兴趣的人发布解决方案。
问题的根源很难调试,因为问题源于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 并且具有读/写/执行权限存储可执行程序的文件夹。
答案 1 :(得分: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窗口的输出重定向到控制台,只需根据需要进行调整。
答案 2 :(得分:0)
您将需要以这种方式使用
using (Process p = Process.Start(oInfo))
{
.....
原因是因为Process.Start()和Process.Star(startinfo)的工作方式略有不同
Process.Start() - 启动(或重用)流程资源 由此Process组件的StartInfo属性指定 将其与组件相关联。
返回值
类型:System.Boolean如果启动了进程资源,则为true;否则为false。假如果 没有启动新的流程资源(例如,如果存在 过程被重用)。
Process.Start(StartInfo) - 启动流程资源 由包含进程启动信息的参数指定(for 例如,要启动的进程的文件名)并将其关联 具有新Process组件的资源。
返回值
类型:System.Diagnostics.Process一个新的Process组件 与进程资源关联,如果没有进程资源则为null 已启动(例如,如果重用现有流程)。
答案 3 :(得分:0)
对我有用的(而不是为应用程序池创建新用户)是切换到内置帐户“本地系统”(在Windows Server 2012 R2的IIS 8.5上)。