问题 在不编码插件时获取当前的EnvDTE或IServiceProvider


我正在编写一些设计时间代码。我想使用这个片段:(找到 这里

var dte = (EnvDTE.DTE) GetService(typeof(EnvDTE.DTE));
if (dte != null)
{
    var solution = dte.Solution;
    if (solution != null)
    {
        string baseDir = Path.GetDirectoryName(solution.FullName);
    }
}

问题是这不能编译。 (GetService不是已知的方法调用)我尝试添加Microsoft.VisualStudio.Shell(和Microsoft.VisualStudio.Shell.10.0),但它没有帮助。

在互联网上环顾四周,我发现你需要一个IServiceProvider来调用它。

但是所有展示如何让IServiceProvider使用EnvDTE的例子。

所以,要获得当前的EnvDTE,我需要IServiceProvider。但要获得IServiceProvider,我需要一个EnvDTE。 (我的桶里有一个洞......)

所以,这是我的问题:

在普通的WPF应用程序中,我该如何获取 当前实例 EnvDTE?

注意:我不是在寻找任何旧的EnvDTE实例。我需要一个用于我当前的Visual Studio实例(我一次运行3-4个Visual Studio实例。)


6888
2018-06-02 17:48


起源

您的WPF代码完全是一个单独的进程,还是WPF代码仍然通过其他机制在Visual Studio中运行? - Jason Malinowski
@jason - 只是一个普通的wpf应用程序。我计划在设计时运行它。但我会对在运行时工作的示例感到满意(例如在OnClick事件中)。 (制作一个新的wpf应用程序,在窗口上拖动一个按钮,然后双击它。) - Vaccano
您需要使用适当的名字对象调用GetActiveObject到您想要的VS实例。这是您需要的信息: msdn.microsoft.com/en-us/library/ms228755.aspx ? - Simon Mourier
我找到了解决方案,请参阅: stackoverflow.com/questions/4724381/... - Dennis


答案:


这个问题有你正在寻找的答案。

在Visual C#2010中获取DTE2对象的引用

特别

https://stackoverflow.com/a/4724924/858142

这是代码: 

Usings:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE;
using Process = System.Diagnostics.Process;

方法:

[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern void GetRunningObjectTable(int reserved,
                                                 out IRunningObjectTable prot);
internal static DTE GetCurrent()
{
   //rot entry for visual studio running under current process.
   string rotEntry = String.Format("!VisualStudio.DTE.10.0:{0}",
                                    Process.GetCurrentProcess().Id);
   IRunningObjectTable rot;
   GetRunningObjectTable(0, out rot);
   IEnumMoniker enumMoniker;
   rot.EnumRunning(out enumMoniker);
   enumMoniker.Reset();
   IntPtr fetched = IntPtr.Zero;
   IMoniker[] moniker = new IMoniker[1];
   while (enumMoniker.Next(1, moniker, fetched) == 0)
   {
       IBindCtx bindCtx;
       CreateBindCtx(0, out bindCtx);
       string displayName;
       moniker[0].GetDisplayName(bindCtx, null, out displayName);
       if (displayName == rotEntry)
       {
           object comObject;
           rot.GetObject(moniker[0], out comObject);
           return (DTE)comObject;
       }
   }
   return null;
}

正如另一个答案所示,这在调试时不起作用。


8
2018-06-12 14:24



嗨Vaccano,我刚刚在我的帖子上看到你的评论,我很高兴我的回答确实对你有所帮助! - Dennis


你需要一个 的IServiceProvider 然后你可以调用它的GetService方法。
dte = (DTE)serviceProvider.GetService(typeof(DTE)); 

所以问题是如何获得对它的引用 IServiceProvider 接口。

如果你正在创建一个 VSPackage的 带有工具窗口(继承自 ToolWindowPane),例如,然后是ToolWindow类本身 实现IServiceProvider。 在这种情况下,当您想在WPF控件中使用IServiceProvider时,您可以在工具窗口构造函数上创建其实例并简单地传递 this 作为控件构造函数的参数。

[Guid("f716c629-b8e3-4ab2-8dbd-8edd67165609")]
public class MyToolWindow : ToolWindowPane
{
    /// <summary>
    /// Standard constructor for the tool window.
    /// </summary>
    public MyToolWindow() :
        base(null)
    {
        ...
        // This is the user control hosted by the tool window
        base.Content = new MyControl(this);
    }

你的控件的构造函数得到了 IServiceProvider 作为一个论点:

public MyControl(IServiceProvider _serviceProvider)

3
2018-06-12 06:38



唉,正如我所说,这是一个“普通的WPF应用程序”。没有任何实现工具或包的东西。 - Vaccano
@Vaccano如果您正在运行独立的WPF应用程序, 根据你想要选择的EnvDTE实例? 什么是 “旧的/新的EnvDTE实例” 为你?它是EnvDTE的旧版本/新版本,还是指的是按时间顺序调用的最后一个Visual Studio实例? - Amir Gonnen
我可以看到我困惑的地方。我这样做是因为我想在WPF应用程序(普通的WPF应用程序)中添加一些设计时元素。但我没有模板或加载项。只是一个普通的WPF应用程序。 Quickhorn的答案最终为我工作。 - Vaccano


我们使用此代码成功完成了此操作:

System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.10.0")

但它并不完美 - 它需要运行一个Visual Studio实例(如果有多个,则该方法将返回其中一个,但您无法控制哪一个)。


0
2018-06-05 22:36



拍摄 - 只需重新阅读你的帖子,看到关于多个实例的最后一部分,并需要一个特定的实例。对不起 - 我没有任何相关内容。 - JMarsch


我发布了 一个答案 在这个类似的问题上: 在Visual C#2010中获取DTE2对象的引用

我的方法是将DTE2.Solution.FullName与正在执行的程序集路径进行比较,这样就可以在使用与Quickhorns相同的ROT枚举后回答过滤可能的候选项。


0
2017-10-23 16:22





对于任何有兴趣使用F#进行此操作的人来说,这里主要是完全转换(目前设置为在linqpad中运行):

open System;
open System.Runtime.InteropServices;
open System.Runtime.InteropServices.ComTypes;
open EnvDTE;
open System.Diagnostics;
//http://stackoverflow.com/questions/10864595/getting-the-current-envdte-or-iserviceprovider-when-not-coding-an-addin

//http://stackoverflow.com/questions/6558789/how-to-convert-out-ref-extern-parameters-to-f
//http://stackoverflow.com/questions/1689460/f-syntax-for-p-invoke-signature-using-marshalas

[<System.Runtime.InteropServices.DllImport("ole32.dll")>] 
extern int CreateBindCtx(System.IntPtr inRef, IBindCtx& outParentRef);
[<System.Runtime.InteropServices.DllImport("ole32.dll")>]
extern int GetRunningObjectTable(System.IntPtr inRef, IRunningObjectTable& outParentRef);
//let dte = System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.12.0") :?> EnvDTE80.DTE2
let comName="VisualStudio.DTE.12.0"
let rotEntry = "!"+comName
//let mutable rot:IRunningObjectTable =null

let rot=
    let mutable result:IRunningObjectTable = null
    GetRunningObjectTable(nativeint 0, &result) |> ignore
    result


let mutable enumMoniker:IEnumMoniker = null
rot.EnumRunning (&enumMoniker) 
enumMoniker.Reset() |> ignore
let mutable fetched = IntPtr.Zero
let mutable moniker:IMoniker[] = Array.zeroCreate 1 //http://msdn.microsoft.com/en-us/library/dd233214.aspx

let matches = seq {
    while enumMoniker.Next(1, moniker, fetched) = 0 do
        "looping" |> Dump
        let mutable bindCtx:IBindCtx = null
        CreateBindCtx(nativeint 0, &bindCtx) |> ignore
        let mutable displayName:string = null
        moniker.[0].GetDisplayName(bindCtx,null, &displayName)
        displayName |> Dump
        if displayName.StartsWith(rotEntry) then
            let mutable comObject = null
            rot.GetObject(moniker.[0], &comObject) |> ignore
            let dte =  comObject:?>EnvDTE80.DTE2
            yield displayName,bindCtx,comObject,dte.FullName, dte
}
matches |> Dump

0
2018-05-02 21:13