9

I need my Visual Studio extension to react to debugging events. I've registered a IDebugEventCallback2 and I'm receiving events, but all I get for each event is an opaque IDebugEvent2 and a Guid, many of which are not only undocumented but don't appear anywhere on the web (or in my registry).

My specific requirement at the moment is to know when the process is Continued - ie. the user has hit Continue, Run to cursor, etc. What Guid should I be looking for?

Or is there some other event family that I should be subscribing to?

(More generally, is there some way I'm missing to find out about the events that are delivered to my IDebugEventCallback2::Event callback, when many of them don't appear in MSDN or anywhere else? Thanks!)

RichieHindle
  • 272,464
  • 47
  • 358
  • 399

2 Answers2

6

There is no easy way to do this. Actions such as Continue and Run to cursor are abstractions implemented by Visual Studio and do not correspond to any unique event(s) with respect to the debug engine. The debug engine event reporting interface IDebugEventCallback2 will enable you to get notified only on fine-grained events such as when creating a breakpoint or reaching a breakpoint.

While Visual Studio enables you to perform actions such as Continue and Run to cursor programmatically, it does not provide a direct way to get notified when they are taken.

You can use EnvDTE.DebuggerEvents.OnXxx events to get notified when something is about to happen. In particular, the OnEnterBreakMode event enables you to intercept a breakpoint hit and possibly take an action. You can also inspect all the details of the reached breakpoint(s) using the current EnvDTE.Debugger inside the event handler.

Now with some effort, you can use these constructs to implement events that correspond to all Visual Studio debugging actions including Continue and Run to cursor accurately. If you require additional events not provided by EnvDTE.DebuggerEvents (such as when a breakpoint is inserted), you have no choice but use IDebugEventCallback2.Event. In this case if you have specific events in mind, please mention them explicitly and I might be able to tell you the corresponding IDebugEventCallback2.Event GUIDs.

Hadi Brais
  • 22,259
  • 3
  • 54
  • 95
  • Thanks! It seems that the specific case of Continue (or variants like Run to Cursor - I don't need to differentiate) should be covered by DebuggerEvents.OnEnterRunMode - correct? But going back to IDebugEventCallback2, after debugging Visual Studio I found that the event {29e2d4a0-503e-4e17-91ea-ea021570a857} seems to correspond with the same thing (I'm calling it "GUID_ExitBreakModeEvent" based on what I see in the Visual Studio stack). I'm also using {ce6f92d3-4222-4b1e-830d-3ecff112bf22} to detect when the debugger context changes - is that sensible? Is there a better alternative? – RichieHindle Jul 01 '15 at 10:51
  • The event {29e2d4a0-503e-4e17-91ea-ea021570a857} corresponds to an internal interface `Microsoft.VisualStudio.Debugger.Interop.Internal.IDebugExitBreakStateEvent` which occur whenever a breakpoint is being exited. This means it will occur on Contine, Run to Cursor and all kinds of steps. – Hadi Brais Jul 01 '15 at 11:10
  • I'm not sure what you mean by "debugger context changes." – Hadi Brais Jul 01 '15 at 11:12
  • `DebuggerEvents.OnEnterRunMode` is just like `IDebugExitBreakStateEvent`. – Hadi Brais Jul 01 '15 at 11:14
  • Many thanks - that's very helpful! By "debugger context changes" I mean, for example, switching to a new stack frame by double-clicking in the Call Stack window. A change to to the current statement - where the yellow pointer is in the margin. – RichieHindle Jul 01 '15 at 11:47
  • You can use `Microsoft.VisualStudio.Debugger.Interop.IDebugCurrentThreadChangedEvent100` for that purpose. You can test for GUID like this: `if (riidEvent == typeof(IDebugCurrentThreadChangedEvent100).GUID)`. Then you can use the `pThread` parameter to determine exactly where the debugger stopped. Don't use {ce6f92d3-4222-4b1e-830d-3ecff112bf22}, it has nothing to do with context changes. – Hadi Brais Jul 01 '15 at 12:04
  • IDebugCurrentThreadChangedEvent100 sounds very useful. However, {ce6f92d3-4222-4b1e-830d-3ecff112bf22} also fires when (for instance) you change the value of a variable via the Watch window, which makes it perfect for what I want, which is basically "Something in the state of the debugger has changed". Is it valid to use for that purpose? Is there an alternative? – RichieHindle Jul 01 '15 at 12:22
  • @RichieHindle Exactly. {ce6f92d3-4222-4b1e-830d-3ecff112bf22}corresponds to an internal interface called `Microsoft.VisualStudio.Debugger.Interop.Internal.IDebugExpressionsDirtyEvent` :) – Hadi Brais Jul 01 '15 at 12:26
  • I don't think there are any alternatives to `IDebugExpressionsDirtyEvent`. Be careful when trying to detect this event as it will be raised not only when a variable's value has changed in the Watch and Locals windows but also when variables are first added to these windows and when invalid actions are made there as well. – Hadi Brais Jul 01 '15 at 12:47
  • Ah - wonderful! You've confirmed that IDebugExpressionsDirtyEvent is just what I need (I'm not worried about it firing in those other cases). One last question if I may - how stable are these GUIDs and the corresponding events? When extension authors like me write code that relies on them, do they then regret it because the Visual Studio guys change things (these are internal details after all, with no guarantees attached). Or are the Visual Studio internals stable enough that something I know works today will continue working tomorrow? – RichieHindle Jul 01 '15 at 12:49
  • There's no guarantee regarding those events from `Microsoft.VisualStudio.Debugger.Interop.Internal`. They might get changed or removed which might cause their GUIDs to change. So a newer version of VS might introduce breaking changes in your extension. However, if that happens, probably new events have been introduced and so you should change your code to use them anyway. Regarding those events from `Microsoft.VisualStudio.Debugger.Interop`, they will remain stable and their GUIDs will not change. – Hadi Brais Jul 01 '15 at 13:02
  • OK, understood, that makes perfect sense. Many thanks for all your help! – RichieHindle Jul 01 '15 at 13:07
  • Hi @HadiBrais is it possible to detect the event before Debugging actually Stops? I am looking at BeforeStopDebuggingProcess() inside IVsDebugProcessNotify. However, I don't know how to subscribe to this event. The events inside IDebugEventCallback2 does not seem to be reliably called before the debugging actually stops (4 out of 5 times, I get notified by 2615D9BC-1948-4D21-81EE-7A963F20CF59). I want to be able to be notified consistently before Debugger Stops. Thank you. – remondo Jul 21 '21 at 12:22
2

You probably got off on the wrong foot here, the IDebugXxx interfaces were really intended to create your own debugging engine. Still useful perhaps to see what is going on in the existing engine, you are suppose to use QueryInterface() to discover the specific interface that matches the event. Like IDebugEngineCreateEvent2, IDebugProcessCreateEvent2, IDebugProcessDestroyEvent2, etcetera. There are a bunch of them, they are listed in the VSSDK\VisualStudioIntegration\Common\Inc\msdbg.h header and include their IID.

Best thing to do is peek at this sample, it shows how to crack the event notification into the specific interfaces. The AD7Events.cs source file shows the wrappers. Do keep in mind that this sample was made to show how to create an engine, not how to use the one already built into VS.

But yes, you are not going to get a "continue debug" event out of this. A debugging engine does not need that event since it is already responsible for taking care of that by itself. It already knows when it calls IDebugProgram3::Continue().

Surely what you are looking for is IVsDebugger.AdviseDebuggerEvents(). That's the one that tells you what the built-in debugger is doing. You need to pass an object of a class that implements IVsDebuggerEvents, your OnModeChanged() method will be called with a DBGMODE notification. I don't see a lot of fantastic examples that demonstrate usage, this web page might be helpful.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks! OnModeChanged() works. Something worth noting: it's subject to a short delay, and doesn't always fire. If you step over a function that takes a long time to run, you'll get a Run event followed by a Break event, but if you step over something quick, you'll only get a Break event. – RichieHindle Jun 27 '15 at 09:25