次の方法で共有


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

更新 : 2007 年 11 月

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);
    }
}
ms228763.alert_note(ja-jp,VS.90).gifメモ :

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

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

参照

処理手順

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

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

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

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

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

概念

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

Visual C++ コード モデル

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

その他の技術情報

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

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

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