0

Background

I'm writing a Visual Studio Extension in C# that executes code in the Immediate Window while debugging another application. The expression returns a value that can be a int, a string literal, a class, etc. The application that I'm debugging is written in C++.

The code

To execute the Immediate Window commands I use this line of code:

var expression = dte.Debugger.GetExpression("funcname()");

And to retrieve the result I use:

string val = expression.Value;

and:

var children = expression.DataMembers;

Here is myFunc() in the application I'm debugging:

std::vector<int> myFunc()
{
    return { 1, 2, 3, 4, 5 };
}

The problem

When I manually run the expression in the Immediate Window, the returned object is dumped as I see it in the Watch Window (see here). I managed to find all the children names, but the values are missing (see here).

What I want is to achieve the DisplayString ({ size=5 }), but I haven't found anything useful yet.

How can I get the DisplayString field from Debugger.GetExpression?

EDIT: I don’t have to use this API. If you know another way that can return this sting, please suggest it. One idea could be retrieving the full output string of the Immediate Window (see the right side), and then parsing it.

EDIT2: See this video that better explains the issue

lax48
  • 197
  • 1
  • 10
  • Hi lax, if you click the “drop down” key(expand the list) from watch window, beside the `string val= expression.Value;`, like the screenshot your shared, will it display `{size=5}`? – LoLance Oct 07 '20 at 09:58
  • @Lance Unfortunately `expression.Value` returns a string, so what you can see in the second screenshot is what I get. I tried to inspect the `expression` variable but `Value` is the only property that makes sense. The other avaible properties are `Type`, `Name` and `DataMembers`, but none of them contain `{ size=5 }`. There is also a property called `Collection`, but it is always null and I think it isn’t the solution to my problem. What makes me drive crazy is that the DataMembers names follow the visualizer. The value property of them has the same issue. – lax48 Oct 07 '20 at 12:02
  • Hi lax, I checked this doc: [std:Vector](https://en.cppreference.com/w/cpp/container/vector) and I noticed that `size` is a member function of std:Vector. And I think that the Properties of Expression can’t get the `size` directly. – Mr Qian Oct 08 '20 at 09:52
  • Hi Perry, thanks for your comment. I think I haven’t explained well enough what the issue is about: what I see in the watch window or in the immediate window is different from what I managed to get from GetExpression. `{ size=5 }` is the display string defined in the visualizer file (.natvis). If the GetExpression hadn’t been able to get the value of `size`, the whole custom visualizer would have failed and so the default one would have been used. On the other hand if you look at `expression.DataMembers`, you will see that the elements matches the custom visualizer. – lax48 Oct 08 '20 at 10:03
  • Sorry, don’t mind that GitHub link. I hadn’t read it carefully. Here’s the right example: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2019 (See the XML snippet of code in the paragraph “Natvis views”) – lax48 Oct 08 '20 at 11:59
  • Oh, I just understood what you said. Yes, `size()` is a member function of the class `std::vector` and of course the Properties of Expression can’t execute the `size()` method. If you look at the example of natvis of std::vector in my comment before this, you will see that the size is calculated by subtracting the end pointer from the begin pointer (`_Mylast - _Myfirst`). However, the visualizer works correctly in the Watch Window and in the Immediate Window but does not fully work with `GetExpression()` (the children of the expander match the names in `DataMembers`, but the value of the ... – lax48 Oct 08 '20 at 13:30
  • ... expression and all children do not). I hope I was clear enough. – lax48 Oct 08 '20 at 13:30
  • Thanks for your detailed explanation, I checked again, the `Value` property do return a String type, not sure if the Sting can be changed to int… And `DataMembers` do return an expressions collection but it is used to get the collection of expressions, you can check [this example](https://learn.microsoft.com/en-us/dotnet/api/envdte.expression.datamembers?view=visualstudiosdk-2019#examples) and [Expressions Interface](https://learn.microsoft.com/en-us/dotnet/api/envdte.expressions?view=visualstudiosdk-2019). So `DataMembers` may not work. – Mr Qian Oct 09 '20 at 10:06
  • @Perry thank you for the reply, but I can’t understand what you mean when you say “ the Value property do return a String type, not sure if the Sting can be changed to int… And DataMembers do return an expressions collection but it is used to get the collection of expressions”. I don’t want to change the string to be an int: I want the string to be `"{ size=5 }"` or whatever is written in the display string in the natvis file. – lax48 Oct 09 '20 at 10:38
  • Also `DataMembers` contain what I expect. I think I’ll post a video in my question to make it more clear – lax48 Oct 09 '20 at 10:42
  • @PerryQian-MSFT I've just uploaded a video that explains the issue. See EDIT2 in my question – lax48 Oct 09 '20 at 15:25

2 Answers2

0

you used a vector variable to get/parse the function return value, it’s reasonable, because the return result of myFunc() is exactly a vector type, thus the variable “std::vector result” can parse and get the vector object correctly. What do you mean by “ manually run the expression in the Immediate Window, the returned object is dumped as I see it in the Watch Window”?

Whilst, EnvDTE.Expression.Value is a string representing the value of the object. It has nothing to do with the displaystring ‘{ size=5 }’, it’s just a property expression. You can print it with below sample code:

public static void Value(DTE dte)  
{  
    // Setup debug Output window.  
    Window w = (Window)dte.Windows.Item(EnvDTE.Constants.vsWindowKindOutput);  
    w.Visible = true;  
    OutputWindow ow = (OutputWindow)w.Object;  
    OutputWindowPane owp = ow.OutputWindowPanes.Add("Value property: ");  
    owp.Activate();  

    EnvDTE.Expression exp = dte.Debugger.GetExpression("tempC", true, 1);  
    owp.OutputString("\nThe name of the expression: " + exp.Name);  
    owp.OutputString("\nThe type of the expression: " + exp.Type);  
    owp.OutputString("\nThe value of the expression: " + exp.Value);  
}

Thus, it should be by-design we are not able to get ‘{size=5}’ from the Immediate Window here: enter image description here

Regarding “the display string defined in the visualizer file (.natvis)” , DisplayString should be the property customized, could you provide your visualizer file (.natvis), or a sample project which can reproduce the issue?

Mia Wu-MSFT
  • 182
  • 6
  • Hi, thank you very much for the answer. While I was trying to create a demo project to reproduce the issue, I finally found where the problem was in my code! The trick was putting the `true` in `dte.Debugger.GetExpression("tempC", true)`! Now `expression.Value` evaluates to `"{ size=5 }"` as expected! – lax48 Mar 02 '21 at 12:33
  • Regarding your question, I'll explain what i meant for "manually run the expression in the Immediate Window, the returned object is dumped as I see it in the Watch Window". Look at the image in the first edit: I were in breakpoint on my target application and I have opened the watch window on the left and the immediate window on the right. As you can see, the information written in the immediate window is the same as the one written in the watch window, even the "{ size=5 }" (which is what I was looking for). The problem was that `dte.Debugger.GetExpression("tempC").Value` was `"{..}"` – lax48 Mar 02 '21 at 12:37
  • If you edit your answer, I will accept it as the final answer: just say that the issue was the missing `true` and, if you can, say what it does (I can't understand it from the [official docs](https://learn.microsoft.com/en-us/dotnet/api/envdte.debugger.getexpression?view=visualstudiosdk-2019)). Thank you again! – lax48 Mar 02 '21 at 12:39
0

I found out what the problem was: I wasn't passing the UseAutoExpandRules (defaulted to false) in GetExpression. Now my code works as expected!

Thanks to @Mia Wu-MSFT who put dte.Debugger.GetExpression("tempC", true, 1) in her code and made me discover the UseAutoExpandRules argument

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
lax48
  • 197
  • 1
  • 10