英語で読む

次の方法で共有


コード モデルを使用したコードの調査 (Visual C#)

Visual Studio コード モデルには、オートメーション クライアントでプロジェクト内のコード定義を調査し、コード要素を変更する機能が用意されています。 コード エディターで内容が変更された場合、コード モデルによって、参照されているすべてのオブジェクトが自動的に更新されます。 たとえば、クラス オブジェクトを参照している場合に、別のユーザーが新しい関数を追加すると、その追加された関数はメンバーの中に表示されます。 コード モデルを使用すると、オートメーション クライアントでは、プロジェクト内でクラス、インターフェイス、構造体、メソッド、プロパティなどの高度な定義を調査するために、Visual Studio 言語のパーサーを実装する必要がなくなります。

Visual Studio コア コード モデルでは、コードの言語固有の部分はありません。たとえば、関数内のステートメント用のオブジェクト モデルは用意されておらず、パラメーターの詳細も指定されていません。 パラメーターに関しては、コード モデルでは、パラメーターの型と名前だけが公開されます。パラメーターが入力パラメーター、出力パラメーター、省略可能なパラメーターなどのうちどれであるかを示す情報は提供されません。 Visual C++ には、Visual C++ プロジェクトを対象とするコア コード モデルの拡張バージョンが用意されています。 マクロのデバッグについては、「Visual C++ コード モデル」を参照してください。

コード モデルによるコードの調査と編集

コード モデルは、主にテキスト ベースであり、プロジェクトのプログラムやコードはテキスト ファイルに格納されています。 プロジェクト モデルを使用してプロジェクトのコード内を検索し、各プロジェクト項目を参照します。その後、FileCodeModel を使用して、プロジェクト項目にコードが含まれているかどうかをチェックします。 プロジェクト項目にコード要素が含まれる場合、コード要素はエディターからオブジェクトを返すことができます。また、コード モデルでは、テキスト エディターのオートメーション モデルを使用して、コードを変更したりローカライズされた解析を実行したりできます。 Editor オブジェクト モデルを使用すると、エディターのカーソル位置を含むコード要素、または関数レベルやクラス レベルの TextPoint オブジェクトを要求できます。

Visual Studio コア コード モデルへの主なエントリ ポイントは、CodeModel オブジェクトです。 標準の CodeElements コレクションは、コード モデルの複数の場所で使用されています。 このコレクションは、CodeElements レベルおよびクラス レベルまたはインターフェイス レベルで、これらのオブジェクトのメンバーを返します。 CodeElements コレクションの各要素は CodeElement2 オブジェクトであり、各 CodeElement2 オブジェクトには Kind プロパティがあります。このプロパティでは、このオブジェクトの型がクラス、インターフェイス、構造体、関数、プロパティ、変数などのどの型であるかを識別します。

言語に固有のコード モデル

Visual C++ には、コア コード モデルの拡張機能が用意されており、Visual C++ 固有のコードを対象とします。 たとえば、Language で指定されたコード要素が Visual C++ コード モデル オブジェクトであり、Kind = vsCMElementClass である場合は、Visual Studio コード モデルから CodeClass の QueryInterface (QI) を使用するか、Visual C++ 言語固有のコード モデルから VCCodeClass の QI を使用するかを選択できます。 Visual C++ 固有のコード モデルの詳細については、「方法 : Visual C++ コード モデルを使用したコード操作 (Visual C#)」および「Visual C++ コード モデル」を参照してください。

Visual Studio コード モデルに関するメモ

  • 言語に固有な Microsoft 言語実装のモデリングを実行するのは、Visual C++ コード モデルの実装のみです。

  • 一部の言語では、Visual Studio コード モデル全体が実装されません。 ヘルプ トピックは、例外が存在する場合にその例外を示します。 コード モデルの実装間の相違点の多くは、各言語で機能が異なることが原因です。 たとえば、トップレベルの関数定義は Visual C++ でしか機能しないため、Visual Basic または Visual C# では、CodeNamespace オブジェクトに関数を追加できません。

説明

このアドインでは、Visual Studio ファイルのコード要素を処理します。 この例を実行するには、Visual Studio コード エディターでコード ファイルを開いておく必要があります。 この例の実行方法の詳細については、「方法 : オートメーション オブジェクト モデルのコード例をコンパイルおよび実行する」を参照してください。

コード

// 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 ); 
        } 
    } 
}

変更されるコード モデル要素値

なんらかの編集よって、クラス、構造体、関数、属性、デリゲートなどのコード モデル要素に割り当てられた値が変更されることがあります。 したがって、これらの値が静的なものであると想定することはできません。

たとえば、コード モデル要素をローカル変数に割り当て、このローカル変数のプロパティ値を設定した場合、あとでローカル変数を参照すると有効なコード モデル要素が含まれていないことがあります。 実際、別のコード モデル要素が含まれていることもあります。

CodeFunction 変数に割り当て、CodeFunction の Name プロパティに "YourFunction" の値を設定した "MyFunction" という名前の関数を含むクラスの例を考えてみます。 このように変数を割り当てると、ローカル変数が同じ CodeFunction を示すとは限りません。 したがって、このプロパティの値を参照すると E_FAIL という結果が返されることがあります。

この問題に対処するときに推奨される方法は、プロパティの値を参照する前に、明示的にローカル変数を正しいコード モデル要素に再割り当てすることです。 これを行うための方法を次に示します。 コードはアドインの形式です。

説明

このアドインの例は、適切な値が返されるように CodeElements の値を参照する正しい方法を示しています。 この例の実行方法の詳細については、「方法 : オートメーション オブジェクト モデルのコード例をコンパイルおよび実行する」を参照してください。

コード

[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);
    }
}

注意

コード モデル要素の子要素のプロパティを設定した場合は、このような動作は起きません。 要素名、関数の型、メソッドのシグネチャなど、CodeElement に直接影響を及ぼすプロパティだけが、 このような不確定な動作を示します。

また、この例が適用されるのは、CodeElement の新しい名前が兄弟間で一意の名前になる場合だけです。 これは、Item プロパティが最初の一致を返してくるためですが、オーバーロードされたメソッドやプロパティ、部分クラス、または同じ名前を持つ名前空間には適用されません。

参照

処理手順

方法 : Visual C++ コード モデル機能拡張のプログラム例をコンパイルする

方法 : アドインを作成する

チュートリアル : ウィザードの作成

方法 : Visual C++ コード モデルを使用したコード操作 (Visual C#)

方法 : Visual C++ コード モデルを使用したコード操作 (Visual Basic)

概念

コード モデルを使用したコードの調査 (Visual Basic)

Visual C++ コード モデル

オートメーション オブジェクト モデルの階層図

その他の技術情報

環境ウィンドウの作成と制御

アドインおよびウィザードの作成

オートメーションと機能拡張のリファレンス