How to specify order of debugger visualizers in Visual Studio - c#

I've been working on a debugger visualizer for Visual Studio for some time and while the actual visualizer works fine. The problem is that it always places itself at the top of the visualizer list when examining a variable which really annoys some of the users who rather have Text as the top one (since the top one is also default when opening VS).
I can't find any support for this on DialogDebuggerVisualizer or DebuggerVisualizerAttribute which were my first thoughts so I've been scouring SO/MSDN/Google for information on how to affect the sort order of the visualizers (preferably to put mine last in the list) but to no avail.
Below is how I register my visualizer, it then just shows a form based on the value that is being visualized.
using Microsoft.VisualStudio.DebuggerVisualizers;
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(Shorthand.VSAddins.JsonVisualizer.JsonVisualizer),
typeof(VisualizerObjectSource),
Target = typeof(string),
Description = "Json Visualizer")]
namespace Shorthand.VSAddins.JsonVisualizer
{
public class JsonVisualizer : DialogDebuggerVisualizer
{
protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
{
var json = objectProvider.GetObject() as string;
var form = new VisualizerForm { Json = json };
windowService.ShowDialog(form);
}
}
}
Does anyone know if it is possible to affect the order of the visualizers or should I just let it be?

I don't think there is a solution. But there is a workaround:
Define your own Text Visualizer and put appropriate DebuggerVisualizer attribute before the attribute of your JsonVisualizer. The result will be that string will be readable by default and Json Visualizer can be chosen. A window with a multi-line textbox is not too much work.
It is probably not even necessary to write visualizer. It should be possible to use internal one but I don't know its name (Which class is used for "Text Visualizer"?).

It will always appear first, by design. The under the hood cast has found the best match for the variable it is reflecting on.
however, you could do either of two things. You could make the visualizer only appear when the sting contains ':'
Or you could use reflection to reorder the visualisers by adding them to the end of the collection in the order you want, then removing the originals from the collection.
For the latter you will most likely have to change the collection from readonly to writable. Via reflection.
There is no reliable source to draw on other than your will to succeed.

I guess that VS 'under the hood' can distinguish between type of string and type of xml quite easily, but Xml is just a string too, so a key question here would be, how does VS tell the difference between the two?
Could you dissect the VS XML visualizer to see how it works (even if you have to use reflector on the DLL to do it, you might get to see the method that works it out)

Related

what will be the Regular Expression to get all the property and variables names of a class in c#?

What will be the Regular Expression to get all the property and variables names of any class in c#, I want to parse the *.cs file. that is i want to select any *.cs file as input and it should get the property name of that selected class, as an output.
can any one help!!!....would appreciate for any help i tried very much but not got the actual result every time class name is coming instead of property.
thanks
Jack
There's no way you're going to be able to get exactly what you want with a regular expression because you need semantic context, not just string parsing.
For example, a good first attempt at finding all of the field and property definitions in a C# file might go something like this
^\s*(?:(?:private|public|protected|internal)\s+)?(?:static\s+)?(?:readonly\s+)?(\w+)\s+(\w+)\s*[^(]
That will match properties (public int Foo {...}) and fields (private int foo;) but not methods (protected void Bar()).
The problem is that a regex engine has no concept of the context within which those tokens appear. It will match both foo and bar in this code:
int foo;
void Stuff()
{
int bar;
}
If you happen to know that your code file follows some coding standards, you may have more luck. For example, if you enforce a style rule that all class members must have access specifiers, then you can make the private/public/etc part of that regex non-optional; since those are only permitted at the class level, it will filter out local variables.
There are other options, none of them too attractive at first glance. There is persistent talk from the C# dev team about exposing the C# compiler as a service in some future version of .NET, which would be perfect here, but I wouldn't expect that any time soon. You could purchase a third-party C# parser/analyzer like this one (caveat: I have zero experience with that, it's just the first Google hit). You could try compiling the .cs file using csc and examining the IL, but you'd need to know all of the third-party references.

Visual Studio Settings.settings file

Is there a way of creating a new setting in the Settings.settings file during run time?
For example, I want to write to the settings file from one class function, and read that value from a different class function. And no, I don't want to pass the values.
I know how to get values from the Settings.settings file
(value = Properties.Settings.Default.XXX)
I know how to update an existing value
(Properties.Settings.Default.XXX = newValue; Properties.Settings.Default.Save())
I want to know how I can add "Name", "Type", "Scope" and "Value" into the Settings.settings file during run time.
Any suggestions would be appreciated!
Thanks,
Ivar
The Issues
I believe Visual Studio generates code when you design the application settings and values, therefore at runtime this would not be easy and at worst impossible without a designer. However you can sometimes call upon design features at runtime.
You'll notice the code-behind has the properties in C# that you create in your designer. For example, I added a setting for:
Age [int] 30.
The code-behind has generated:
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("30")]
public int Age {
get {
return ((int)(this["Age"]));
}
set {
this["Age"] = value;
}
}
(The code generation is why you have strongly-typed settings)
I'm unsure if you could effect this same thing at runtime. You would have to generate code and feed it back to the JIT compiler dynamically or something like that. Or maybe there's another way I don't know about in my limited understanding of settings.
Suggestion/Workaround
I'd suggest figuring out an alternate/easier way instead of jumping through hoops. For example, make one setting a collection type that is serializable so it can store multiple values.
Then you can, for example, store multiple ages under just one setting:
Ages [System.Collections.ArrayList] {add multiple values programatically}
You might end up with C# code to manage it like:
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add("1");
list.Add("30");
Properties.Settings.Default.Ages = list;
Properties.Settings.Default.Save();

How would I retrieve the fully qualified name of an identifier in a VS macro?

I'm trying to resolve the fully qualified name of a c# identifier at a certain point (cursor) of a code window, using a Macro (or even an Add-in) in Visual Studio 2008.
For example, if the cursor is in "Rectangle", I would like "System.Drawing.Rectangle" returned.
I've tried FileCodeModel.CodeElements and .CodeElementFromPoint but they only retrieve the containing method or class (and others).
If this can't be done using a macro or add-in (even though VS does know the information via intellisense), would it be possible to use Reflection read in the c# file and get the desired info?
It can be done. Here's one solution (albeit a somewhat hacky one): use F1 Help Context. In order to make F1 help work, Visual Studio pushes the fully-qualified type name of the current selection or insertion point into a bag of name/value pairs called "F1 Help Context". And there are public APIs in the Visual Studio SDK for querying the contents of F1 Help Context.
In order to stay sane, you'll want to enable the debugging registry key for F1 Help Context. This lets you see what's in Help Context at any time via the oft-maligned Dynamic Help window. To do this:
start visual studio and choose Dynamic Help from the Help menu.
set the registry key below (you need step #1 to create the registry tree)
restart Visual Studio to pick up the changes
Now, in the dynamic help window there will be debug output so you can see what's in F1 help context. The rest of this answer describes how to get at that context programmatically so your add-in can use it.
Here's the registry key:
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Dynamic Help]
"Display Debug Output in Retail"="YES"
As you'll see from looking at the F1 debug output, Visual Studio doesn't explicitly tell you "this is the identifier's type". Instead, it simply sticks the fully-qualified type name at the head of one or more "Help Keywords" which F1 uses to bring up help. For example, you can have System.String, VS.TextEditor, and VS.Ambient in your help context, and only the first one is related to the current code.
The trick to make this easier is this: Visual Studio can mark keywords as case-sensitive or case-insensitive. AFAIK, the only part of Visual Studio which injects case-sensitive keywords is the code editor of case-sensitive languages (C#, C++) in response to code context. Therefore, if you filter all keywords to case-sensitive keywords, you know you're looking at code.
Unfortunately, the C# editor also pushes language keywords (not just identifiers) into help context if the insertion point is on top of a language keyword. So you'll need to screen out language keywords. There are two ways to do this. You can simply try to look them up in the type system, and since they're not valid type names (especially not the way VS mangles them, e.g. "string_CSharpKeyword" for the string keyword) you can just fail silently. Or you can detect the lack of dots and assume it's not a type name. Or you can detect the _CSharpKeyword suffix and hope the VS team doesn't change it. :-)
Another potential issue is generics. The type name you'll get from VS for a generic type looks like this:
System.Collections.Generic.List`1
and methods look like this:
System.Collections.Generic.List`1.FindAll.
You'll need to be smart about detecting the back-tick and dealing with it.
Also, you might get interesting behavior in cases like ASP.NET MVC .ASPX files where there's both C# code and other case-sensitive code (e.g. javascript) on the page. In that case, you'll need to look at the attributes as well. In addition to keywords, Help Context also has "attributes", which are name/value pairs describing the current context. For example, devlang=csharp is one attribute. The code below can be used to pull out attributes too. You'll need to experiment to figure out the right attributes to look for so you don't end up acting on javascript or other odd code.
Anyway, now that you understand (or at least have been exposed to!) all the caveats, here's some code to pull out the case-sensitive keyword (if it exists) from help context, as well as the rest of the name/value pairs. (keywords are simply name/value pairs whose name is "keyword").
Keep in mind that this code requires the Visual Studio SDK (not just the regular VS install) in order to build, in order to get the Microsoft.VisualStudio.Shell.Interop, Microsoft.VisualStudio.Shell, and Microsoft.VisualStudio.OLE.Interop namespaces (which you'll need to add as references in your addin project).
OK, have fun and good luck!
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
using System.Globalization;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.OLE.Interop;
using System.Collections.Generic;
public class HelpAttribute
{
public string Name;
public string Value;
public VSUSERCONTEXTPRIORITY Priority;
public VSUSERCONTEXTATTRIBUTEUSAGE Usage;
}
public class HelpContext2 : List<HelpAttribute>
{
public static HelpContext2 GetHelpContext(DTE2 dte)
{
// Get a reference to the current active window (presumably a code editor).
Window activeWindow = dte.ActiveWindow;
// make a few gnarly COM-interop calls in order to get Help Context
Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)activeWindow.DTE;
Microsoft.VisualStudio.Shell.ServiceProvider serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(sp);
IVsMonitorUserContext contextMonitor = (IVsMonitorUserContext)serviceProvider.GetService(typeof(IVsMonitorUserContext));
IVsUserContext userContext;
int hresult = contextMonitor.get_ApplicationContext(out userContext);
HelpContext2 attrs = new HelpContext2(userContext);
return attrs;
}
public HelpContext2(IVsUserContext userContext)
{
int count;
userContext.CountAttributes(null, 1, out count);
for (int i = 0; i < count; i++)
{
string name, value;
int priority;
userContext.GetAttributePri(i, null, 1, out priority, out name, out value);
VSUSERCONTEXTATTRIBUTEUSAGE[] usageArray = new VSUSERCONTEXTATTRIBUTEUSAGE[1];
userContext.GetAttrUsage(i, 1, usageArray);
VSUSERCONTEXTATTRIBUTEUSAGE usage = usageArray[0];
HelpAttribute attr = new HelpAttribute();
attr.Name = name;
attr.Value = value;
attr.Priority = (VSUSERCONTEXTPRIORITY)priority;
attr.Usage = usage; // name == "keyword" ? VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Lookup : VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Filter;
this.Add(attr);
}
}
public string CaseSensitiveKeyword
{
get
{
HelpAttribute caseSensitive = Keywords.Find(attr =>
attr.Usage == VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_LookupF1_CaseSensitive
|| attr.Usage == VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Lookup_CaseSensitive
);
return caseSensitive == null ? null : caseSensitive.Value;
}
}
public List<HelpAttribute> Keywords
{
get
{
return this.FindAll(attr=> attr.Name == "keyword");
}
}
}

C#: Attrbute for intellisense to show method only outside of assembly

Basically what I'm hoping for is something that would work like how the Obsolete attribute works with Intellisense and strikes the method text when typing out the name. What I'm looking for is an attribute that blocks the method from being seen with the assembly it's defined. Kind of like an reverse internal. Using 3.5 by the by.
Yeah sounds odd but if you need the reason why, here it is:
My current solution for lazy loading in entity framework involves having the generated many to one or one to one properties be internal and have a facade? property that is public and basically loads the internal property's value:
public ChatRoom ParentRoom
{
get
{
if(!ParentRoomInnerReference.IsLoaded)
{
ParentRoomInnerReference.Load();
}
return ParentRoomInner;
}
set
{
ParentRoomInner = value;
}
}
Problem with this is if someone tries to use the ParentRoom property in a query:
context.ChatItem.Where(item => item.ParentRoom.Id = someId)
This will blow up since it doesn't know what to do with the facade property when evaluating the expression. This isn't a huge problem since the ParentRoomInner property can be used and queries are only in the entity assembly. (IE no selects and such in the UI assembly) The only situation comes in the entity assembly since it can see both properties and it's possible that someone might forget and use the above query and blow up at runtime.
So it would be nice if there were an attribute or some way to stop the entity assembly from seeing (ie blocked by intellisense) the outward facing properties.
Basically inside the assembly see ParentRoomInner. Outside the assembly see ParentRoom. Going to guess this isn't possible but worth a try.
I do see that there is an attribute
for stopping methods from being
viewable
(System.ComponentModel.EditorBrowsable)
but it's choices are rather slim and
don't really help.
You can use the EditorBrowsableAttribute for this:
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public void MyMethod() {}
One thing to know, though: In c#, you will still get intellisense on the method if it is in the same assembly as the one you are working in. Someone referencing your assembly (or your project, for a project reference) will not see it though. You can also pass EditorBrowsableState.Advanced, and then you will only get intellisense if c# if you clear the HideAdvancedMembers option in Tools Options.
I haven't heard of a good way to do this in plain .NET. But, here are some ideas. Maybe one of them will work, or set you off in a direction that will be helpful.
Use FxCop, probably writing your own rule to make sure ParentRoom isn't called from the asslembly that defined it.
Look into the various post-processing projects for .NET (link design-by-contract).
Write some code inside your ParentRoom getter which will check the stack (using "new Stack()" or "new StackFrame(1)" to figure out whether the caller was from the same assembly. If so, either throw an exception or simply return ParentRoomInner.

DataTable descendant with DebuggerDisplay attribute loses DebuggerVisualizer

I have a descendant of DataTable that has the DebuggerDisplay attribute defined. The default visualizer for DataTable is removed when I add the DebuggerDisplay attribute. If I remove the DebuggerDisplay attribute, the DataTable visualizer returns. I want the default DataTable visualizer and my override for DebuggerDisplay.
Do you guys know how to get it working?
//does not work
//[DebuggerVisualizer("Microsoft.VisualStudio.Debugger.DataSetVisualizer", typeof(DataTable))]
//DebuggerDisplay attribute removes DataTable visualizer. Odd behavior to me.
[DebuggerDisplay("{TableName}, Count = {Rows.Count}, {GetColumnNames()}")]
public class MyTable<T> : DataTable where T : class{}
Just to clarify, I've got no idea why deriving and specifying a different attribute disables the visualizer.
I've tried something similar and nothing stops you from having both DebuggerDisplay and DebuggerVisualizer applied to a type. The image below shows both, the left circle is the debugger visualizer, the right circle is the debugger display:
However, you may get issues with trying to use the DataSetVisualizer type on your class. It took a lot of jiggery-pokery, but I ended up with the following definition for my class:
[Serializable]
[DebuggerVisualizer(typeof(EnhancedDataSetVisualizer.DataSetVisualizer),
typeof(EnhancedDataSetVisualizer.DataSetVisualizerSource))]
[DebuggerDisplay("{Name}")]
public sealed class SpecFlowTableWrapper : DataSet
{
// Body omitted, not important.
}
I was constantly having to change what arguments I specified in DebuggerVisualizer. It turns out the missing piece for me was specifying the VisualizerObjectSource.
I then get the debugger display showing my data set name, and when I click the magnifying glass it loads the DataSetVisualizer.
The important part in all this is two references:
Microsoft.VisualStudio.Debugger.DataSetVisualizer
This contains the DataSetVisualizer and DataSetVisualizerSource types.
Microsoft.VisualStudio.DebuggerVisualizers
This is a dependency of the other reference.
The second item is generally available in the "Add References..." dialog in Visual Studio, however the first item is found in the VS installation directory.
For me (VS version may vary):
C:\Program Files (x86)\Microsoft Visual Studio
10.0\Common7\Packages\Debugger\Visualizers\
Called:
Microsoft.VisualStudio.Debugger.DataSetVisualizer.dll
Make sure "Copy Local" is true for the first reference as well (it should be true by default anyway). Note that for debugging, this reference is now a dependency so you need to make sure it is in the working directory of whatever project you are debugging, otherwise you get VS debugger errors.
Re-build, start debugger, enjoy. Sorry it was 2 years late.

Categories

Resources