How to get class properties and methods from a selected text in code Window?

Answered How to get class properties and methods from a selected text in code Window?

  • Friday, March 05, 2010 5:08 PM
     
     
    Hi everybody! 
    I searched a lot before asking this question, but i wasn't able to find the right answer.
    I have to implement an add-in that, from a selected text in my code window (VB 2008), has to find every property and method of the selected class.

    I.E.

    Public class MyClass
    {
    string a;
    int b;

    // and so on
    }

    public class TestClass
    {
    MyClass mc = new MyClass();
    ...
    ...
    ...
    mc.a="test";
    ...
    ...
    }

    I mean that, if i select mc in TestClass, i have to be able to display all properties (a, b and so on) and methods of MyClass (suppose that MyClass is not part of the same namespace or project, so think about MyClass as string or int or everithing you want).

    I will put this code in a button_click event on a context menu, but this is not important

    I hope i explained my question well

    Thanks, Luca

All Replies

  • Friday, March 05, 2010 6:44 PM
     
     Answered

    Hi Luca!

    As I know, there is no easy way to "find out what mc is". I mean, there is no code model for code inside method - just namespaces, classes, properties, methods etc. So you will have to parse (may somebody knows the easier way) the meaning of mc to find out that mc is a variable of type with name MyClass. From there. You can use EnvDTE.CodeModel or  FileCodeModel (or EnvDTE80.CodeModel2 and FileCodeModel) to find the class MyClass(does not work properly for each type of file - for example there are some problems with code behind files for aspx pages inside web site project). For the beginning You can use a library called LinqToCodeModel from http://code.msdn.microsoft.com/LinqToCodeModel

  • Sunday, March 07, 2010 5:45 PM
     
     

    Today I was looking for something in VS extensibility classes and found the assembly Microsoft.VisualStudio.CSharp.Services.Language.dll.
    Some namespaces and classes seem promising to solve the problem with the code inside method. In particular, I found this:
    Microsoft.VisualStudio.CSharp.Services.Language.CallHierarchy.SearchEngine.Utilities class - seems to contain some useful methods You can use to determine, "what mc is". There is a method called GetMemberAtLocation, which seems to be useful. Unfortunately I have no example how to use it.

    Microsoft.VisualStudio.CSharp.Services.Language.CodeModel - contains code model for CSharp, which seems to be powerful enough to go inside the method body, but I had no chance to play with it yet - so I don't know for sure.

  • Monday, March 08, 2010 10:23 AM
     
     
    Many thanks fedi, i will try all the solutions. 
  • Saturday, March 13, 2010 11:41 PM
     
     Answered
    I figured out, how to access the inside body of methods.

    As I mentioned, the previous assemblies may be helpful. After some playing I get this:

    [

     

    DllImport("CSLangSvc.dll", PreserveSig=false)]

     

     

    public static extern void LangService_GetDteProject(ILangService langService, Project dteProject, out IProject project); 

    [DllImport("CSLangSvc.dll", PreserveSig=false)]

     

     

     

    public static extern void LangService_GetInstance(out ILangService langService);


    Using this you can:
    1., Get the Language Service:

    Microsoft.VisualStudio.CSharp.Services.Language.Interop.

     

    ILangService langService;
    LangService_GetInstance (out langService);
    2., Create compiler host using

    Microsoft.RestrictedUsage.CSharp.Compiler.IDE.IDECompilerHost compilerHost=

     

    new Microsoft.RestrictedUsage.CSharp.Compiler.IDE.IDECompilerHost();

    3., Get the IProject

    Microsoft.VisualStudio.CSharp.Services.Language.Interop.

     

    IProject prj;
    Integra.Common.
    VsUtils.LangService_GetDteProject(engine.LanguageService, projectItem.ContainingProject, out prj);
    4., Get the member at given position

    Microsoft.RestrictedUsage.CSharp.Core.

     

    FileName fileName=new Microsoft.RestrictedUsage.CSharp.Core.FileName(projectItem.FileNames[0])

    Microsoft.RestrictedUsage.CSharp.Core.

     

    Position pos=new Microsoft.RestrictedUsage.CSharp.Core.Position(line, column);

     ParseTreeNode

     

     

    leafNode=engine.CompilerHost.Compilers[idx of compiler - there is one for each project (except web site)].GetCompilation().SourceFiles[].GetParseTree().FindLeafNode(pos);

    And thats it.
    After this, You can use Microsoft.VisualStudio.CSharp.Services.Language.CallHierarchy.SearchEngine.Utilities to find further information - or get the insiparation as I did :-).

    • Marked As Answer by LucaPetra Tuesday, March 16, 2010 2:31 PM
    •  
  • Thursday, March 18, 2010 11:03 AM
     
     
    hi fedi, i found Microsoft.VisualStudio.CSharp.Services.Language.dll, it seems to be the right direction but i was unable to find Microsoft.RestrictedUsage assmebly in my visual studio 2008 or in internet. Am i missing something?

    thank you again

    EDIT: I cannot find LangService_GetInstance (out langService) . When i try to use that function i get an exception that tells me "no entry point found...". So i tried to explore the dll and i found that only 9 functions are exported:

    CreateCompilerFactory
    DllCanUnloadNow
    DllGetClassObject
    DllRegisterServer
    DllUnregisterServer
    DllUnregisterServer
    DllUnregisterServer
    VSDllRegisterServer
    InMemoryCompile
    VSDllUnregisterServer

    No one of these functions seems to be useful for my purpose, except the DllGetClassObject one, but i don't know nothing about that. Parameters, type, ecc. 
  • Sunday, March 28, 2010 3:18 PM
     
     Answered

    Hi Luca,

    I'm sorry, my mistake.

    The problem seems to be, in VS 2008. I just tried this in 10.0 assemblies and It seems these namespaces are totally new in VS 2010.

    So now to put this in right place - I was talking about:

    Microsoft.VisualStudio.CSharp.Services.Language, Version 10.0.0.0

    Microsoft.VisualStudio.CSharp.Services.Language.Interop.dll, Version 10.0.0.0

    located in C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\

    I'm afraid, You gonna need VS 2010 to get the things working (the way I was writing about) or use the approach from my first answer for VS 2008 (may some existing code analyser assemblies can be helpful in this).

    Once again - sorry for misleading You down the wrong path for VS 2008.

    • Marked As Answer by LucaPetra Monday, March 29, 2010 11:10 AM
    •  
  • Monday, March 29, 2010 11:10 AM
     
     

    Thank you again fedi, i'm thinking about writing my add-in for VS 2010 if it's easier to extend. 

     

    However, during my research i wondered what kind of dlls are used by common add-ins such resharper or reflector. I think it's impossible that they have to parse the entire file every time, so i think that it has to exist something that can "easily" link different code elements (think about "go to declaration" command or similar). I've not been able to find any of this, but i still believe in it. 

    EDIT: I tried to use LinqToCodeModel but it seems to be just a "shorter" way to use the CodeModel and not an in-depth code analysis (since it is based upon the CodeModel)
    • Edited by LucaPetra Monday, March 29, 2010 3:01 PM
    •  
  • Monday, March 29, 2010 2:55 PM
     
     

    Hi,

    I think, they are actually going through MS-IL - not a nice and pleasant work. You can look inside with reflector, but be carefull with licensing policies. Some static code analyser libraries - like FxCop may help too. They seem to be integral part of VS 2010 installation - licensing might not be a problem (perhaps). Resharper is offering some extensibility, som plugins can be created for it - it might be the way too.

  • Monday, February 07, 2011 8:23 PM
     
     
    Did you ever find a good solution to this? I've been playing around with ParseTreeNode's for a couple of days now, but can't find a way of working out what type a ParseTreeNode expression would compile down to? 
  • Tuesday, February 08, 2011 8:30 AM
     
     

    Hi woodced,

    First I should note, that these assemblies/namespaces in VS 2010 are not directly supported by MS despite they are public (no documentation). This means MS can change them any time they want. So far - SP1 Beta brings nothing disturbing - just minor if (xy) throw Z; changes.

    There is one namespace, which You may check: Microsoft.RestrictedUsage.CSharp.Semantics and the namespace Microsoft.RestrictedUsage.CSharp.Extensions.ExpressionTreeExtensions.

    You can compile the whole type or just one particular method using IExpressionTreeProvided.CompilerMethod/CompileType/CompiletTypeOrNamespace etc. As soon as You get the (IDE)Compilation. I can see You already know that

    Than You can walk the expression tree to find the right information You need. I would recommend Linq or something implementing IExpressionTreeVisitor (derived from ExpressionTreeVisitor for instance) and some Func<Expression, bool> method as the filter for "appropriate" expressions.

  • Tuesday, February 08, 2011 8:39 AM
     
     

    Fedi, 

    Thanks for that info. I had a bit of a play with it last night and it looks like I can make some serious progress there.

    Glad to know you've had a go with SP1 and it doesn't break this stuff. If you've done any decent work with this stuff are there any major pitfalls to look out for?

     

     

  • Tuesday, February 08, 2011 3:41 PM
     
     

    Hi,

    This will be a long one - again :-) But I will try to explain the problems I encountered.

    I am still trying to implement some "code model caching and change tracking" (EnvDTE.CodeModelEvents is just not enough) to avoid traversing the trees again and again. It is more difficult than I thought. First I used CSharpXYZ classes from Microsoft.RestrictedUsage.CSharp.Semantics namespace. But it was too slow for me and there was a bug - retrieving attributes did not work (metod exported from cslangsvc.dll was returning wrong values and therefore I was unable to retrieve attributes using these classes but this may be fixed in SP1). So I had to use parse tree nodes for attributes to workaround this issue.

    Then I tried to use the Microsoft.VisualStudio.CSharp.Services.Language.CodeModel namespace, but this is just some wrapper for native code used with EnvDTE.CodeModel. In fact. The native stuff sits in cslangsvc.dll. Than it is wrapped to mentioned classes and than wrapped to some COM wrapper interface IComWrapper. This is nice because You can use the EnvDTE.CodeModel - retrieve IComWrapper and "unwrap" the managed code model (in few places reflection comes into play) and access directly the CodeModel and (with some reflection) You can access the parse tree.

    I had some problems with CodeModel too. If You are working with WebSite instead of WebApplication each aspx, ascx is built separately and therefore has its own IDECompiler. Its very slow to retrieve FileCodeModel for aspx and ascx (Yes they have one too - contains the code generated at runtime) and their code behind files. And there is a huge amount of "random exceptions" thrown at You while using CodeModel during solution load (some asynchronous stuff) - this was the major problem for me so I am avoiding code model everywhere I can now :-). If You use it only for opened documents, works quite fine, but If You use it on solution load, it is just throwing exceptions here and there and the second time You try it - it works fine and the third time it is broken again.

    Now I'm using ParseTreeNodes for code model stuff and will use Expressions for some advanced "on demand hi-tech stuff" :-) for performance reasons.

    Keep in Mind, that You can not cache these instances directly - when You change something in code - they are just "disposed" and they throw exceptions everywhere. You can use ParseTreeNodePath for keeping track of SOME(classes, methods, properties, Namespaces) parse tree nodes and classes like CSharpXYZReference to keep track of classes used in Semantics namespace

    Summary:

    The CodeModel is full of bugs and problems. Use Semantics namespace content instead (expressions). For low level access use ParseTreeNodes.

    You can look into CallHierarchy namespace for some inspiration :-)

  • Tuesday, February 08, 2011 3:54 PM
     
     

    Fedi, 

     

    Thanks for the really detailed and interesting response. It's great to be able to lean on someone whose had experience with this undocumented stuff.  I'm sure that'll save me loads of builds. 

     

    Looking forward to having a bit of a test this evening. 

     

    Steve

  • Friday, June 01, 2012 8:33 AM
     
     
    thank you fedi24, this issue has puzzled me a couple of mouths.