以编程方式将 using 指令添加到 class
Adding a using directive to a class programmatically
我正在研究Visual Studio可扩展性。
MSDN 中的这段代码创建了一个新的 C# 解决方案,其中包含一个带有 class 的项目:
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 的引用。
到目前为止,还不错。
结果 class 是:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyProject
{
class MyClass
{
}
}
经过大量谷歌搜索后我没有找到的是添加 using 指令的方法
在 class 代码中( 在这种情况下使用 NUnit.Framework;)。
简单的方法是编写直接操作 class 文档的行。
有没有办法使用 Visual Studio Extensibility 以编程方式执行此操作?
更新
在尝试为创建的 class 获取 CodeClass 对象之后,
我尝试了 Finding a ProjectItem by type name via DTE 中发布的代码
变化不大。
这是更新后的代码:
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;
}
嗯,事实证明 FindClass 总是 return 为空,因为 project.CodeModel.CodeElements.Count 为零。
呃?
更新 2
好吧,请不要打败 me.The 原始代码在 projectPath 变量中有多余的反斜杠。
这导致 project.CodeModel.CodeElements.Count 为零。
此外,FindClass 需要没有扩展名的 class FullName 并搜索 public class仅供参考。
我更正了代码,但在 return 中仍然得到 null(我想是我自己的错:我一定是漏掉了什么)。
无论如何,FindClass 在 all 项目 CodeElements 中搜索给定的 class,包括 classes
项目引用。
这对我来说有点过分了,因为我正在搜索项目本地的 class。
所以我写了一个函数来做到这一点。
在这里:
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 class 并添加了该函数。
下一步是检索完整的 class 源代码,包括 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 枚举的所有成员:他们中的大多数抛出异常,除了:
vsCMPart.vsCMPartBody, vsCMPart.vsCMPartHeader, vsCMPart.vsCMPartNavigate and vsCMPart.vsCMPartWholeWithAttributes.
vsCMPart.vsCMPartHeader和vsCMPart.vsCMPartWholeWithAttributesreturn相同的结果(至少在这种情况下),
而其他人则没有 return 整个代码。
保持简短:
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");
}
如果我们想要 IDE 中显示的源代码,包括 using 指令,
我们必须使用 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);
}
现在我们已经非常接近问题的解决方案了。
看我的回答。
(对不起,如果这看起来像小说)
据我所知,没有直接的方法可以将 using 指令添加到 CodeClass。
我找到的唯一方法是:
它当然需要改进,但它确实有效。
此代码假定所有 using 指令都排成一行,位于 class 代码的顶部。
例如,如果命名空间中存在 using 指令,它将无法正常工作。
它会在最后找到的指令之后添加给定的指令。
它 而不是 检查代码以确定指令是否已经存在。
private void AddUsingDirectiveToClass(CodeClass codeClass, string directive) {
CodeElement lastUsingDirective = null;
foreach (CodeElement ce in codeClass.ProjectItem.FileCodeModel.CodeElements) {
if (ce.Kind == vsCMElement.vsCMElementImportStmt) {
// save it
lastUsingDirective = ce;
}
else {
if (lastUsingDirective != null) {
// insert given directive after the last one, on a new line
EditPoint insertPoint = lastUsingDirective.GetEndPoint().CreateEditPoint();
insertPoint.Insert("\r\nusing " + directive + ";");
}
}
}
}
所以,最终的工作代码是
EnvDTE.DTE dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE.DTE;
EnvDTE80.Solution2 solution = (EnvDTE80.Solution2)dte.Solution;
try {
/*
* NOTE while the MSDN sample states you must open an existing solution for the code to work,
* it works also without opening a solution.
*/
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 = solutionPath + @"\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);
// the following code would do since there is only a single project
//project = solution.Projects.Item(1);
// tried this :
// project = solution.Projects.Item("MyProject");
// but it throws an invalid argument exception
// search project by name
foreach (EnvDTE.Project p in solution.Projects) {
if (p.Name == "MyProject") {
project = p;
break;
}
}
// add a reference to NUnit
VSLangProj.VSProject vsProject = (VSLangProj.VSProject)project.Object;
vsProject.References.Add("NUnit.Framework");
// Retrieve the path to the class template.
string itemPath = solution.GetProjectItemTemplate("Class.zip", "CSharp");
//Create a new project item based on the template, in this case, a Class.
/*
* Here we find the same problem as with solution.AddFromTemplate(...) ( see above )
*/
EnvDTE.ProjectItem projectItem = project.ProjectItems.AddFromTemplate(itemPath, "MyClass.cs");
solution.SaveAs(solutionPath + @"\MySolution.sln");
project.Save();
// retrieve the new class we just created
EnvDTE.CodeClass codeClass = ClassFinder.FindClassInProjectItems(project, "MyClass");
if (codeClass != null) {
DisplayClassFullSource(codeClass);
AddUsingDirectiveToClass(codeClass, "NUnit.Framework");
project.Save();
DisplayClassFullSource(codeClass);
}
}
catch (Exception ex) {
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
P.S.
在接受这个答案之前,我会等一会儿,以防其他人发布更好的解决方案。
编辑
时间过去了,没有关于该主题的进一步帖子。我将此标记为已接受的答案。
我正在研究Visual Studio可扩展性。 MSDN 中的这段代码创建了一个新的 C# 解决方案,其中包含一个带有 class 的项目:
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 的引用。
到目前为止,还不错。
结果 class 是:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyProject
{
class MyClass
{
}
}
经过大量谷歌搜索后我没有找到的是添加 using 指令的方法
在 class 代码中( 在这种情况下使用 NUnit.Framework;)。
简单的方法是编写直接操作 class 文档的行。
有没有办法使用 Visual Studio Extensibility 以编程方式执行此操作?
更新
在尝试为创建的 class 获取 CodeClass 对象之后, 我尝试了 Finding a ProjectItem by type name via DTE 中发布的代码 变化不大。 这是更新后的代码:
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;
}
嗯,事实证明 FindClass 总是 return 为空,因为 project.CodeModel.CodeElements.Count 为零。 呃?
更新 2
好吧,请不要打败 me.The 原始代码在 projectPath 变量中有多余的反斜杠。
这导致 project.CodeModel.CodeElements.Count 为零。
此外,FindClass 需要没有扩展名的 class FullName 并搜索 public class仅供参考。
我更正了代码,但在 return 中仍然得到 null(我想是我自己的错:我一定是漏掉了什么)。
无论如何,FindClass 在 all 项目 CodeElements 中搜索给定的 class,包括 classes
项目引用。
这对我来说有点过分了,因为我正在搜索项目本地的 class。
所以我写了一个函数来做到这一点。
在这里:
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 class 并添加了该函数。
下一步是检索完整的 class 源代码,包括 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 枚举的所有成员:他们中的大多数抛出异常,除了:
vsCMPart.vsCMPartBody, vsCMPart.vsCMPartHeader, vsCMPart.vsCMPartNavigate and vsCMPart.vsCMPartWholeWithAttributes.
vsCMPart.vsCMPartHeader和vsCMPart.vsCMPartWholeWithAttributesreturn相同的结果(至少在这种情况下),
而其他人则没有 return 整个代码。
保持简短:
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");
}
如果我们想要 IDE 中显示的源代码,包括 using 指令,
我们必须使用 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);
}
现在我们已经非常接近问题的解决方案了。 看我的回答。 (对不起,如果这看起来像小说)
据我所知,没有直接的方法可以将 using 指令添加到 CodeClass。
我找到的唯一方法是:
它当然需要改进,但它确实有效。
此代码假定所有 using 指令都排成一行,位于 class 代码的顶部。
例如,如果命名空间中存在 using 指令,它将无法正常工作。
它会在最后找到的指令之后添加给定的指令。
它 而不是 检查代码以确定指令是否已经存在。
private void AddUsingDirectiveToClass(CodeClass codeClass, string directive) {
CodeElement lastUsingDirective = null;
foreach (CodeElement ce in codeClass.ProjectItem.FileCodeModel.CodeElements) {
if (ce.Kind == vsCMElement.vsCMElementImportStmt) {
// save it
lastUsingDirective = ce;
}
else {
if (lastUsingDirective != null) {
// insert given directive after the last one, on a new line
EditPoint insertPoint = lastUsingDirective.GetEndPoint().CreateEditPoint();
insertPoint.Insert("\r\nusing " + directive + ";");
}
}
}
}
所以,最终的工作代码是
EnvDTE.DTE dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE.DTE;
EnvDTE80.Solution2 solution = (EnvDTE80.Solution2)dte.Solution;
try {
/*
* NOTE while the MSDN sample states you must open an existing solution for the code to work,
* it works also without opening a solution.
*/
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 = solutionPath + @"\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);
// the following code would do since there is only a single project
//project = solution.Projects.Item(1);
// tried this :
// project = solution.Projects.Item("MyProject");
// but it throws an invalid argument exception
// search project by name
foreach (EnvDTE.Project p in solution.Projects) {
if (p.Name == "MyProject") {
project = p;
break;
}
}
// add a reference to NUnit
VSLangProj.VSProject vsProject = (VSLangProj.VSProject)project.Object;
vsProject.References.Add("NUnit.Framework");
// Retrieve the path to the class template.
string itemPath = solution.GetProjectItemTemplate("Class.zip", "CSharp");
//Create a new project item based on the template, in this case, a Class.
/*
* Here we find the same problem as with solution.AddFromTemplate(...) ( see above )
*/
EnvDTE.ProjectItem projectItem = project.ProjectItems.AddFromTemplate(itemPath, "MyClass.cs");
solution.SaveAs(solutionPath + @"\MySolution.sln");
project.Save();
// retrieve the new class we just created
EnvDTE.CodeClass codeClass = ClassFinder.FindClassInProjectItems(project, "MyClass");
if (codeClass != null) {
DisplayClassFullSource(codeClass);
AddUsingDirectiveToClass(codeClass, "NUnit.Framework");
project.Save();
DisplayClassFullSource(codeClass);
}
}
catch (Exception ex) {
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
P.S.
在接受这个答案之前,我会等一会儿,以防其他人发布更好的解决方案。
编辑
时间过去了,没有关于该主题的进一步帖子。我将此标记为已接受的答案。