It is possible to communicate to windows service f

2020-07-23 04:48发布

问题:

I am bit new to windows services. I have WCF service running on one machine(machine 1) and windows service running on another machine(machine 2).

I need to I need to do run a powershell script on machine 2 using the WCF service. I have no idea where to start and how to accomplish this. Further more I need to pass a message from WCF web service to Windows Service

Please advice me or provide some good example or tutorial.

EDIT
I want to run a powershell script on machine 2. This powershell script only knows by the WCF service.
In simply want to do is, pass that powershell to the Machine 2. How to do that??

回答1:

First of all, I will assume that you are taking over an existing project, as you have a little contradiction in your question: you are new to Windows services, yet you state that you have one in your system. I'll also consider that you have to maintain the existing software model and, at the same time, that you have control over both machines. So:

  • The WCF service on machine 1 provides the PS scripts
  • The Windows service on machine 2 should execute these scripts as soon as they are available and passed by the WCF service on machine 1

In order to deal with this, you might consider hosting another WCF service on machine 2: How to: Host a WCF Service in a Managed Windows Service

How this will work? You'll be able to call this second WCF service on machine 2 from the WCF service on machine 1 every time a new PS script is available. Subsequently, the WCF service on machine 2 may save the script to some repository (file, database, in memory) and call the ServiceController.ExecuteCommand Method on the Windows service. At this point, the Windows service will get the script form it's saved location and execute it. Although I have the feeling that this is a unnecessarily over-complicated software project, here is an implementation for your situation, just to answer your question that yes, it is possible.

On machine 2, install the Windows service, which contains a WCF service:

using System;
using System.Linq;
using System.ComponentModel;
using System.ServiceModel;
using System.ServiceProcess;
using System.Configuration.Install;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace Sample.Services
{
    [ServiceContract(Namespace = "http://Sample.Services")]
    public interface IScriptExecutor
    {
        [OperationContract]
        void ExecuteScript(string script);
    }

    /// <summary>
    /// The WCF service class which will pass the script to the Windows
    /// service
    /// </summary>
    public class ScriptExecutorService : IScriptExecutor
    {
        const string PATH = @"C:\test\queue.txt";
        public void ExecuteScript(string script)
        {
            File.WriteAllText(PATH, script);
            ServiceController myService = 
                new ServiceController("WCFScriptWindowsService");
            myService.ExecuteCommand((int)MyCustomCommands.ExecuteScript);

        }
    }

    public class ScriptWindowsService : ServiceBase
    {
        const string PATH = @"C:\test\queue.txt";
        const string PATH_OUT = @"C:\test\out.txt";

        public ServiceHost serviceHost = null;
        public ScriptWindowsService()
        {
            ServiceName = "WCFScriptWindowsService";
        }

        protected override void OnCustomCommand(int command)
        {
            switch (command)
            {
                case (int)MyCustomCommands.ExecuteScript:
                    // Execute the PS script
                    var ps = PowerShell.Create();
                    var runspace = RunspaceFactory.CreateRunspace();
                    ps.Runspace = runspace;
                    // read the command from a repository, 
                    // could also be a database
                    ps.AddCommand(File.ReadAllText(PATH));
                    runspace.Open();
                    var results = ps.Invoke().ToList();
                    runspace.Close();
                    foreach (var result in results)
                    {
                        // writing the results to a file
                        File.AppendAllText(PATH_OUT, 
                            String.Format("{0}\r\n", 
                                result.BaseObject.GetType()));
                    }
                    break;
                default:
                    break;
            }
        }

        public static void Main()
        {
            ServiceBase.Run(new ScriptWindowsService());
        }

        protected override void OnStart(string[] args)
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
            }
            serviceHost = 
                new ServiceHost(typeof(ScriptExecutorService));
            serviceHost.Open();
        }

        protected override void OnStop()
        {
            if (serviceHost != null)
            {
                serviceHost.Close();
                serviceHost = null;
            }
        }
    }

    // Provide the ProjectInstaller class which allows 
    // the service to be installed by the Installutil.exe tool
    [RunInstaller(true)]
    public class ProjectInstaller : Installer
    {
        private ServiceProcessInstaller process;
        private ServiceInstaller service;

        public ProjectInstaller()
        {
            process = new ServiceProcessInstaller();
            process.Account = ServiceAccount.LocalSystem;
            service = new ServiceInstaller();
            service.ServiceName = "WCFScriptWindowsService";
            Installers.Add(process);
            Installers.Add(service);
        }
    }

    /// <summary>
    /// Holds the custom commands (only one, in our case)
    /// </summary>
    public enum MyCustomCommands { ExecuteScript = 128 };
}

On machine 1, modify the WCF service so that it calls the WCF service on machine 2:

using System.ServiceModel;

namespace Sample.Services
{
    [ServiceContract(Namespace = "http://Sample.Services")]
    public interface IScriptProvider
    {
        [OperationContract]
        string GetScript();
    }
    public class ScriptProviderService : IScriptProvider
    {
        public string GetScript()
        {
            // do some processing ...
            // let's say we end up with the "Get-Service" script
            var script = "Get-Service";
            // make sure you add a reference to the ExecutorServiceReference
            // WCF service on machine 2
            var client = new WcfService1.ExecutorServiceReference
                .ScriptExecutorClient();
            client.ExecuteScript(script);
            return script;
        }
    }
}

I will stress again: you do not have to execute the PS script from the Windows service itself. By hosting the new WCF service in the Windows service, you can easily call it's methods and move the logic from the OnCustomCommand method's override to the ScriptExecutorService's ExecuteScript method.

I'll update my answer if my model assumptions were wrong.