コード モデルを使用したコードの調査 (Visual Basic)
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 Basic)」および「Visual C++ コード モデル」を参照してください。
Visual Studio コア コード モデルに関するメモ
言語に固有な Microsoft 言語実装のモデリングを実行するのは、Visual C++ コード モデルの実装のみです。
一部の言語では、Visual Studio コード モデル全体が実装されません。ヘルプ トピックは、例外が存在する場合にその例外を示します。コード モデルの実装間の相違点の多くは、各言語で機能が異なることが原因です。たとえば、トップレベルの関数定義は Visual C++ でしか機能しないため、Visual Basic または Visual C# では、CodeNamespace オブジェクトに関数を追加できません。
Description
このアドインでは、Visual Studio ファイルのさまざまなコード要素を処理します。この例を実行するには、Visual Studio コード エディターでソース コード ファイルを開いておく必要があります。この例の実行方法の詳細については、「方法 : オートメーション オブジェクト モデルのコード例をコンパイルおよび実行する」を参照してください。
コード
' Add-in code.
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)
' Pass the applicationObject member variable to the code example.
OutlineCode(_applicationObject)
End Sub
Sub OutlineCode(ByVal dte As DTE2)
Dim fileCM As FileCodeModel2 _
= CType(dte.ActiveDocument.ProjectItem.FileCodeModel, _
FileCodeModel2)
Dim elts As CodeElements
elts = fileCM.CodeElements
Dim elt As CodeElement2
Dim i As Integer
MsgBox("About to walk top-level elements ...")
For i = 1 To fileCM.CodeElements.Count
elt = CType(elts.Item(i), CodeElement2)
CollapseElt(elt, elts, i)
Next
End Sub
Sub CollapseElt(ByVal elt As CodeElement2, ByVal elts As _
CodeElements, ByVal loc As Long)
Dim epStart As EditPoint2
Dim epEnd As EditPoint2
epStart = CType(elt.StartPoint.CreateEditPoint, EditPoint2)
' Do this since we move it later.
epEnd = CType(elt.EndPoint.CreateEditPoint, EditPoint2)
epStart.EndOfLine()
If ((elt.IsCodeType) And (elt.Kind <> _
vsCMElement.vsCMElementDelegate)) Then
MsgBox("Got type but not a delegate, named : " & elt.Name)
Dim ct As CodeType
ct = CType(elt, CodeType)
Dim mems As CodeElements
mems = ct.Members
MsgBox("Set mems = ct.members")
Dim i As Integer
For i = 1 To ct.Members.Count
CollapseElt(CType(mems.Item(i), CodeElement2), mems, i)
Next
ElseIf (elt.Kind = vsCMElement.vsCMElementNamespace) Then
MsgBox("Got a namespace, named: " & elt.Name)
Dim cns As CodeNamespace
cns = CType(elt, CodeNamespace)
MsgBox("set cns = elt, named: " & cns.Name)
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(CType(mems_vb.Item(i), CodeElement2), _
mems_vb, i)
Next
End If
End Sub
変更されるコード モデル要素値
なんらかの編集よって、クラス、構造体、関数、属性、デリゲートなどのコード モデル要素に割り当てられた値が変更されることがあります。したがって、これらの値が静的なものであると想定することはできません。
たとえば、コード モデル要素をローカル変数に割り当て、このローカル変数のプロパティ値を設定した場合、あとでローカル変数を参照すると有効なコード モデル要素が含まれていないことがあります。実際、別のコード モデル要素が含まれていることもあります。
CodeFunction 変数に割り当て、CodeFunction の Name プロパティに "YourFunction" の値を設定した "MyFunction" という名前の関数を含むクラスの例を考えてみます。このように変数を割り当てると、ローカル変数が同じ CodeFunction を示すとは限りません。したがって、このプロパティの値を参照すると E_FAIL という結果が返されることがあります。
この問題に対処するときに推奨される方法は、プロパティの値を参照する前に、明示的にローカル変数を正しいコード モデル要素に再割り当てすることです。これを行うための方法を次に示します。コードはアドインの形式です。
Description
このアドインの例は、適切な値が返されるように 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#)