Export (0) Print
Expand All

Discovering Code with the Code Model

The Visual Studio .NET code model offers automation clients the ability to discover code definitions in a project and modify those code elements. The code model automatically updates any referenced objects when modifications are made in the code editor. For example, if you reference a Class object and a user later adds a new function, it is listed when you iterate the members. The code model allows automation clients to avoid having to implement a parser for Visual Studio .NET languages in order to discover the high-level definitions in a project, such as classes, interfaces, structures, methods, properties, and so on.

The code model avoids language-specific areas of code, so it does not, for example, provide an object model for statements in functions or give full details on parameters. For parameters, the code model exposes only the type and name of the parameter, and no information is provided about whether the parameter is input, output, optional, and so forth.

Examining and Editing Code with the Code Model

The code model is primarily text-based in that the program or code in the project is stored in text files. You can navigate to a project's code using the project model to visit each project item, and then check to see if the project item contains code by using ProjectItem.FileCodeModel. If a project item contains code elements, those elements can return objects from the editor and the code model can use the editor's text object model to modify code or perform localized parsing. Using the Editor object model, you can request the code element containing the editor's insertion point or an EditPoint object at the function or class level.

The primary entry point to the Visual Studio .NET core code model is the CodeModel object. A general CodeElements collection is used in several places in the code model. There is one at the FileCodeModel.CodeElements level and at the class or interface level that returns the members of these objects. Each element of a CodeElements collection is a CodeElement object, and each CodeElement object has a Kind property that identifies its type, whether it is a class, interface, struct, function, property, variable, and so forth.

Language-Specific Code Models

Some Visual Studio .NET languages provide a richer, alternative, language-specific model. For example, if CodeElement.Language indicates that a given code element is a Visual C++ .NET code model object, and CodeElement.Kind = vsCMElementClass, then you can choose to either QueryInterface for CodeClass from the Visual Studio .NET code model or QueryInterface for VCCodeClass from the Visual C++ .NET language-specific code model.

Notes for the Visual Studio .NET Code Model

  • Only the Visual C++ .NET code model implementation performs language-specific modeling of the Microsoft language implementations.
  • The code model for implementation Visual Basic. NET is read-only.
  • Some implementations do not implement the entire Visual Studio .NET code model, so you must pay attention to Help topics for whether all languages are required to implement certain members. For example, only Visual C# .NET returns DocComment properties, and only Visual C++ .NET implements any methods on the CodeModel object for adding code. Most other differences between the code models are due to functional differences between the languages. For example, you cannot add functions to CodeNamespace objects in Visual Basic .NET or Visual C# .NET because only Visual C++ .NET features top-level function definitions.

Code Object Model Sample

The following VSA code demonstrates how to use the VCCodeElement and VCCodeElements code model objects and members to manipulate a Visual C++ .NET project.

Sub OutlineCode()
    Dim fileCM As FileCodeModel
    fileCM = ActiveDocument.ProjectItem.FileCodeModel
    Dim elts As CodeElements
    elts = fileCM.CodeElements
    Dim elt As CodeElement
    Dim i As Integer
    msgbox("about to walk top-level elts ...")
    For i = 1 To fileCM.CodeElements.Count
        elt = elts.Item(i)
        CollapseElt(elt, elts, i)
    Next
End Sub

Sub CollapseElt(ByVal elt As CodeElement, ByVal elts As CodeElements, loc As Long)
    Dim epStart As EditPoint
    Dim epEnd As EditPoint
    epStart = elt.StartPoint.CreateEditPoint
    epEnd = elt.EndPoint.CreateEditPoint 'Do this since we move it later.
    epStart.EndOfLine()
    If ((elt.IsCodeType) And (elt.Kind <> vsCMElement.vsCMElementDelegate)) Then
        msgbox("got type but not a delegate")
        Dim ct As CodeType
        ct = elt
        msgbox("set ct = elt")
        Dim mems As CodeElements
        mems = ct.Members
        msgbox("set mems = ct.members")
        Dim i As Integer
        For i = 1 To ct.Members.Count
            CollapseElt(mems.Item(i), mems, i)
        Next
    ElseIf (elt.Kind = vsCMElement.vsCMElementNamespace) Then
        msgbox("got a namespace")
        Dim cns As CodeNamespace
        cns = elt
        msgbox("set cns = elt")
        Dim mems_vb As CodeElements
        mems_vb = cns.Members
        msgbox("got cns.members")
        Dim i As Integer
        
        For i = 1 To cns.Members.Count
            CollapseElt(mems_vb.Item(i), mems_vb, i)
        Next
    End If
    msgbox("might outline " & elt.name)
    If (epStart.LessThan(epEnd)) Then
        msgbox("start less than end, outlining ...")
        Loc = loc + 1
        If (loc <= elts.count) Then
            epEnd.MoveToPoint(elts.Item(loc).StartPoint)
            epEnd.LineUp()
            epEnd.EndOfLine()
        End If
        epStart.OutlineSection(epEnd)
    End If
End Sub
Note   The code below assumes that a Visual C++ .NET project is open.
Imports EnvDTE
Imports System.Diagnostics
Imports Microsoft.VisualStudio.VCCodeModel

Public Module Module1
'*************************************************************************
'*************************************************************************
'VCCodeElement
'*************************************************************************
'*************************************************************************
    Sub test()
        Dim vcCM As VCCodeModel
        Dim vcCodeElement As VCCodeElement
        vcCM = DTE.Solution.Item(1).CodeModel
        vcCodeElement = vcCM.CodeElements.Item("MyClass")
        AddCommentAtStart(vcCodeElement)
        'AddComment(vcCodeElement)
    End Sub

    'Shows how to get a VCCodeElement
    Sub GetVCCodeElement()
        Dim vcCM As VCCodeModel
        Dim vcCodeElement As VCCodeElement
        vcCM = DTE.Solution.Item(1).CodeModel
        vcCodeElement = vcCM.AddClass("MyClass", "MyClass.h")
    End Sub

    'Collection
    'Takes a VCCodeElement as a parameter and displays the names of all 
    'the code elements
    'that are declared at the same scope.
    Sub DisplaySiblings(ByVal vcCodeElement As VCCodeElement)
        Dim sibling As VCCodeElement
        For Each sibling In vcCodeElement.Collection
            MsgBox(sibling.Name)
        Next
    End Sub

    'DisplayName
    'Shows the DisplayName of a function which includes the parameter 
    'names.
    Sub DisplayName()
        Dim vcCM As VCCodeModel
        Dim vcCodeElement As VCCodeElement
        vcCM = DTE.Solution.Item(1).CodeModel
        vcCodeElement = vcCM.AddFunction("MyFunction", "File.h", vsCMFunction.vsCMFunctionFunction, "void")
        MsgBox(vcCodeElement.DisplayName)
    End Sub

    'EndPointOf
    'Adds a comment at the end of a code element declaration
    Sub AddCommentAtEnd(ByVal vcCodeElement As VCCodeElement)
        Dim textPoint As TextPoint
        textPoint = vcCodeElement.EndPointOf(vsCMPart.vsCMPartWhole)
        textPoint.CreateEditPoint().Insert("/*Comment*/")
    End Sub

    'IsInjected
    'Goes through all the global VCCodeElements in a file and displays
    'the name of those objects that are injected by attribute expansions.
    Sub InjectedObjects()
        Dim vcCM As VCCodeModel
        Dim vcCodeElement As VCCodeElement
        vcCM = DTE.Solution.Item(1).CodeModel
        For Each vcCodeElement In vcCM.CodeElements
            If (vcCodeElement.IsInjected) Then
                MsgBox(vcCodeElement.Name + " is injected by an attribute expansion.")
            End If
        Next
    End Sub

    'IsReadOnly
    'Verifies if the file is read-only before adding a comment to 
    'the code element.
    Sub AddComment(ByVal vcCodeElement As VCCodeElement)
        If (Not vcCodeElement.IsReadOnly) Then
            vcCodeElement.Comment = "This is a comment."
        End If
    End Sub

    'Location Property
    'Displays the file where each top level code element is declared.
    Sub DisplayLocation()
        Dim vcCM As VCCodeModel
        Dim vcCodeElement As VCCodeElement
        vcCM = DTE.Solution.Item(1).CodeModel
        For Each vcCodeElement In vcCM.CodeElements
            MsgBox(vcCodeElement.Name + " is declared in " + vcCodeElement.Location)
        Next
    End Sub

    'Project Property
    'StartPointOf Property
    'Adds a comment before the VCCodeElement declaration
    Sub AddCommentAtStart(ByVal vcCodeElement As VCCodeElement)
        Dim textPoint As TextPoint
        textPoint = vcCodeElement.StartPointOf(vsCMPart.vsCMPartWhole)
        textPoint.CreateEditPoint().Insert("/*Comment*/")
    End Sub

    'IsSelf Method
    'Receives two code elements if they represent the same object it 
    'displays a message.
    Sub IsSameObject(ByVal codeElement1 As VCCodeElement, ByVal codeElement2 As VCCodeElement)
        If (codeElement1.IsSelf(codeElement2)) Then
            MsgBox(codeElement1.Name + " and " + codeElement2.Name + " represent the same object.")
        End If
    End Sub

    'ValidateCodeElementName Method
    'It receives a class name, if it is valid then adds a class with that 
    'name.
    Sub AddClass(ByVal vcCM As VCCodeModel, ByVal className As String)
        If (vcCM.ValidateCodeElementName(className, vsCMElement.vsCMElementClass)) Then
            vcCM.AddClass(className, "File.h")
        End If
    End Sub

    'ValidateMember Method
    'It receives a class, a method name and a type, if the name is valid 
    'it then adds a method with that name
    Sub AddMethod(ByVal vcCM As VCCodeModel, ByVal classElement As VCCodeClass, ByVal methodName As String, ByVal type As String)
        If (vcCM.ValidateMember(methodName, vsCMElement.vsCMElementFunction, type)) Then
            classElement.AddFunction(methodName, vsCMFunction.vsCMFunctionFunction, type)
        End If
    End Sub
    
'*************************************************************************
'*************************************************************************
'VCCodeElements
'*************************************************************************
'*************************************************************************
    'This is a place where Visual C++ .NET differs from Visual Studio .NET.
    'Item is supposed to return an error if the element was not found, 
    'however we just return NULL. Find was supposed to look for an element 
    'and if it was not present just return NULL, so they are both
    'essentially the same for the VCCodeModel

    'Find
    'Looks for the THIS_FILE variable in the file stdafx.h. If it can't 
    'find it, then it adds it.
    Sub AddThisFile()
        Try
            Dim vcCM As VCFileCodeModel
            Dim vcCodeElements As VCCodeElements
            vcCM = CType(DTE.Solution.Item(1).ProjectItems.Item("stdafx.h"), VCFileCodeModel)
            vcCodeElements = vcCM.CodeElements
            If (vcCodeElements.Find("THIS_FILE") Is Nothing) Then
                Dim codeVariable As VCCodeVariable
                codeVariable = vcCM.AddVariable("THIS_FILE", "char")
            End If
        catch e as System.Exception
            MsgBox(e.Message + e.StackTrace)
        End Try
    End Sub

    'Item
    'Displays the name of all the top level elements
    Sub FindItem()
        Dim vcCM As VCCodeModel
        Dim vcCodeElements As VCCodeElements
        vcCM = DTE.Solution.Item(1).CodeModel
        vcCodeElements = vcCM.CodeElements
        Dim i As Integer
        For i = 1 To vcCodeElements.Count
            MsgBox(vcCodeElements.Item(i))
        Next
    End Sub
End Module

See Also

Visual C++ Code Model | Web Forms Code Model | Samples for Code Model Extensibility | Creating and Controlling Environment Windows | Creating Add-Ins and Wizards | Creating an Add-In | Creating a Wizard | Automation and Extensibility Reference | Automation Object Model Chart

Show:
© 2015 Microsoft