Click here to Skip to main content
Click here to Skip to main content

Running Any Command Line exe Remotely Using the Process Class

, 23 Dec 2011
Rate this:
Please Sign up or sign in to vote.
Builds on the existing RunRemote project to run commands on a remote server.

Introduction

My original goal was to run a process from the command line that was only installed on a remote server. This process is an exe that does not run under .NET managed code.

I found The Code Project article “Push and Run .NET Code on Remote Machine” by Jim Wiese (a.k.a. Spunk) was the easiest way to do this: Push and Run .NET.

All I had to do was ensure that the same domain account (that I was using to log in at the client) was added to the Administrators group under “Local Users and Groups” on the remote server. No other setup was required on the remote server.

The minimum requirements are Windows XP Pro, Server 2003, or a newer version of Windows with Pro or better (.NET 2.0 or newer is also required).

The only thing I had to add was a .NET executable called “Process” to run existing unmanaged apps on the remote server. The whole reason for doing this is that I needed to set up a batch file that was able to run some exe command line apps on the client, but one of these was only installed on a remote server. It impacted the database tables that the client used, but you could not run the very useful command line utility on the client.

So, this article is about creating a .NET add-on utility for Jim Wiese’s RunRemote.exe.

As a side note, you will learn some techniques toward mastering the .NET Process class.

Getting Started With Examples

Figure1.png

Figure 1

Figure 1 shows an example of running RunRemote.exe on a network system named "CO007-PL-0025". Process.exe is copied to CO007-PL-0025 and then executed using the service that was started remotely by RunRemote.exe.

Process.exe takes the arguments on the command line after it, where the first argument is the name of the exe to run, and the rest of the arguments are parameters that apply to the exe. So in this case, Process runs "cmd.exe" with parameters:

/C – to run specified command and then terminate

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

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

Any argument with spaces must have quotes, otherwise quotes are optional.

The normal dir output is shown and the last two lines are output by Process.exe to show the return value of the exe it started where, in the command line environment, 0 indicates OK, and errors are usually flagged with a return value of 1. And lastly, the "Completed:" status to make it easy to track what happened if you are redirecting the output from a big batch file to a log file.

Figure 2 shows another example of running a Microsoft utility called "srvcheck.exe". This command is installed from a Windows Resource Kit Tools bundle you can download off the Microsoft website. I only installed it on the remote server. When I installed it on my server, the installer automatically added the path to the System Path environment variable.

SrvCheck.exe happens to take one required parameter: the computer name with the UNC "\\" preceding it. In my test environment, the only computer name that works is the name of the local system that SrvCheck.exe is running on.

Fugure2.png

Figure 2

Note that cmd /C is not required here because srvcheck.exe is not part of the Windows cmd shell.

The output from srvcheck.exe is the list of shared folders with accounts and privileges on my home networking workgroup. (Quite useful for examining those tricky shares between XP Home and Windows 7…)

The Proccess.exe Console Application

Now I will explain Process.exe in detail. Process.cs has very few lines of code, but it is also very powerful when used with RunRemote.exe.

I only reference the System.dll (version 2.0) and you need to use these four namespaces:

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

This is rest of the source listing:

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

The first part checks that command line parameters meet requirements. If not, exit error code is set and it returns.

The next section sets up the System.Diagnostics.Process class with the ProcessStartInfo properties.

  • FileName – Sets the application to start (found in arg[0]). Must be an exe since UseShellExecute is false.
  • UseShellExecute – This is set to false to enable redirect of standard output and error streams.
  • RedirectStandardOutput – Allows redirection of standard output through the Process class StandardOutput stream.
  • RedirectStandardError – Same for a standard error stream.
  • CreateNoWindowfalse is the default. Needed to allow Ctrl+c for killing the process.
  • Arguments – The remaining command line arguments are appended to this property.

Then the process pointed to by FileName is started. The system Path environment variable will be used to find the exe if needed.

While the process is running, lines are read from standard output and written out using Console.WriteLine(). Thread.Sleep(100) prevents a "busy waiting" loop state.

The reason Process handles the exe output this way is so the output will get sent back to RunRemote on the client system.

Once the started process exits, any data in standard error is output and the final result state messages are written.

About Push and Run .NET Code on Remote Machine

As you have probably figured out, this project relies heavily on Jim Wiese's project that can be found here: Push and Run .NET.

This is one of the few projects that I have not had to make any modifications to the source code in order for it to be useful for me. If you want the source, you can get it from the "Push and Run…" article.

Software Firewalls

If your computers are not on a domain or you have a software firewall on each machine, it can be very difficult to get this to work. You have to open up the DCOM port on TCP 135 among other things.

Here are links to a couple of articles about enabling your firewall settings in order to start the remote service:

While writing this article, I was able to get it to work between two Windows 7 (Ultimate/Pro) systems on a standard Workgroup networking setup. You need to have a user with administrator rights on each system with the same username and password.

It was very easy to get it to work on servers using domain user accounts. Of course, you need access to an account that has local administrator rights on the servers and both systems will use the same account by default.

I didn't test on a home network with XP Pro, but I think it will work. I don't think XP Home will work. The admin$ share was blocked when I tried it.

A Note About the Project

The reason I built the project with .NET 2.0 is to make sure it would work on most Windows Server 2003 systems that have not been upgraded. Usually, in a production environment, one just can't upgrade a server and restart it. Upgrades have to be scheduled and tested to ensure other processes still work.

I work with a Windows Server 2003 system that has no .NET installed at all. In this case, the main task I want to run remotely has to be run on another server. But fortunately, the command-line utility can redirect internally to the third server that has no .NET installed.

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

Dan Randolph
Software Developer (Senior) Delcan
United States United States
Dan Randolph is currently a Web Applications Developer with Delcan. Mr. Randolph has a B.S. dergee in Computer Science from the University of Wyoming. He is an active member of the Denver Visual Studio User Group. You can find him posting in the forums on [code.]msdn.microsoft.com and Code Project.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberSergio Andrés Gutiérrez Rojas24-Dec-11 21:45 
QuestionFew queries [modified] PinmvpSacha Barber22-Dec-11 22:36 
AnswerRe: Few queries PinmemberDan Randolph23-Dec-11 9:57 
AnswerRe: Few queries PinmemberDan Randolph23-Dec-11 13:47 
GeneralRe: Few queries PinmvpSacha Barber23-Dec-11 22:44 
GeneralVery nice article Pinmembersudhansu_k12322-Dec-11 18:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 23 Dec 2011
Article Copyright 2011 by Dan Randolph
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid