VSPackge插件系列:如何正确获取DTE

  做VS插件开发,不得不了解DTE,有了DTE我们就可以与VS交互了,比如说获取当前选择的文件,比如说获取当前主窗口,比如说获取编译器等等,关于DTE接口更多的说明我把接口地址贴出来方便大家查阅。

http://technet.microsoft.com/zh-cn/library/envdte.dte(v=vs.100)

  如何正确的获取DTE呢?

以下是从网上找到的一些方法

EnvDTE80.DTE2 myDTE2 = (EnvDTE80.DTE2)Microsoft.VisualBasic.Interaction.CreateObject("VisualStudio.DTE.9.0", "");
EnvDTE80.DTE2 _dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.11.0"); 
Type visualStudioType = Type.GetTypeFromProgID("VisualStudio.DTE.11.0");
EnvDTE80.DTE2 _dte2 = Activator.CreateInstance(visualStudioType) as EnvDTE80.DTE2;

参考地址:http://msdn.microsoft.com/zh-cn/library/68shb4dw(VS.80).aspx

  这些代码在我做测试的时候并不能很好的工作,比如说"VisualStudio.DTE.9.0",这个字符串,很明显跟VS版本有关,这样针对不同版本的VS就有所不同,比如VS2010获取的是10.0,VS2012获取的是11.0等等,另外一个更加令人蛋疼的问题是,以上方法获取的DTE只适合开一个VS的情况,当开多个VS的时候我们获取的DTE并不是我们当前打开项目的DTE。所以我查找一些资料最终在以下这篇文章找到答案http://social.msdn.microsoft.com/Forums/zh-CN/09fb98aa-31d0-45e2-ace2-35b6d59e855a/vsx-faq-vspackagedte。那我们就可以用以下代码获取。

EnvDTE80.DTE2 _dte2 = (EnvDTE80.DTE2)ServiceProvider.GlobalProvider.GetService(typeof(EnvDTE.DTE));

这种方式可以正确的获取DTE,并且在多个vs中不存在混乱的现象,也没有DET版本的限制。

此方法还有限制,就是必须在vs初始化完成获取Dte,否则获取的dte为空,

以下文章是一种解决方法

http://blogs.msdn.com/b/vsxteam/archive/2008/06/09/dr-ex-why-does-getservice-typeof-envdte-dte-return-null.aspx

为了防止文章丢失我拷贝到下边,以供参考

Dr. eX: Why does GetService(typeof(EnvDTE.DTE)) return null?

Ever build a Visual Studio package with code that looks like the following?

protectedoverridevoid Initialize()

{

base.Initialize();

this.dte = GetService(typeof(DTE)) asDTE;

……

}

And then realize later on that this.dte is null?

A number of Visual Studio integrators have encountered just this problem in the past, and having recently seen this problem pop up again, it’s definitely past due for Dr. eX to blog about this.

The reason the above GetService call can sometimes return null, is due to the environment not being fully loaded and initialized. When the IDE is in this state it’s said to be in a “zombie” state. The IDE actually tracks this state with the VSPROPID_Zombie property.

To work around this problem, you simply need to defer the code in your Initialize method, until the VSPROPID_Zombie property has been set to false. To do this, your package should implement IVsShellPropertyChanges interface, and execute your initialization code from your IVsShellPropertyChanges.OnShellPropertyChange implementation. For example:

public class MyPackage : Package, IVsShellPropertyEvents

{

DTE dte;

uint cookie;

protected override void Initialize()

{

base.Initialize();

// set an eventlistener for shell property changes

IVsShell shellService = GetService(typeof(SVsShell)) as IVsShell;

if (shellService != null)

ErrorHandler.ThrowOnFailure(shellService.AdviseShellPropertyChanges(this,out cookie));

// code not depending on zombie state

……..

}

public int OnShellPropertyChange(int propid, object var)

{

// when zombie state changes to false, finish package initialization

if ((int)__VSSPROPID.VSSPROPID_Zombie == propid)

{

if ((bool)var == false)

{

// zombie state dependent code

this.dte = GetService(typeof(SDTE)) as DTE;

// eventlistener no longer needed

IVsShell shellService = GetService(typeof(SVsShell)) as IVsShell;

if (shellService != null)

ErrorHandler.ThrowOnFailure(shellService.UnadviseShellPropertyChanges(this.cookie));

this.cookie = 0;

}

}

return VSConstants.S_OK;

}

}

上一篇:CEGUI0.8.4例子


下一篇:ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求