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