Discovering Code by Using the Code Model (Visual C#)

Visual Studio add-ins are deprecated in Visual Studio 2013. You should upgrade your add-ins to VSPackage extensions. For more information about upgrading, see FAQ: Converting Add-ins to VSPackage Extensions.

The Visual Studio code model offers automation clients the ability to discover code definitions in a project and modify those code elements. The code model automatically updates all 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 among the members. The code model allows automation clients to avoid implementing a parser for Visual Studio languages in order to discover the high-level definitions in a project, such as classes, interfaces, structures, methods, properties, and so on.

The Visual Studio core 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. Visual C++ offers an extended version of the core code model that is targeted towards Visual C++ projects. For information about this, see Visual C++ Code Model.

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 find a project's code by using the project model to visit each project item, and then check to see if the project item contains code by using FileCodeModel. If a project item contains code elements, those elements can return objects from the editor and the code model can use the text editor automation model to modify code or perform localized parsing. By using the Editor object model, you can request the code element containing the editor's insertion point or a TextPoint object at the function or class level.

The primary entry point to the Visual Studio 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 CodeElements level and at the class or interface level that returns the members of these objects. Each element of a CodeElements collection is a CodeElement2 object, and each CodeElement2 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

Visual C++ provides an extension to the core code model to target Visual C++-specific code. For example, if Language indicates that a given code element is a Visual C++ code model object, and Kind = vsCMElementClass, then you can choose to either QueryInterface (QI) for CodeClass from the Visual Studio code model or QI for VCCodeClass from the Visual C++ language-specific code model. For more information on the Visual C++-specific code model, see How to: Manipulate Code by Using the Visual C++ Code Model (Visual C#) and Visual C++ Code Model.

Notes for the Visual Studio Code Model

  • Only the Visual C++ code model implementation performs language-specific modeling of the Microsoft language implementations.

  • Some languages do not implement the entire Visual Studio code model. Help topics point out exceptions when they exist. Most differences between implementations of the code model are due to functional differences between the languages. For example, you cannot add functions to CodeNamespace objects in Visual Basic or Visual C# because only Visual C++ features top-level function definitions.

Description

This Add-in walks through the code elements of a Visual Studio file. In order to run the example, you must have a code file open in the Visual Studio code editor. For more information on how to run the examples, see How to: Compile and Run the Automation Object Model Code Examples.

Code

// Add-in code.
using System.Windows.Forms;
public void OnConnection(object application,
 Extensibility.ext_ConnectMode connectMode, object addInInst, ref
 System.Array custom)
{
    _applicationObject = (_DTE2)application;
    _addInInstance = (AddIn)addInInst;
    // Pass the applicationObject member variable to the code example.
    OutlineCode((DTE2)_applicationObject); 
}

public void OutlineCode( DTE2 dte ) 
{ 
    FileCodeModel fileCM = 
      dte.ActiveDocument.ProjectItem.FileCodeModel; 
    CodeElements elts = null; 
    elts = fileCM.CodeElements; 
    CodeElement elt = null; 
    int i = 0; 
    MessageBox.Show( "about to walk top-level code elements ..."); 
    for ( i=1; i<=fileCM.CodeElements.Count; i++ ) 
    { 
        elt = elts.Item( i ); 
        CollapseElt( elt, elts, i ); 
    } 
} 

public void CollapseElt( CodeElement elt, CodeElements elts, long loc ) 
{ 
    EditPoint epStart = null; 
    EditPoint epEnd = null; 
    epStart = elt.StartPoint.CreateEditPoint(); 
    // Do this because we move it later.
    epEnd = elt.EndPoint.CreateEditPoint(); 
    epStart.EndOfLine(); 
    if ( ( ( elt.IsCodeType ) & ( elt.Kind !=
      vsCMElement.vsCMElementDelegate ) ) ) 
    { 
        MessageBox.Show( "got type but not a delegate, 
          named : " + elt.Name); 
        CodeType ct = null; 
        ct = ( ( EnvDTE.CodeType )( elt ) ); 
        CodeElements mems = null; 
        mems = ct.Members; 
        int i = 0; 
        for ( i=1; i<=ct.Members.Count; i++ ) 
        { 
            CollapseElt( mems.Item( i ), mems, i ); 
        } 
    } 
    else if ( ( elt.Kind == vsCMElement.vsCMElementNamespace ) ) 
    { 
        MessageBox.Show( "got a namespace, named: " + elt.Name); 
        CodeNamespace cns = null; 
        cns = ( ( EnvDTE.CodeNamespace )( elt ) ); 
        MessageBox.Show( "set cns = elt, named: " + cns.Name); 

        CodeElements mems_vb = null; 
        mems_vb = cns.Members; 
        MessageBox.Show( "got cns.members"); 
        int i = 0; 

        for ( i=1; i<=cns.Members.Count; i++ ) 
        { 
            CollapseElt( mems_vb.Item( i ), mems_vb, i ); 
        } 
    } 
}

Code Model Element Values Can Change

The assigned values of code model elements, such as classes, structs, functions, attributes, delegates, and so forth, may change after you make certain kinds of edits. Consequently, you cannot assume that the values will remain static.

If you assign a code model element to a local variable, for example, and you then set a property value for this local variable, the local variable may not contain a valid code model element when you reference it later. In fact, it may even contain a different code model element.

Consider a class containing a function named "MyFunction" which is assigned to a CodeFunction variable, and then the Name property of the CodeFunction is set to the value "YourFunction." After this variable assignment, you are no longer guaranteed that your local variable represents the same CodeFunction. Subsequently, accessing the value of the property may return E_FAIL as a result.

The recommended approach for dealing with this situation is to explicitly reassign your local variable to the correct code model element before accessing its property values. The following shows an example of how to do this. (The code is in the form of an add-in.)

Description

This add-in demonstrates the correct way to access values for CodeElements so that the proper value is retrieved. For more information on how to run the examples, see How to: Compile and Run the Automation Object Model Code Examples.

Code

[Visual Basic]

Public Sub OnConnection(ByVal application As Object, ByVal _
  connectMode As ext_ConnectMode, ByVal addInInst As Object, _
  ByRef custom As Array) Implements IDTExtensibility2.OnConnection
    _applicationObject = CType(application, DTE2)
    _addInInstance = CType(addInInst, AddIn)
    ReassignValue(_applicationObject)
End Sub

Sub ReassignValue(ByVal dte As DTE2)
    ' Before running, create a new Windows application project,
    ' and then add a function to it named MyFunction.
    Try
        Dim myFCM As FileCodeModel = _
          dte.ActiveDocument.ProjectItem.FileCodeModel
        ' Change the MyFunction name in Form1 class to
        ' the name, OtherFunction.
        Dim myClass1 As CodeClass = _
          CType(myFCM.CodeElements.Item("Form1"), CodeClass2)
        Dim myFunction As CodeFunction = _
          CType(myClass1.Members.Item("MyFunction"), CodeFunction2)
        myFunction.Name = "OtherFunction"
        myFunction = CType(myClass1.Members.Item("OtherFunction"), _
          CodeFunction2)
    Catch ex As Exception
        MsgBox(ex.ToString)
    End Try
End Sub

[C#]

public void OnConnection(object application, ext_ConnectMode 
  connectMode, object addInInst, ref Array custom)
{
    _applicationObject = (DTE2)application;
    _addInInstance = (AddIn)addInInst;
    ReassignValue(_applicationObject);
}

// Before running, create a new Windows application project,
// and then add a function to it named MyFunction.
public void ReassignValue(DTE2 dte)
{
    try
    {
        FileCodeModel myFCM = 
          dte.ActiveDocument.ProjectItem.FileCodeModel;
        // Change the MyFunction name in Form1 class to
        // the name, OtherFunction.
        CodeClass myClass1 = 
          (CodeClass2)myFCM.CodeElements.Item("Form1");
        CodeFunction myFunction = 
          (CodeFunction2)myClass1.Members.Item("MyFunction");
        myFunction.Name = "OtherFunction";
        myFunction = 
          (CodeFunction2)myClass1.Members.Item("OtherFunction");
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
    }
}

Note

Setting the properties of child elements of your code model element does not exhibit this behavior. Only properties that directly affect the CodeElement — such as the name of the element, the type of a function, the signature of a method, etc. — exhibit this non-deterministic behavior.

Also, this example works only if the new name of the CodeElement is unique among its siblings. This is because the Item property returns the first match, which does not work for overloaded methods/properties, partial classes, or namespaces with the same name.

See Also

Tasks

How to: Compile Example Code for Visual C++ Code Model Extensibility

How to: Create an Add-In

Walkthrough: Creating a Wizard

How to: Manipulate Code by Using the Visual C++ Code Model (Visual C#)

How to: Manipulate Code by Using the Visual C++ Code Model (Visual Basic)

Concepts

Discovering Code by Using the Code Model (Visual Basic)

Visual C++ Code Model

Automation Object Model Chart

Other Resources

Creating and Controlling Environment Windows

Creating Add-ins and Wizards

Automation and Extensibility Reference