Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Running Programs on a Remote Machine with Web Services

4.96/5 (8 votes)
1 Apr 2014CPOL4 min read 31.7K   720  
Run programs on remote machines without PsExec or WMI

Introduction

When I Google how to run a program on a remote machine, inevitably someone says to use either PsExec or WMI. The only problem is.... well, there are a number of problems:

  • PsExec is often flagged by anti-virus software as dangerous
  • Sys Admins do not want a tool like PsExec on servers
  • The license is often in the remote computer registry or linked to the computer name so even with UNC or a mapped drive, you end up with the demo version... or it doesn't work at all
  • In a web application, the web server needs the app installed locally or a mapped drive. Also not popular with Sys Admins and Webmasters

Not that PsExec and WMI are bad solutions; just sometimes, because of policy, security, licensing, or cost, you need to execute the remote program using C# and only C#.

The solution is to create a WCF service that calls the Process class, runs the remote program, and then the service returns any additional information you need.

Background

This article has two parts - the service and using the service. We use an example of a software installed on a remote computer. This example installs the web service as a Windows Service even though it could easily - and probably more easily - be set up as a web service in IIS. Even though it runs as a Windows Service, it is available as a URL endpoint.

In the example, we run StatTransfer from a service. StatTransfer is a software used to convert statistical data like R, SAS, SPSS, and Stata to other statistical data types, or to ODBC sources like SQL Server. Therein lies the real use case - no libraries exist and there is no way that you can parse this type of data, unless you write the equivalent of the commercial software.

The service is written with .NET 4.5. The web application is .NET 4.0. I am using Visual Studio 2012 in the example.

Using the Code

The Service

There is no magic here. Most of the code comes directly from this MSDN article. But it does help to have a working example. The following will walk you through it step-by-step:

  1. First, create a new C# Console Application (not a WCF Application) called MyDataServices.
  2. In Program.cs, you will need to add namespaces for ServiceModel, ServiceProcess, Diagnostics, IO, and Configuration (see download). You basically remove everything (class Program and static void Main(string[] args). Then you set up the interface, service, methods, and the path to your executable and any arguments. This is not all that straight-forward so the following is a bit of a code dump to show you how it is done:

IMPORTANT: The service will run on the same computer where the local executable is installed.

// Define a service contract.
  [ServiceContract(Namespace = "http://MyDataServices.foo.com")]
  public interface IDataService
  {
      [OperationContract]
      bool ProcessStatTransfer(MemoryStream inputCommands, string inputName);
  }

  // Implement the IDataService service contract in a service class.
  public class DataService : IDataService
  {

      // Implement the IDataService methods.
      public bool ProcessStatTransfer(MemoryStream inputCommands, string inputName)
      {
          try
          {
              string m_stattransfer_loc = "C:\\Program Files\\StatTransfer\\StatTransfer12-64\\st.exe"
              string m_stattransfer_file = "C:\\MyFolder\\" + inputName;

              using (FileStream m_fsfile = new FileStream(m_stattransfer_file, FileMode.Create, FileAccess.Write))
              {
                  inputCommands.WriteTo(m_fsfile);
              }

              ProcessStartInfo processInfo = new ProcessStartInfo("\"" + m_stattransfer_loc + "\"");

              processInfo.Arguments = "\"" + m_stattransfer_file + "\"";
              processInfo.UseShellExecute = false;
              processInfo.ErrorDialog = false;
              processInfo.CreateNoWindow = true;

              Process batchProcess = new Process();
              batchProcess.StartInfo = processInfo;
              batchProcess.Start();

              return true;
          }
          catch
          {
              return false;
          }
      }
  }

The service is almost ready to be called remotely, but in this example we are setting it up as a Windows Service, so you also want to add the OnStart, OnStop and code for the ProjectInstaller class which allows the service to be installed by the Installutil.exe tool . Again, see the download for the full example. See this MSDN link for how to install the service using Installutil.exe.

So after all this, you should have a WCF service, running as a Windows Service, that you can access with a URL that looks something like this:

http://myserver:8080/MyDataServices/DataService 

Using the Service

The first thing to do is create a reference to the service endpoint. Go to References; Add Service Reference and put in the URL above. Note that you may need to open port 8080 if you follow this example.

Once you have the reference, add a "using" statement...

using MyDataServiceReference; 

...then add a "client." Then, you are ready to call the remote program via the WCF service. The whole thing looks like this (service call in bold):

MyDataServiceReference.DataServiceClient m_client = new DataServiceClient();

   using (MemoryStream m_ms_stcmd = new MemoryStream())
   {
       StreamWriter m_sw = new StreamWriter(m_ms_stcmd);
       // These are commands specific to the remote .exe
       // You can pass a file named something.stcmd
       // to the .exe and it will process it.
       m_sw.Write("DBW table " + m_filename_noex);
       m_sw.WriteLine();
       m_sw.Write("copy  \"" + m_fullpath + "\" odbc");
       m_sw.WriteLine();
       m_sw.Write("quit");
       m_sw.Flush();
       m_ms_stcmd.Position = 0;

       bool m_svcresult = m_client.ProcessStatTransfer(m_ms_stcmd, m_filename_noex + ".stcmd");
   }

That is pretty much it. This example is simplified in terms of error handling, ConfigurationManager entries, security, etc. The main point is not to connect directly to the remote computer, but to create a service wrapper that runs on the remote computer and then you call the service.

History

  • 1st April, 2014: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)