/// <summary>
        /// Asynchronously creates a plugin instance and connects to it.
        /// </summary>
        /// <param name="requestHandlers">Request handlers.</param>
        /// <param name="options">Connection options.</param>
        /// <param name="sessionCancellationToken">A cancellation token for the plugin's lifetime.</param>
        /// <returns>A task that represents the asynchronous operation.
        /// The task result (<see cref="Task{TResult}.Result" />) returns a <see cref="Plugin" />
        /// instance.</returns>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="requestHandlers" />
        /// is either <c>null</c> or empty.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="options" />
        /// is either <c>null</c> or empty.</exception>
        /// <exception cref="OperationCanceledException">Thrown if <paramref name="sessionCancellationToken" />
        /// is cancelled.</exception>
        /// <remarks>This is intended to be called by a plugin.</remarks>
        public static async Task <IPlugin> CreateFromCurrentProcessAsync(
            IRequestHandlers requestHandlers,
            ConnectionOptions options,
            CancellationToken sessionCancellationToken)
        {
            if (requestHandlers == null)
            {
                throw new ArgumentNullException(nameof(requestHandlers));
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            sessionCancellationToken.ThrowIfCancellationRequested();

            var encoding       = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
            var standardInput  = new StreamReader(Console.OpenStandardInput(), encoding);
            var standardOutput = new StreamWriter(Console.OpenStandardOutput(), encoding);
            var sender         = new Sender(standardOutput);
            var receiver       = new StandardInputReceiver(standardInput);
            var logger         = PluginLogger.DefaultInstance;

            if (logger.IsEnabled)
            {
                WriteCommonLogMessages(logger);
            }

            var messageDispatcher = new MessageDispatcher(requestHandlers, new RequestIdGenerator(), logger);
            var connection        = new Connection(messageDispatcher, sender, receiver, options, logger);
            var process           = Process.GetCurrentProcess();
            var filePath          = process.MainModule.FileName;
            var pluginProcess     = new PluginProcess(process);

            // Wire up handlers before calling ConnectAsync(...).
            var plugin = new Plugin(
                filePath,
                connection,
                pluginProcess,
                isOwnProcess: true,
                idleTimeout: Timeout.InfiniteTimeSpan);

            requestHandlers.TryAdd(MessageMethod.Close, new CloseRequestHandler(plugin));
            requestHandlers.TryAdd(MessageMethod.MonitorNuGetProcessExit, new MonitorNuGetProcessExitRequestHandler(plugin));

            try
            {
                await connection.ConnectAsync(sessionCancellationToken);
            }
            catch (Exception)
            {
                plugin.Dispose();

                throw;
            }

            return(plugin);
        }
示例#2
0
        private async Task <IPlugin> CreatePluginAsync(
            string filePath,
            IEnumerable <string> arguments,
            IRequestHandlers requestHandlers,
            ConnectionOptions options,
            CancellationToken sessionCancellationToken)
        {
            var startInfo = new ProcessStartInfo(filePath)
            {
                Arguments              = string.Join(" ", arguments),
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
            };

            var process           = Process.Start(startInfo);
            var sender            = new Sender(process.StandardInput);
            var receiver          = new StandardOutputReceiver(new PluginProcess(process));
            var messageDispatcher = new MessageDispatcher(requestHandlers, new RequestIdGenerator());
            var connection        = new Connection(messageDispatcher, sender, receiver, options);
            var pluginProcess     = new PluginProcess(process);

            var plugin = new Plugin(
                filePath,
                connection,
                pluginProcess,
                isOwnProcess: false,
                idleTimeout: _pluginIdleTimeout);

            try
            {
                // Wire up handlers before calling ConnectAsync(...).
                RegisterEventHandlers(plugin);

                await connection.ConnectAsync(sessionCancellationToken);

                process.EnableRaisingEvents = true;
            }
            catch (ProtocolException ex)
            {
                throw new ProtocolException(
                          string.Format(CultureInfo.CurrentCulture, Strings.Plugin_Exception, plugin.Name, ex.Message));
            }
            catch (Exception)
            {
                plugin.Dispose();

                throw;
            }

            return(plugin);
        }
示例#3
0
        private async Task <IPlugin> CreatePluginAsync(
            string filePath,
            IEnumerable <string> arguments,
            IRequestHandlers requestHandlers,
            ConnectionOptions options,
            CancellationToken sessionCancellationToken)
        {
            var args = string.Join(" ", arguments);

#if IS_DESKTOP
            var startInfo = new ProcessStartInfo(filePath)
            {
                Arguments              = args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
                WindowStyle            = ProcessWindowStyle.Hidden,
            };
#else
            var startInfo = new ProcessStartInfo
            {
                FileName = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") ??
                           (NuGet.Common.RuntimeEnvironmentHelper.IsWindows ?
                            "dotnet.exe" :
                            "dotnet"),
                Arguments              = $"\"{filePath}\" " + args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false),
                WindowStyle            = ProcessWindowStyle.Hidden,
            };
#endif
            var    pluginProcess = new PluginProcess(startInfo);
            string pluginId      = Plugin.CreateNewId();

            using (var combinedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(sessionCancellationToken))
            {
                // Process ID is unavailable until we start the process; however, we want to wire up this event before
                // attempting to start the process in case the process immediately exits.
                EventHandler <IPluginProcess> onExited = null;
                Connection connection = null;

                onExited = (object eventSender, IPluginProcess exitedProcess) =>
                {
                    exitedProcess.Exited -= onExited;

                    OnPluginProcessExited(eventSender, exitedProcess, pluginId);

                    if (connection?.State == ConnectionState.Handshaking)
                    {
                        combinedCancellationTokenSource.Cancel();
                    }
                };

                pluginProcess.Exited += onExited;

                var stopwatch = Stopwatch.StartNew();

                pluginProcess.Start();

                if (_logger.IsEnabled)
                {
                    WriteCommonLogMessages(_logger);
                }

                var sender            = new Sender(pluginProcess.StandardInput);
                var receiver          = new StandardOutputReceiver(pluginProcess);
                var processingHandler = new InboundRequestProcessingHandler(new HashSet <MessageMethod> {
                    MessageMethod.Handshake, MessageMethod.Log
                });
                var messageDispatcher = new MessageDispatcher(requestHandlers, new RequestIdGenerator(), processingHandler, _logger);
                connection = new Connection(messageDispatcher, sender, receiver, options, _logger);

                var plugin = new Plugin(
                    filePath,
                    connection,
                    pluginProcess,
                    isOwnProcess: false,
                    idleTimeout: _pluginIdleTimeout,
                    id: pluginId);

                if (_logger.IsEnabled)
                {
                    _logger.Write(new PluginInstanceLogMessage(_logger.Now, plugin.Id, PluginState.Started, pluginProcess.Id));
                }

                try
                {
                    // Wire up handlers before calling ConnectAsync(...).
                    plugin.Faulted += OnPluginFaulted;
                    plugin.Idle    += OnPluginIdle;

                    await connection.ConnectAsync(combinedCancellationTokenSource.Token);

                    // It's critical that this be registered after ConnectAsync(...).
                    // If it's registered before, stuff could be disposed, which would lead to unexpected exceptions.
                    // If the plugin process exited before this event is registered, an exception should be caught below.
                    plugin.Exited += OnPluginExited;
                }
                catch (ProtocolException ex)
                {
                    plugin.Dispose();

                    throw new ProtocolException(
                              string.Format(CultureInfo.CurrentCulture, Strings.Plugin_Exception, plugin.Name, ex.Message));
                }
                catch (TaskCanceledException ex)
                {
                    stopwatch.Stop();

                    plugin.Dispose();

                    throw new PluginException(
                              string.Format(
                                  CultureInfo.CurrentCulture,
                                  Strings.Plugin_FailedOnCreation,
                                  plugin.Name,
                                  stopwatch.Elapsed.TotalSeconds,
                                  pluginProcess.ExitCode),
                              ex);
                }
                catch (Exception)
                {
                    plugin.Dispose();

                    throw;
                }

                return(plugin);
            }
        }
        private async Task <IPlugin> CreatePluginAsync(
            string filePath,
            IEnumerable <string> arguments,
            IRequestHandlers requestHandlers,
            ConnectionOptions options,
            CancellationToken sessionCancellationToken)
        {
            var args = string.Join(" ", arguments);

#if IS_DESKTOP
            var startInfo = new ProcessStartInfo(filePath)
            {
                Arguments              = args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
            };
#else
            var startInfo = new ProcessStartInfo
            {
                FileName               = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"),
                Arguments              = $"\"{filePath}\" " + args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
            };
#endif
            var process  = Process.Start(startInfo);
            var sender   = new Sender(process.StandardInput);
            var receiver = new StandardOutputReceiver(new PluginProcess(process));

            if (_logger.IsEnabled)
            {
                WriteCommonLogMessages(_logger);

                _logger.Write(new PluginInstanceLogMessage(_logger.Now, process.Id));
            }

            var messageDispatcher = new MessageDispatcher(requestHandlers, new RequestIdGenerator(), _logger);
            var connection        = new Connection(messageDispatcher, sender, receiver, options, _logger);
            var pluginProcess     = new PluginProcess(process);

            var plugin = new Plugin(
                filePath,
                connection,
                pluginProcess,
                isOwnProcess: false,
                idleTimeout: _pluginIdleTimeout);

            try
            {
                // Wire up handlers before calling ConnectAsync(...).
                RegisterEventHandlers(plugin);

                await connection.ConnectAsync(sessionCancellationToken);

                process.EnableRaisingEvents = true;
            }
            catch (ProtocolException ex)
            {
                throw new ProtocolException(
                          string.Format(CultureInfo.CurrentCulture, Strings.Plugin_Exception, plugin.Name, ex.Message));
            }
            catch (Exception)
            {
                plugin.Dispose();

                throw;
            }

            return(plugin);
        }
示例#5
0
        private async Task <IPlugin> CreatePluginAsync(
            string filePath,
            IEnumerable <string> arguments,
            IRequestHandlers requestHandlers,
            ConnectionOptions options,
            CancellationToken sessionCancellationToken)
        {
            var args = string.Join(" ", arguments);

#if IS_DESKTOP
            var startInfo = new ProcessStartInfo(filePath)
            {
                Arguments              = args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
            };
#else
            var startInfo = new ProcessStartInfo
            {
                FileName               = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH"),
                Arguments              = $"\"{filePath}\" " + args,
                UseShellExecute        = false,
                RedirectStandardError  = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                StandardOutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)
            };
#endif
            var process = new Process();

            string pluginId = Plugin.CreateNewId();

            // Process ID is unavailable until we start the process; however, we want to wire up this event before
            // attempting to start the process in case the process immediately exits.
            EventHandler onExited = null;

            onExited = (object eventSender, EventArgs e) =>
            {
                process.Exited -= onExited;

                int?processId = GetProcessIdOrNull(process);

                OnPluginProcessExited(eventSender, e, pluginId, processId);
            };

            process.Exited += onExited;

            var pluginProcess = new PluginProcess(process);

            process.StartInfo = startInfo;

            process.Start();

            if (_logger.IsEnabled)
            {
                WriteCommonLogMessages(_logger);
            }

            var sender            = new Sender(process.StandardInput);
            var receiver          = new StandardOutputReceiver(pluginProcess);
            var messageDispatcher = new MessageDispatcher(requestHandlers, new RequestIdGenerator(), _logger);
            var connection        = new Connection(messageDispatcher, sender, receiver, options, _logger);

            var plugin = new Plugin(
                filePath,
                connection,
                pluginProcess,
                isOwnProcess: false,
                idleTimeout: _pluginIdleTimeout,
                id: pluginId);

            if (_logger.IsEnabled)
            {
                int?processId = GetProcessIdOrNull(process);

                _logger.Write(new PluginInstanceLogMessage(_logger.Now, plugin.Id, PluginState.Started, processId));
            }

            try
            {
                // Wire up handlers before calling ConnectAsync(...).
                RegisterEventHandlers(plugin);

                await connection.ConnectAsync(sessionCancellationToken);

                process.EnableRaisingEvents = true;
            }
            catch (ProtocolException ex)
            {
                throw new ProtocolException(
                          string.Format(CultureInfo.CurrentCulture, Strings.Plugin_Exception, plugin.Name, ex.Message));
            }
            catch (Exception)
            {
                plugin.Dispose();

                throw;
            }

            return(plugin);
        }