Visual Studio の拡張性を勉強しています。MSDNのこのコードは、クラスを持つプロジェクトを含む新しい C# ソリューションを作成します。
EnvDTE.DTE dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE.DTE;
EnvDTE80.Solution2 solution = (EnvDTE80.Solution2)dte.Solution;
try {
solution.Create(@"F:\Dev\Visual Studio 2013\Packages\Spikes\VSPNewSolution\Test\MySolution", "MySolution");
string templatePath = solution.GetProjectTemplate("ConsoleApplication.zip", "CSharp");
string projectPath = @"F:\Dev\Visual Studio 2013\Packages\Spikes\VSPNewSolution\\Test\MySolution\MyProject";
/*
* from MZTools site :
* Once you have the template file name, you can add a project to the solution using the EnvDTE80.Solution.AddFromTemplate method.
* Note: this method returns null (Nothing) rather than the EnvDTE.Project created,
* so you may need to locate the created project in the Solution.Projects collection.
* See PRB: Solution.AddXXX and ProjectItems.AddXXX methods return Nothing (null).
*/
EnvDTE.Project project = solution.AddFromTemplate(templatePath, projectPath, "MyProject", false);
EnvDTE.ProjectItem projectItem;
String itemPath;
// Point to the first project
project = solution.Projects.Item(1); // try also "MyProject"
VSLangProj.VSProject vsProject = (VSLangProj.VSProject)project.Object;
vsProject.References.Add("NUnit.Framework");
// Retrieve the path to the class template.
itemPath = solution.GetProjectItemTemplate("Class.zip", "CSharp");
//Create a new project item based on the template, in this case, a Class.
projectItem = project.ProjectItems.AddFromTemplate(itemPath, "MyClass.cs");
}
catch (Exception ex) {
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
VSLangProjを使用してMyProjectへの参照を追加することができました。
ここまでは順調ですね。
結果のクラスは次のとおりです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyProject
{
class MyClass
{
}
}
何度もググっても見つからなかったのは、クラス コードに using ディレクティブを追加する方法です (この場合はNUnit.Framework を使用)。
簡単な方法は、クラス ドキュメントを直接操作する行を記述することです。
Visual Studio Extensibility を使用してプログラムで行う方法はありますか?
アップデート
作成されたクラスのCodeClassオブジェクトを取得しようとした後、投稿されたコードを試しました ほとんど変更を加えずに、DTE を介して型名で ProjectItem を検索します。更新されたコードは次のとおりです。
EnvDTE.DTE dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE.DTE;
EnvDTE80.Solution2 solution = (EnvDTE80.Solution2)dte.Solution;
try {
string solutionPath = @"F:\Dev\Visual Studio 2013\Packages\Spikes\VSPNewSolution\Test\MySolution";
solution.Create(solutionPath, "MySolution");
string templatePath = solution.GetProjectTemplate("ConsoleApplication.zip", "CSharp");
string projectPath = @"F:\Dev\Visual Studio 2013\Packages\Spikes\VSPNewSolution\\Test\MySolution\MyProject";
EnvDTE.Project project = solution.AddFromTemplate(templatePath, projectPath, "MyProject", false);
EnvDTE.ProjectItem projectItem;
String itemPath;
foreach (EnvDTE.Project p in solution.Projects) {
if (p.Name == "MyProject") {
project = p;
break;
}
}
VSLangProj.VSProject vsProject = (VSLangProj.VSProject)project.Object;
vsProject.References.Add("NUnit.Framework");
itemPath = solution.GetProjectItemTemplate("Class.zip", "CSharp");
projectItem = project.ProjectItems.AddFromTemplate(itemPath, "MyClass.cs");
// I decided to save both, just in case
solution.SaveAs(solutionPath + @"\MySolution.sln");
project.Save();
EnvDTE.CodeClass codeClass = FindClass(project, "MyClass.cs");
// Display the source code for the class (from MSDN).
if (codeClass != null) {
EnvDTE.TextPoint start = codeClass.GetStartPoint(EnvDTE.vsCMPart.vsCMPartWhole);
EnvDTE.TextPoint finish = codeClass.GetEndPoint(EnvDTE.vsCMPart.vsCMPartWhole);
string src = start.CreateEditPoint().GetText(finish);
System.Windows.Forms.MessageBox.Show(src, codeClass.FullName + "Source");
}
}
catch (Exception ex) {
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
private CodeClass FindClass(Project project, string className) {
return FindClass(project.CodeModel.CodeElements, className);
}
private CodeClass FindClass(CodeElements elements, string className) {
foreach (CodeElement element in elements) {
if (element is CodeNamespace || element is CodeClass) {
CodeClass c = element as CodeClass;
if (c != null && c.Access == vsCMAccess.vsCMAccessPublic) {
if (c.FullName == className)
return c;
CodeClass subClass = FindClass(c.Members, className);
if (subClass != null)
return subClass;
}
CodeNamespace ns = element as CodeNamespace;
if (ns != null) {
CodeClass cc = FindClass(ns.Members, className);
if (cc != null)
return cc;
}
}
}
return null;
}
project.CodeModel.CodeElements.Count がゼロであるため、FindClass は常に null を返します。え?
UPDATE 2
まあ、私を殴らないでください。元のコードには、projectPath変数に余分なバックスラッシュがありました。
これにより、project.CodeModel.CodeElements.Count がゼロになりました。
また、FindClassは拡張子のないクラスFullNameを必要とし、パブリッククラスのみを検索します。
コードを修正しましたが、それでもnullが返されました (私自身のせいだと思います: 何かを見逃したに違いありません)。
とにかく、FindClassは、プロジェクト参照内のクラスを含む、すべてのプロジェクト CodeElements で指定されたクラスを検索します。
プロジェクトにローカルなクラスを検索しているので、これは私の場合はやり過ぎです。
だから私はそれを行う関数を書きました。
ここにあります :
public static CodeClass FindClassInProjectItems(Project project, string className) {
CodeClass result = null;
foreach (EnvDTE.ProjectItem pi in project.ProjectItems) {
if (pi.Name == className + ".cs") {
if (pi.FileCodeModel != null) {
foreach (EnvDTE.CodeElement ce in pi.FileCodeModel.CodeElements) {
if (ce is EnvDTE.CodeClass) {
result = ce as EnvDTE.CodeClass;
break;
}
else if (ce is EnvDTE.CodeNamespace) {
CodeNamespace ns = ce as CodeNamespace;
if (ns.Name == project.Name) {
foreach (CodeElement sce in ns.Members) {
if (sce is CodeClass && sce.Name == className) {
result = sce as CodeClass;
break;
}
}
}
}
}
}
}
}
return result;
}
動作するので、静的ClassFinderクラスを作成し、関数を追加しました。
次のステップは、using ディレクティブを含む完全なクラス ソース コードを取得することでした。
MSDN hereでサンプルを見つけました。これは重要なコードです。
// Display the source code for the class.
TextPoint start = cls.GetStartPoint(vsCMPart.vsCMPartWhole);
TextPoint finish = cls.GetEndPoint(vsCMPart.vsCMPartWhole);
string src = start.CreateEditPoint().GetText(finish);
実際には、最初の行で例外がスローされます。
だから私はvsCMPart enum のすべてのメンバーを試しました: それらのほとんどは例外をスローします:
vsCMPart.vsCMPartBody、vsCMPart.vsCMPartHeader、vsCMPart.vsCMPartNavigate、およびvsCMPart.vsCMPartWholeWithAttributes。
vsCMPart.vsCMPartHeaderとvsCMPart.vsCMPartWholeWithAttributesは同じ結果 (少なくともこの場合) を
返しますが、他のものはコード全体を返しません。
短くするために:
private void DisplayClassSource(CodeClass codeClass) {
EnvDTE.TextPoint start = codeClass.GetStartPoint(vsCMPart.vsCMPartHeader);
EnvDTE.TextPoint finish = codeClass.GetEndPoint();
string source = start.CreateEditPoint().GetText(finish);
System.Windows.Forms.MessageBox.Show(source, codeClass.FullName + "Class source");
}
private void DisplayNamespaceSource(CodeNamespace codeNamespace) {
EnvDTE.TextPoint start = codeNamespace.GetStartPoint(EnvDTE.vsCMPart.vsCMPartWholeWithAttributes);
EnvDTE.TextPoint finish = codeNamespace.GetEndPoint();
string src = start.CreateEditPoint().GetText(finish);
System.Windows.Forms.MessageBox.Show(src, codeNamespace.FullName + "Namespace source");
}
using ディレクティブを含め、IDE に表示されるソース コードが
必要な場合は、classCode.ProjectItem オブジェクトを使用する必要があります。
private void DisplayClassFullSource(CodeClass codeClass) {
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (CodeElement ce in codeClass.ProjectItem.FileCodeModel.CodeElements) {
if (ce.Kind == vsCMElement.vsCMElementImportStmt) {
// this is a using directive
// ce.Name throws an exception here !
sb.AppendLine(GetImportCodeLines(ce));
}
else if (ce.Kind == vsCMElement.vsCMElementNamespace) {
sb.AppendLine();
sb.AppendLine(GetNamespaceCodeLines(ce));
}
}
System.Windows.Forms.MessageBox.Show(sb.ToString(), codeClass.FullName + "class source");
}
private static string GetImportCodeLines(CodeElement ce) {
TextPoint start = ce.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes);
TextPoint finish = ce.GetEndPoint(vsCMPart.vsCMPartWholeWithAttributes);
return start.CreateEditPoint().GetText(finish);
}
private string GetNamespaceCodeLines(CodeElement ce) {
EnvDTE.TextPoint start = ce.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes);
//EnvDTE.TextPoint finish = codeClass.GetEndPoint(EnvDTE.vsCMPart.vsCMPartWhole); // ERROR : the method or operation is not implemented
EnvDTE.TextPoint finish = ce.GetEndPoint();
return start.CreateEditPoint().GetText(finish);
}
今、私たちは問題の解決に非常に近づいています. 私の答えを見てください。(小説っぽくてすみません)