65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Process 类远程运行任何命令行 exe

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2011 年 12 月 19 日

MIT

6分钟阅读

viewsIcon

139854

downloadIcon

7338

基于现有的 RunRemote 项目,在远程服务器上运行命令。

引言

我最初的目标是运行一个只安装在远程服务器上的命令行进程。这个进程是一个不在 .NET 托管代码下运行的 exe 文件。

我发现 Jim Wiese(又名 Spunk)的 Code Project 文章“在远程机器上推送并运行 .NET 代码”是实现此目的最简单的方法:推送并运行 .NET

我所要做的就是确保将我用于客户端登录的相同域帐户添加到远程服务器“本地用户和组”下的管理员组中。远程服务器上不需要其他设置。

最低要求是 Windows XP Pro、Server 2003 或更高版本的 Windows Pro 或更高版本(还需要 .NET 2.0 或更高版本)。

我唯一需要添加的是一个名为“Process”的 .NET 可执行文件,用于在远程服务器上运行现有的非托管应用程序。这样做的全部原因是我需要设置一个批处理文件,该文件能够在客户端运行一些 exe 命令行应用程序,但其中一个只安装在远程服务器上。它影响了客户端使用的数据库表,但您无法在客户端运行非常有用的命令行实用程序。

因此,本文旨在为 Jim Wiese 的 RunRemote.exe 创建一个 .NET 附加实用程序。

另外,您还将学习一些掌握 .NET Process 类的技巧。

示例入门

Figure1.png

图 1

图 1 显示了一个在名为“CO007-PL-0025”的网络系统上运行 RunRemote.exe 的示例。Process.exe 被复制到 CO007-PL-0025,然后使用 RunRemote.exe 远程启动的服务执行。

Process.exe 会接受其后的命令行参数,其中第一个参数是要运行的 exe 的名称,其余参数是适用于该 exe 的参数。因此,在这种情况下,Process 会运行“cmd.exe”并带参数

/C – to run specified command and then terminate

dir – the cmd shell command to display a list of files

"C:\*.*" – [drive:][path][filename]

任何带空格的参数都必须加引号,否则引号是可选的。

显示正常的 dir 输出,最后两行由 Process.exe 输出,以显示其启动的 exe 的返回值,在命令行环境中,0 表示 OK,错误通常用 1 的返回值标记。最后是“Completed:”状态,以便在将大型批处理文件的输出重定向到日志文件时轻松跟踪发生了什么。

图 2 显示了运行 Microsoft 实用程序“srvcheck.exe”的另一个示例。此命令是从您可以从 Microsoft 网站下载的 Windows 资源工具包捆绑包中安装的。我只将其安装在远程服务器上。当我将其安装在我的服务器上时,安装程序会自动将路径添加到系统路径环境变量中。

SrvCheck.exe 碰巧接受一个必需参数:前面带 UNC "\\" 的计算机名称。在我的测试环境中,唯一可用的计算机名称是 SrvCheck.exe 正在运行的本地系统的名称。

Fugure2.png

图 2

请注意,此处不需要 cmd /C,因为 srvcheck.exe 不属于 Windows cmd shell。

srvcheck.exe 的输出是我的家庭网络工作组中共享文件夹的列表,包括帐户和权限。(对于检查 XP Home 和 Windows 7 之间那些棘手的共享非常有用……)

Process.exe 控制台应用程序

现在我将详细解释 Process.exeProcess.cs 的代码行数很少,但与 RunRemote.exe 结合使用时也非常强大。

我只引用 System.dll(版本 2.0),您需要使用这四个命名空间

using System;
using System.Diagnostics;
using System.IO;
using System.Threading;

这是源代码的其余部分

namespace ProcessCmd
{
  class Program
  {
    static void Main(string[] args)
    {
    if (args.Length < 1)
    {
      Console.WriteLine("Purpose: Use with RunRemote to shell execute any remote exe");
      Console.WriteLine(@" Usaage: RunRemote <\\server> Process <remote.exe> [arg1 arg2 ...]");
      System.Environment.ExitCode = 1;
      return;
    }
    Process p = new Process();
    p.StartInfo.FileName = args[0];
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.StartInfo.RedirectStandardError = true;
    p.StartInfo.CreateNoWindow = false;
    int nArgs = args.Length;
    if (nArgs > 1) p.StartInfo.Arguments = args[1];
    for (int i = 2; i < nArgs; ++i)
    {
       p.StartInfo.Arguments = String.Concat(p.StartInfo.Arguments," ", args[i]);
    }
    p.Start();
    StreamReader so = p.StandardOutput;
    while (!p.HasExited)
    {
        Thread.Sleep(100);
      if (!so.EndOfStream)
      {
        Console.WriteLine(so.ReadLine());
      }
    }
    Console.WriteLine(p.StandardOutput.ReadToEnd());
    Console.Write("Return value = ");
    Console.WriteLine(p.ExitCode);
    Console.Write("\n");
    if (!p.StandardError.EndOfStream)
    {
      Console.WriteLine("StdError: " + p.StandardError.ReadToEnd());
      Console.Write("\n");
    }
    if (p.ExitCode == 0)
        Console.WriteLine(String.Concat("Completed: ", 
           p.StartInfo.FileName, " ", p.StartInfo.Arguments));
    Console.Write("\n");
    System.Environment.ExitCode = p.ExitCode;
    }
  }
}

第一部分检查命令行参数是否符合要求。如果不符合,则设置退出错误代码并返回。

下一节使用 ProcessStartInfo 属性设置 System.Diagnostics.Process 类。

  • FileName – 设置要启动的应用程序(在 arg[0] 中找到)。必须是 exe,因为 UseShellExecutefalse
  • UseShellExecute – 此项设置为 false 以启用标准输出和错误流的重定向。
  • RedirectStandardOutput – 允许通过 Process 类的 StandardOutput 流重定向标准输出。
  • RedirectStandardError – 标准错误流也一样。
  • CreateNoWindowfalse 是默认值。需要允许 Ctrl+c 来终止进程。
  • Arguments – 剩余的命令行参数附加到此属性。

然后启动 FileName 指向的进程。如果需要,系统路径环境变量将用于查找 exe。

当进程运行时,从标准输出读取行并使用 Console.WriteLine() 写出。Thread.Sleep(100) 防止“忙等待”循环状态。

Process 以这种方式处理 exe 输出的原因是为了将输出发送回客户端系统上的 RunRemote。

一旦启动的进程退出,标准错误中的任何数据都会输出,并写入最终结果状态消息。

关于在远程机器上推送并运行 .NET 代码

正如您可能已经发现的,本项目严重依赖 Jim Wiese 的项目,该项目可以在这里找到:推送并运行 .NET

这是我少数不需要修改源代码就能对我有所帮助的项目之一。如果您想要源代码,可以从“推送并运行……”文章中获取。

软件防火墙

如果您的计算机不在域中,或者每台机器上都有软件防火墙,那么要使其工作可能非常困难。您必须打开 TCP 135 上的 DCOM 端口等。

以下是关于启用防火墙设置以启动远程服务的一些文章链接

在撰写本文时,我能够在两个 Windows 7(旗舰版/专业版)系统之间,在标准工作组网络设置下使其正常工作。您需要拥有每个系统上具有相同用户名和密码的管理员权限用户。

使用域用户帐户在服务器上使其正常工作非常容易。当然,您需要访问对服务器具有本地管理员权限的帐户,并且两个系统默认都会使用相同的帐户。

我没有在带有 XP Pro 的家庭网络上测试,但我认为它会工作。我认为 XP Home 不会工作。当我尝试时,admin$ 共享被阻止了。

关于项目的一点说明

我使用 .NET 2.0 构建项目的原因是为了确保它可以在大多数未升级的 Windows Server 2003 系统上运行。通常,在生产环境中,人们不能简单地升级服务器并重新启动它。升级必须进行安排和测试,以确保其他进程仍然正常工作。

我使用的 Windows Server 2003 系统根本没有安装 .NET。在这种情况下,我想要远程运行的主要任务必须在另一台服务器上运行。但幸运的是,命令行实用程序可以在内部重定向到没有安装 .NET 的第三台服务器。

© . All rights reserved.