protected override void HandleAttachRequestAsync()

in src/OpenDebugAD7/AD7DebugSession.cs [1120:1305]


        protected override void HandleAttachRequestAsync(IRequestResponder<AttachArguments> responder)
        {
            const string telemetryEventName = DebuggerTelemetry.TelemetryAttachEventName;

            // ProcessId can be either a string or an int. We attempt to parse as int, if that does not exist we attempt to parse as a string.
            string processId = responder.Arguments.ConfigurationProperties.GetValueAsInt("processId")?.ToString(CultureInfo.InvariantCulture) ?? responder.Arguments.ConfigurationProperties.GetValueAsString("processId");
            string miDebuggerServerAddress = responder.Arguments.ConfigurationProperties.GetValueAsString("miDebuggerServerAddress");
            DateTime attachStartTime = DateTime.Now;
            JObject pipeTransport = responder.Arguments.ConfigurationProperties.GetValueAsObject("pipeTransport");
            bool isPipeTransport = (pipeTransport != null);
            bool isLocal = string.IsNullOrEmpty(miDebuggerServerAddress) && !isPipeTransport;
            string mimode = responder.Arguments.ConfigurationProperties.GetValueAsString("MIMode");
            bool useExtendedRemote = responder.Arguments.ConfigurationProperties.GetValueAsBool("useExtendedRemote").GetValueOrDefault(false);

            if (isLocal)
            {
                if (string.IsNullOrEmpty(processId))
                {
                    responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "attach: property 'processId' needs to be specified"));
                    return;
                }
            }
            else
            {
                string propertyCausingRemote = !string.IsNullOrEmpty(miDebuggerServerAddress) ? "miDebuggerServerAddress" : "pipeTransport";

                if (!string.IsNullOrEmpty(miDebuggerServerAddress) && !string.IsNullOrEmpty(processId) && !useExtendedRemote)
                {
                    responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1002, "attach: 'useExtendedRemote' needs to be set to true when 'processId' is used with " + propertyCausingRemote));
                    return;
                }
                else if (isPipeTransport && (string.IsNullOrEmpty(processId) || string.IsNullOrEmpty(pipeTransport.GetValueAsString("debuggerPath"))))
                {
                    responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1001, "attach: properties 'processId' and 'debuggerPath' needs to be specified with " + propertyCausingRemote));
                    return;
                }
            }

            int pid = 0;

            ProtocolException protocolException = isLocal ? VerifyLocalProcessId(processId, telemetryEventName, out pid) : VerifyProcessId(processId, telemetryEventName, out pid);

            if (protocolException != null)
            {
                responder.SetError(protocolException);
                return;
            }

            SetCommonDebugSettings(responder.Arguments.ConfigurationProperties);

            string program = responder.Arguments.ConfigurationProperties.GetValueAsString("program");
            string executable = null;
            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_Attach);

                if (isPipeTransport)
                {
                    if (string.IsNullOrEmpty(pipeTransport.GetValueAsString("debuggerPath")))
                    {
                        responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1011, "debuggerPath is required for attachTransport."));
                        return;
                    }

                    if (string.IsNullOrEmpty(program))
                    {
                        responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1009, "attach: property 'program' is missing or empty"));
                        return;
                    }
                    else
                    {
                        executable = program;
                    }
                    m_isAttach = true;
                }
                else
                {
                    if (string.IsNullOrEmpty(program))
                    {
                        responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1009, "attach: property 'program' is missing or empty"));
                        return;
                    }

                    if (!ValidateProgramPath(ref program, mimode))
                    {
                        responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1010, String.Format(CultureInfo.CurrentCulture, "attach: program path '{0}' does not exist", program)));
                        return;
                    }

                    executable = program;
                    m_isAttach = true;
                }

                if (int.TryParse(processId, NumberStyles.None, CultureInfo.InvariantCulture, out m_processId))
                {
                    m_processId = Constants.InvalidProcessId;
                }
                m_processName = program ?? string.Empty;

                SetCommonMISettings(responder.Arguments.ConfigurationProperties);

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

                // attach
                int hr = m_engineLaunch.LaunchSuspended(null, m_port, executable, null, null, null, launchJson, 0, 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, 1012, 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);
                properties.Add(DebuggerTelemetry.TelemetryMIMode, mimode);
                properties.Add(DebuggerTelemetry.TelemetryFrameworkVersion, GetFrameworkVersionAttributeValue());

                DebuggerTelemetry.ReportTimedEvent(telemetryEventName, DateTime.Now - attachStartTime, properties);
                success = true;

                responder.SetResponse(new AttachResponse());
            }
            catch (Exception e)
            {
                responder.SetError(CreateProtocolExceptionAndLogTelemetry(telemetryEventName, 1007, string.Format(CultureInfo.CurrentCulture, AD7Resources.Error_ExceptionOccured, e.InnerException?.ToString() ?? e.ToString())));
            }
            finally
            {
                // Clear _currentLaunchState
                CurrentLaunchState currentLaunchState;
                lock (m_lock)
                {
                    currentLaunchState = m_currentLaunchState;
                    m_currentLaunchState = 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);
                }

                if (!success)
                {
                    m_process = null;
                    this.Protocol.Stop();
                }
            }
        }