Can't execute statement with VS Debugger Interop
Asked Answered
E

2

16

I'm writing a debugger extension VSPackage in which I want to execute a statement in the debugged process when a breakpoint is hit. In my extension code I have this:

void Initialize()
{
    // ...standard vspackage init code omitted...

    Globals.Init((DTE2)GetService(typeof(DTE)));              
    Globals.DebuggerEvents.OnEnterBreakMode += (dbgEventReason reason, ref dbgExecutionAction action) =>
    {
        try
        {
           var e1 = Globals.Application.Debugger.GetExpression("1+2");
           Debug.WriteLine(e1.Value);     // Prints "3"

           Globals.Application.Debugger.ExecuteStatement("x = 1+2", 1000);
           Debug.WriteLine("OK");         // Never prints this                          
        } 
        catch (Exception ex)
        {
           Debug.WriteLine("Error: "+ex); // Nor this
        }
    }             
}

When debugging this extension in a VS instance I load a trivial program looking like this

static void Main()
{
   int x = 5;
   Console.WriteLine("X is "+x); // Breakpoint on this line
}

When the breakpoint is hit in the debugged process the handler is called and the output window for the extension shows "3", so evaluating expressions works, but it never succeeds executing the statement. Nothing more is printed to the output window. No exception or timeout occurs and I can't continue debugging the process, the debugger appears to have crashed.

The globals class just holds the DTE and DebuggerEvents

public static class Globals
{
   public static void Init(DTE2 dte)
   {
      Application = dte;
      DebuggerEvents = dte.Events.DebuggerEvents;    
   }

   public static DTE2 Application { get; private set; }
   public static DebuggerEvents DebuggerEvents { get; private set; }
}

What am I doing wrong, or misunderstanding here?

Enchant answered 14/2, 2015 at 10:56 Comment(2)
Best thing to assume here is that the debugger engine is not in a state yet where it can start taking commands. Standard trick is to do it later with a timer or, say, ThreadPool.QueueUserWorkItem() so it happens later.Peritonitis
Delaying it helps somewhat it will sometimes execute an expression. I have a feeling may be killing it by re-entering the handler, as if ExecuteStatement immediately raises OnEnterBreakMode (that could explain why nothing happens unless you delay it somewhat).Enchant
K
2

This is an old question, but there is so little on Google about these issues, I thought I'd offer some help. Some important considerations:

  1. Use GetExpresssion3(TreatAsStatement:=True), if possible, instead of ExecuteStatement (I could not get ExecuteStatement working properly).
  2. The thread calling your delegate (OnEnterBreakMode) is the same thread that will need will to run again in order to execute your expression or statement. Therefore, call your GetExpression method on a new thread (Task.Run..)
  3. You will have to monitor and manage the Reason value for OnEnterBreakMode. The initial Reason is UnwindFromException for the actual unhandled exception. Then, it is expected you are setting a variable, such as tempStack = New System.Diagnostics.StackTrace(True). OnEnterBreakMode will be called again following execution of this statement, but this time with Evaluation for the Reason. At this point you now call all of your GetExpressions to collect all of your data without additional OnEnterBreakMode calls.

    Dim dte2 As EnvDTE80.DTE2 = GetGlobalService(GetType(EnvDTE.DTE))

    Dim debugger5 as EnvDTE100.Debugger5 = Dte2.Debugger

Interesting design observation: System.Diagnostics.StackTrace is a very strangely designed class in the context of the rest of the .NET framework until you have to work on this project where you are extracting the StackTrace through this very technique and see the benefit of its otherwise odd design.

Kailyard answered 7/3, 2017 at 18:10 Comment(0)
K
1

I was tinkering a lot with Visual Studio debugging, and the ultimate cause of freezing was always related to thread handling: VS allows any piece of code to run while debugging only in the main thread. Every other thread is disabled and in case your debug code depends on a different thread it will freeze too.

My guess: You initialized your DTE in a different thread than what you are debugging.

Assumed result: Delegate method tries to load the context of the initializing thread which is different from the debugged thread, and thus it is bound to get frozen.

Proposed solution: Don't use delegate method. They implicitly refer back to the original execution context. Instead register a regular method, and reinitialize your DTE in that context.

Kandrakandy answered 5/2, 2017 at 20:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.