一、 使用窗口
1、 使用WinForm窗口
在VS.NET中显示WinForm窗口非常容易,和平常编写WinForm程序没什么两样,步骤如下:
l 在项目里添加一个WinForm窗体
l 修改WinForm窗体的构造函数
将VS环境的根对象DTE对象传入,以便在WinForm中可以操纵开发环境
publicclass MyForm : System.Windows.Forms.Form
{
private EnvDTE.DTE applicationObject;
…
public MyForm(EnvDTE.DTE applicationObject)
{
InitializeComponent();
this.applicationObject = applicationObject;
…
}
l 在Connect文件的Exec函数里生成并显示WinForm窗体。
MyForm form =new MyForm(this.applicationObject.DTE);
form.ShowDialog();
注意:这里的this.applicationObject是_DTE接口指针,它的一个成员DTE成员才是DTE对象,不能直接用类型转换如(DTE) this.applicationObject
图-1 在VS集成环境里显WinForm窗体
2、 使用工具窗口
VS.NET的工具窗口指的是类似“解决方案资源管理器”,“类视图”,“工具箱”这样停靠在上下左右侧边的窗口。而如代码编辑窗口这样的窗口,需要用VSIP进行开发,外接程序插件是没有办法开发这类窗口的。
根据MSDN里的资料,如果要开发侧边工具窗口,是件非常繁琐的事情。因为VS.NET是基于COM开发的,所以开发出来的侧边工具窗口也需要实现一系列接口,也就是说,不能直接使用.NET程序集。幸而已经有人做了这件事情,开发了一个将.NET用户控件包装成COM组件的容器VSUserControlHostLib,使得我们只需要开发出.NET用户控件,就可以将其嵌入到VS集成环境里了
开发步骤:
1、 生成Addin项目
2、 在项目中添加VSUserControlHost.dll引用
3、 在Connect.cs文件的OnConnection方法中添加显示窗口的代码
下面是代码示例:(添加在OnConnect方法中)
//GUID串可以用VS开发环境里菜单命令“工具à创建GUID”弹出的对话框创建
string guidstr = "{65FADCBF-63D2-448b-8A4B-393D7E751345}";
object objTemp =null;
VSUserControlHostLib.IVSUserControlHostCtl objControl =null;
//create the 'Explorer' tool window
/*
[C#]
返回一个 Window对象
public Window CreateToolWindow(
AddIn AddInInst, //必选项。其生存期决定工具窗口生存期的 AddIn对象
string ProgID, //必选项。Document对象或 ActiveX 控件的编程 ID。
string Caption, //新工具窗口的标题
string GuidPosition, //新工具窗口的唯一标识符,可用作 Window.Item的索引
ref object DocObj//工具窗口中要承载的 Document对象或控件
);
*/
//这里先生成容器窗口,返回的控件对象在objTemp里
windowExplorer = applicationObject.Windows.CreateToolWindow(
addInInstance,
"VSUserControlHost.VSUserControlHostCtl",
"菜单浏览器",
guidstr,
ref objTemp);
//使用容器窗口时,必须在调用该容器控件前设置其为可见,否者将会不能正常显示
windowExplorer.Visible =true;
windowExplorer.IsFloating =false;
//将返回的工具窗口转换为容器对象
objControl = (VSUserControlHostLib.IVSUserControlHostCtl)objTemp;
System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
//用容器对象包含用户控件,
menuExplorer = (MenuExplore)objControl.HostUserControl(asm.Location,
"MenuBrowser.MenuExplore");
//设置窗口的图标
//windowExplorer.SetTabPicture(…)
//将DTE对象赋给窗口
menuExplorer.Application = applicationObject;
说明:
l 生成工具窗口:
以上代码先用CreateToolWindow函数生成工具窗口,工具窗口里承载的VSUserControlHost.VSUserControlHostCtl类型控件由objTemp对象返回
l 设置可见性:
将工具窗口设置为可见,非浮动
l 获取IVSUserControlHostCtl接口:
将返回的工具窗口中承载的控件对象objTemp转换为IVSUserControlHostCtl接口类型,并赋给objControl对象
l 加载用户控件:
用objControl的HostUserControl方法加载指定程序集的指定用户控件对象,上面代码中因为MenuBrowser.MenuExplore类是在插件项目的程序集里,所以用System.Reflection.Assembly.GetExecutingAssembly()方法获取当前程序集位置。如果用户控件是在其他程序集里,需要根据实际情况指定程序集位置
4、 最后还应该在OnDisconnection方法里关闭窗口
if(windowExplorer!= null)
{
windowExplorer.Visible = false;
}
下图就是笔者用来列举所有的系统菜单对象名称的一个插件,使用的就是工具窗口
图-2 在VS集成环境中使用工具窗口
一、 操纵VS开发环境
VS.NET自动化模型涉及的面太广,本文只针对一些专题加以说明。
1、 利用代码模型浏览代码
l 获取代码模型对象
//获取当前正活动的文档
Document activeDocument = applicationObject.ActiveDocument;
if(activeDocument !=null)
{
ProjectItem projectItem = activeDocument.ProjectItem;
if(projectItem !=null)
{
FileCodeModel fileCodeModel = projectItem.FileCodeModel;
if(fileCodeModel !=null)
{
CodeElements codeElements = fileCodeModel.CodeElements;
if(codeElements !=null)
{
foreach(CodeElement cein codeElements)
{
result.AddRange(getCodeElements(ce));
}
}
}
}
}
说明:
上面的代码先从DTE对象(也即VS自动模型的根对象)获得当前正活动的文档对象
然后再根据文档对象获取和它关联的项目元素,这里的ProjectItem其实就是在解决方案资源管理器下面的项目文件夹对应的子项。可以是各种类型,如文件,文件夹等
再从项目元素获得关联的文件代码模型
根据文件代码模型获取其代码元素的集合CodeElementCollection,代码元素的父类为CodeElement,下面派生出许多的子类,代表具体的代码元素,分别有:
CodeNamespace、CodeClass、CodeInterface、CodeFunction
CodeProperty、CodeVariable、CodeDelegate、CodeStruct、CodeEnum
代表着命名空间、类、接口、函数、属性、变量、委托、结构、枚举等类型的代码元素,某个CodeElement具体是何类型,可以根据CodeElement.vsCMElement枚举类型来判断,然后再显式转换成具体的子类即可
但是更进一步的代码元素,如函数体内的语句,好像还没有看到有方法检索。不过也可以利用一些其它的方法,比如CodeRush的程序集里面提供了一个StructuralParser程序集,用于C#的代码分析。如果谁有兴趣可以研究研究
l 根据具体类型检索代码元素(不再多做解释了):
private ArrayList getCodeElements(CodeElement baseElement)
{
ArrayList result =new ArrayList();
switch(baseElement.Kind)
{
case vsCMElement.vsCMElementNamespace:
CodeNamespace cn = (CodeNamespace) baseElement;
foreach(CodeElement elementin cn.Members)
{
result.AddRange(getCodeElements(element));
}
break;
case vsCMElement.vsCMElementClass:
CodeClass cc = (CodeClass) baseElement;
foreach(CodeElement elementin cc.Members)
{
result.AddRange(getCodeElements(element));
}
break;
case vsCMElement.vsCMElementInterface:
CodeInterface ci = (CodeInterface) baseElement;
foreach(CodeElement elementin ci.Members)
{
result.AddRange(getCodeElements(element));
}
break;
case vsCMElement.vsCMElementFunction:
result.Add(baseElement);
break;
case vsCMElement.vsCMElementProperty:
{
result.Add(baseElement);
}
break;
case vsCMElement.vsCMElementEvent:
{
result.Add(baseElement);
}
break;
case vsCMElement.vsCMElementDelegate:
{
result.Add(baseElement);
}
break;
case vsCMElement.vsCMElementStruct:
{
result.Add(baseElement);
CodeStruct cs = (CodeStruct) baseElement;
foreach(CodeElement elementin cs.Members)
{
result.AddRange(getCodeElements(element));
}
}
break;
case vsCMElement.vsCMElementEnum:
{
result.Add(baseElement);
}
break;
case vsCMElement.vsCMElementAttribute:
result.Add(baseElement);
break;
case vsCMElement.vsCMElementUsingStmt:
result.Add(baseElement);
break;
case vsCMElement.vsCMElementVariable:
result.Add(baseElement);
break;
}
return result;
}
2、 操纵解决方案及其项目文档
l 获取当前被选中的项目
下面代码演示了鼠标点击一个项目,或项目中的子项时,如何得到该项目对象
public Project GetSelectedProject()
{
Project project =null;
//从被选中对象中获取工程对象
EnvDTE.SelectedItem item = Application.SelectedItems.Item(1);
if(item.Project !=null)
{//被选中的就是项目本生
project = item.Project;
}
else
{//被选中的是项目下的子项
project = item.ProjectItem.ProjectItems.ContainingProject;
}
return project;
}
l 获取当前项目的所在目录
private string GetSelectedProjectPath()
{
string path = "";
//获取被选中的工程
Project project = GetSelectedProject();
if(project != null)
{
//全名包括*.csproj这样的文件命
path = project.FullName;
}
//去掉工程的文件名
path = Path.GetDirectoryName(path);
return path;
}
l 将文件加入工程中
//获取被选中的工程
Project project = this.GetSelectedProject();
//将文件夹下的文件加入工程
project.ProjectItems.AddFromDirectory(sdir);
//将单个文件加入工程
project.ProjectItems.AddFromFile(nfile);
l 向项目中加入程序集引用
using VSLangProj;
private void AddReference(string assembly)
{
Project project = GetSelectedProject();
VSProject vsproject = null;
if (project.Kind == PrjKind.prjKindCSharpProject)
{
//工程类型为C#工程,将project的Object成员转换为VSProject对象
vsproject = project.Object as VSProject;
}
if(vsproject != null)
{
//获取C#工程的引用集
VSLangProj.References refers = vsproject.References;
if(refers != null)
{
//将程序集引用添加到工程中
refers.Add(assembly);
}
}
}
3、 在代码编辑窗口操纵代码
这里不再详述,只是列出网上的资源,大家可以自行参考
二、 参考资料
http://www.knowdotnet.com/add-insmacros.html
msdn帮助目录:Visual Studio .NET-->使用 Visual Studio .NET进行开发-->参考-->自动化与扩展性参考-->公共环境对象模型
三、 下载包说明:
CodeNavigator :演示了如何通过代码模型浏览代码
GuidGen :演示了WinForm窗口在VS.NET集成环境中的应用
MenuBrowser :演示了工具窗口的应用以及例举菜单对象的名称
RegionsAddIn :用于添加#Region标签,演示了如何操纵代码编辑窗口
STDTools :演示了如何向工程里添加文件,以及程序集引用,以及在工程项目的右键菜单上添加自己的菜单项