File: Configuration\RemoteHelper.cs
Project: ndp\cdf\src\WCF\Tools\WsatConfig\WsatConfig.csproj (WsatConfig)
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------
 
namespace Microsoft.Tools.ServiceModel.WsatConfig
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Management;
    using System.Threading;
    using System.IO;
    using System.Runtime.InteropServices;
 
    class RemoteHelper
    {
        // the name of the remote machine on which the commands are executed
        string machineName;
 
        // the class that represnets the WMI Win32_Process
        ManagementClass processClass;
        ManagementScope managementScope;
 
        const int autoEventTimeout = 90000;      // 90 secs
        const int delayInAutoEventTimeout = 400; // 400 milliseconds
 
        internal RemoteHelper(string machineName)
        {
            this.machineName = machineName;
 
            // establish connection
            ConnectionOptions co = new ConnectionOptions();
            co.Authentication = AuthenticationLevel.PacketPrivacy;
            co.Impersonation = ImpersonationLevel.Impersonate;
 
            // define the management scope
            managementScope = new ManagementScope("\\\\" + machineName + "\\root\\cimv2", co);
            // define the path used
            ManagementPath path = new ManagementPath("Win32_Process");
            ObjectGetOptions options = new ObjectGetOptions(new ManagementNamedValueCollection(), TimeSpan.FromSeconds(15), false);
 
            // get the object for the defined path in the defined scope
            // this object will be the object on which the InvokeMethod will be called
            processClass = new ManagementClass(managementScope, path, options);
        }
 
        const string MethodCreate = "Create";
        const string QueryProcessExitEvent = "SELECT * FROM Win32_ProcessStopTrace";
        static class InputParameters
        {
            public const string CommandLine = "CommandLine";
        }
 
        static class OutputParameters
        {
            public const string ProcessId = "ProcessID";
            public const string ReturnValue = "ReturnValue";
        }
 
        internal void ExecuteWsatProcess(string arguments)
        {
            ProcessStopTraceHandler handler;
            Utilities.Log("ExecuteWsatProcess(" + arguments + ")");
 
            try
            {
                ManagementBaseObject inParams = processClass.GetMethodParameters(MethodCreate);
                inParams[InputParameters.CommandLine] = GetDeploymentPath() + " " + arguments;
 
                WqlEventQuery wqlEventQuery = new WqlEventQuery(QueryProcessExitEvent);
                ManagementEventWatcher watcher = new ManagementEventWatcher(managementScope, wqlEventQuery);
 
                handler = new ProcessStopTraceHandler();
                watcher.EventArrived += new EventArrivedEventHandler(handler.Arrived);
                watcher.Start();
 
                ManagementBaseObject outParams = processClass.InvokeMethod(
                    MethodCreate,
                    inParams,
                    null);
 
                if (outParams.Properties[OutputParameters.ProcessId].Value == null ||
                    outParams.Properties[OutputParameters.ReturnValue].Value == null)
                {
                    throw new WsatAdminException(WsatAdminErrorCode.REMOTE_MISSING_WSAT, SR.GetString(SR.ErrorRemoteWSATMissing));
 
                }
 
                // the process ID when executing a remote command
                uint processID = (uint)outParams.Properties[OutputParameters.ProcessId].Value;
 
                uint result = (uint)outParams.Properties[OutputParameters.ReturnValue].Value;
 
                if (result != 0)
                {
                    throw new WsatAdminException(WsatAdminErrorCode.REMOTE_MISSING_WSAT, SR.GetString(SR.ErrorRemoteWSATMissing));
                }
 
                handler.ProcessId = processID;
 
                int totalDelay = 0;
                while (!handler.IsArrived && totalDelay < autoEventTimeout)
                {
                    totalDelay += delayInAutoEventTimeout;
                    System.Threading.Thread.Sleep(delayInAutoEventTimeout);
                }
                watcher.Stop();
            }
            catch (WsatAdminException)
            {
                throw;
            }
#pragma warning suppress 56500
            catch (Exception e)
            {
                if (Utilities.IsCriticalException(e))
                {
                    throw;
                }
 
                throw new WsatAdminException(WsatAdminErrorCode.REMOTE_EXECUTION_ATTEMPT_ERROR,
                                                SR.GetString(SR.ErrorAttemptRemoteExecution, e.Message));
            }
 
            if (handler.IsArrived && handler.ReturnCode != 0)
            {
                throw new WsatAdminException((WsatAdminErrorCode)handler.ReturnCode,
                                                SR.GetString(SR.ErrorRemoteExecution, handler.ReturnCode));
            }
            else if (!handler.IsArrived)
            {
                throw new WsatAdminException(WsatAdminErrorCode.REMOTE_TIMEOUT, SR.GetString(SR.ErrorRemoteTimeout));
            }
 
            Utilities.Log("ExecuteWSATProcess successfully quitted.");
        }
 
        internal string GetDeploymentPath()
        {
            string path = null;
 
            try
            {
                //We first check if the 4.0 install path was available.
                RegistryConfigurationProvider reg = new RegistryConfigurationProvider(Microsoft.Win32.RegistryHive.LocalMachine,
                    WsatKeys.WcfSetupKey40,
                    machineName);
 
                path = reg.ReadString(WsatKeys.RuntimeInstallPath, null);
 
                if (string.IsNullOrEmpty(path))
                {
 
             //If path under 4.0 doesnt exit, check under 3.0
                 RegistryConfigurationProvider reg2 = new RegistryConfigurationProvider(Microsoft.Win32.RegistryHive.LocalMachine,
                        WsatKeys.WcfSetupKey,
                machineName);
 
                 path = reg2.ReadString(WsatKeys.RuntimeInstallPath, null);
                 if (string.IsNullOrEmpty(path))
                 {
                    throw new WsatAdminException(WsatAdminErrorCode.CANNOT_GET_REMOTE_INSTALL_PATH,
                                                                    SR.GetString(SR.ErrorCannotGetRemoteInstallPath));
        
                 }
 
                }
 
                path = Path.Combine(path, "WsatConfig.exe");
            }
            catch (WsatAdminException e)
            {
                throw new WsatAdminException(WsatAdminErrorCode.CANNOT_GET_REMOTE_INSTALL_PATH,
                                                                SR.GetString(SR.ErrorCannotGetRemoteInstallPath), e);
            }
            catch (ArgumentException e) // if the path in the remote registry is invalid...
            {
                throw new WsatAdminException(WsatAdminErrorCode.CANNOT_GET_REMOTE_INSTALL_PATH,
                                                                SR.GetString(SR.ErrorCannotGetRemoteInstallPath), e);
            }
            return path;
        }
    }
 
    class ProcessStopTraceHandler
    {
        bool isArrived = false;
        uint processID = 0;
        uint returnCode;
 
        Dictionary<uint, uint> processesExited = new Dictionary<uint, uint>();
 
        public uint ProcessId
        {
            get { return processID; }
            set
            {
                processID = value;
                if (value > 0)
                {
                    lock (processesExited)
                    {
                        uint exitCode = 0;
                        if (processesExited.TryGetValue(value, out exitCode))
                        {
                            isArrived = true;
                            returnCode = exitCode;
                        }
                    }
                }
            }
        }
 
        //Handles the event when it arrives
        internal void Arrived(object sender, EventArrivedEventArgs e)
        {
            uint procID = (uint)e.NewEvent.Properties["ProcessID"].Value;
            uint exitCode = (uint)e.NewEvent.Properties["ExitStatus"].Value;
 
            if (ProcessId == 0)
            {
                lock (processesExited)
                {
                    processesExited[procID] = exitCode;
                }
            }
            else if (procID == this.ProcessId)
            {
                this.returnCode = exitCode;
                isArrived = true;
            }
        }
 
        //Used to determine whether the event has arrived or not.
        internal bool IsArrived
        {
            get
            {
                return isArrived;
            }
        }
 
        internal uint ReturnCode
        {
            get
            {
                return returnCode;
            }
        }
    }
}