int IDebugEngine2.Attach(IDebugProgram2[] rgpPrograms, IDebugProgramNode2[] rgpProgramNodes, uint celtPrograms, IDebugEventCallback2 pCallback, enum_ATTACH_REASON dwReason) {
ThrowIfDisposed();
if (rgpPrograms.Length != 1) {
throw new ArgumentException("Zero or more than one programs", nameof(rgpPrograms));
}
_program = rgpPrograms[0] as RDebugPortSupplier.DebugProgram;
if (_program == null) {
throw new ArgumentException("rgpPrograms[0] must be an " + nameof(RDebugPortSupplier.DebugProgram), nameof(rgpPrograms));
}
Marshal.ThrowExceptionForHR(_program.GetProgramId(out _programId));
_events = pCallback;
Session = _program.Session;
Tracer = TaskExtensions.RunSynchronouslyOnUIThread(ct => Session.TraceExecutionAsync(ct));
MainThread = new AD7Thread(this);
IsConnected = true;
// Enable breakpoint instrumentation.
TaskExtensions.RunSynchronouslyOnUIThread(ct => Tracer.EnableBreakpointsAsync(true, ct));
// Send notification after acquiring the session - we need it in case there were any breakpoints pending before
// the attach, in which case we'll immediately get breakpoint creation requests as soon as we send these, and
// we will need the session to process them.
AD7EngineCreateEvent.Send(this);
AD7ProgramCreateEvent.Send(this);
Send(new AD7LoadCompleteEvent(), AD7LoadCompleteEvent.IID);
var sessionProvider = WorkflowProvider.GetOrCreate().RSessions;
sessionProvider.BrokerChanged += SessionProvider_BrokerChanged;
// Register event handlers after notifying VS that debug engine has loaded. This order is important because
// we may get a Browse event immediately, and we want to raise a breakpoint notification in response to that
// to pause the debugger - but it will be ignored unless the engine has reported its creation.
// Also, AfterRequest must be registered before Browse, so that we never get in a situation where we get
// Browse but not AfterRequest that follows it because of a race between raising and registration.
Session.AfterRequest += Session_AfterRequest;
Session.Disconnected += Session_Disconnected;
// If we're already at the Browse prompt, registering the handler will result in its immediate invocation.
// We want to handle that fully before we process any following AfterRequest event to avoid concurrency issues
// where we pause and never resume, so hold the lock while adding the handler.
lock (_browseLock) {
Tracer.Browse += Tracer_Browse;
}
return VSConstants.S_OK;
}