protected override void HandleLaunchRequestAsync()

in src/OpenDebugAD7/AD7DebugSession.cs [930:1118]


        protected override void HandleLaunchRequestAsync(IRequestResponder<LaunchArguments> responder)
        {
            const string telemetryEventName = DebuggerTelemetry.TelemetryLaunchEventName;

            int hr;
            DateTime launchStartTime = DateTime.Now;

            string mimode = responder.Arguments.ConfigurationProperties.GetValueAsString("MIMode");
            string program = responder.Arguments.ConfigurationProperties.GetValueAsString("program")?.Trim();
            if (string.IsNullOrEmpty(program))
            {
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: property 'program' is missing or empty"));
                return;
            }

            // If program is still in the default state, raise error
            if (program.EndsWith(">", StringComparison.Ordinal) && program.Contains('<', StringComparison.Ordinal))
            {
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: launch.json must be configured. Change 'program' to the path to the executable file that you would like to debug."));
                return;
            }

            // Should not have a pid in launch
            if (responder.Arguments.ConfigurationProperties.ContainsKey("processId"))
            {
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "launch: The parameter: 'processId' should not be specified on Launch. Please use request type: 'attach'"));
                return;
            }

            JToken pipeTransport = responder.Arguments.ConfigurationProperties.GetValueAsObject("pipeTransport");
            string miDebuggerServerAddress = responder.Arguments.ConfigurationProperties.GetValueAsString("miDebuggerServerAddress");

            // Pipe trasport can talk to remote machines so paths and files should not be checked in this case.
            bool skipFilesystemChecks = (pipeTransport != null || miDebuggerServerAddress != null);

            // For a remote scenario, we assume whatever input user has provided is correct.
            // The target remote could be any OS, so we don't try to change anything.
            if (!skipFilesystemChecks)
            {
                if (!ValidateProgramPath(ref program, mimode))
                {
                    responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1002, String.Format(CultureInfo.CurrentCulture, "launch: program '{0}' does not exist", program)));
                    return;
                }
            }

            string workingDirectory = responder.Arguments.ConfigurationProperties.GetValueAsString("cwd");
            if (string.IsNullOrEmpty(workingDirectory))
            {
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1003, "launch: property 'cwd' is missing or empty"));
                return;
            }

            if (!skipFilesystemChecks)
            {
                workingDirectory = m_pathConverter.ConvertLaunchPathForVsCode(workingDirectory);
                if (!Directory.Exists(workingDirectory))
                {
                    responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1004, String.Format(CultureInfo.CurrentCulture, "launch: workingDirectory '{0}' does not exist", workingDirectory)));
                    return;
                }
            }

            SetCommonDebugSettings(responder.Arguments.ConfigurationProperties);

            bool success = false;
            try
            {
                lock (m_lock)
                {
                    Debug.Assert(m_currentLaunchState == null, "Concurrent launches??");
                    m_currentLaunchState = new CurrentLaunchState();
                }
                var eb = new ErrorBuilder(() => AD7Resources.Error_Scenario_Launch);

                // Don't convert the workingDirectory string if we are a pipeTransport connection. We are assuming that the user has the correct directory separaters for their target OS
                string workingDirectoryString = pipeTransport != null ? workingDirectory : m_pathConverter.ConvertClientPathToDebugger(workingDirectory);

                m_sessionConfig.StopAtEntrypoint = responder.Arguments.ConfigurationProperties.GetValueAsBool("stopAtEntry").GetValueOrDefault(false);

                m_processId = Constants.InvalidProcessId;
                m_processName = program;

                enum_LAUNCH_FLAGS flags = enum_LAUNCH_FLAGS.LAUNCH_DEBUG;
                if (responder.Arguments.NoDebug.GetValueOrDefault(false))
                {
                    flags = enum_LAUNCH_FLAGS.LAUNCH_NODEBUG;
                }

                SetCommonMISettings(responder.Arguments.ConfigurationProperties);

                string launchJson = JsonConvert.SerializeObject(responder.Arguments.ConfigurationProperties);

                // Then attach
                hr = m_engineLaunch.LaunchSuspended(null,
                    m_port,
                    program,
                    null,
                    null,
                    null,
                    launchJson,
                    flags,
                    0,
                    0,
                    0,
                    this,
                    out m_process);
                if (hr != HRConstants.S_OK)
                {
                    // If the engine raised a message via an error event, fire that instead
                    if (hr == HRConstants.E_ABORT)
                    {
                        string message;
                        lock (m_lock)
                        {
                            message = m_currentLaunchState?.CurrentError?.Item2;
                            m_currentLaunchState = null;
                        }
                        if (message != null)
                        {
                            responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1005, message));
                            return;
                        }
                    }

                    eb.ThrowHR(hr);
                }

                hr = m_engineLaunch.ResumeProcess(m_process);
                if (hr < 0)
                {
                    // try to terminate the process if we can
                    try
                    {
                        m_engineLaunch.TerminateProcess(m_process);
                    }
                    catch
                    {
                        // Ignore failures since we are already dealing with an error
                    }

                    eb.ThrowHR(hr);
                }

                var properties = new Dictionary<string, object>(StringComparer.Ordinal);

                if (flags.HasFlag(enum_LAUNCH_FLAGS.LAUNCH_NODEBUG))
                {
                    properties.Add(DebuggerTelemetry.TelemetryIsNoDebug, true);
                }

                properties.Add(DebuggerTelemetry.TelemetryMIMode, mimode);
                properties.Add(DebuggerTelemetry.TelemetryFrameworkVersion, GetFrameworkVersionAttributeValue());

                DebuggerTelemetry.ReportTimedEvent(telemetryEventName, DateTime.Now - launchStartTime, properties);

                success = true;
            }
            catch (Exception e)
            {
                // Instead of failing to launch with the exception, try and wrap it better so that the information is useful for the user.
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1007, string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_ExceptionOccured, e.InnerException?.ToString() ?? e.ToString())));
                return;
            }
            finally
            {
                // Clear _currentLaunchState
                CurrentLaunchState currentLaunchState;
                lock (m_lock)
                {
                    currentLaunchState = m_currentLaunchState;
                    m_currentLaunchState = null;
                }

                if (!success)
                {
                    m_process = null;
                }

                // If we had an error event that we didn't wind up returning as an exception, raise it as an event
                Tuple<MessagePrefix, string> currentError = currentLaunchState?.CurrentError;
                if (currentError != null)
                {
                    SendMessageEvent(currentError.Item1, currentError.Item2);
                }
            }

            responder.SetResponse(new LaunchResponse());
        }