Получение текущего EnvDTE или IServiceProvider, когда НЕ кодирует Addin

Я кодирую некоторый код времени разработки. Я хочу использовать этот фрагмент: (Найдено здесь)

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 за раз.)

Ответ 1

У этого вопроса есть ответ, на который вы смотрите.

Получить ссылку на объект DTE2 в Visual С# 2010

В частности

fooobar.com/questions/480468/...

Вот код:

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;
}

Как указывает другой ответ, во время отладки это не работает.

Ответ 2

Вам нужен IServiceProvider, а затем вы можете вызвать его метод GetService.
dte = (DTE)serviceProvider.GetService(typeof(DTE));

Итак, вопрос заключается в том, как получить ссылку на интерфейс IServiceProvider.

Если вы создаете VsPackage с помощью окна инструментов (которое наследуется от ToolWindowPane), то сам класс ToolWindow реализует IServiceProvider. В таком случае, когда вы хотите использовать IServiceProvider в своем элементе управления WPF, вы создаете его экземпляр в своем конструкторе окна инструмента и просто передаете 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

Мы успешно выполнили этот код:

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

Это не идеально, но для этого требуется, чтобы выполнялся ровно один экземпляр Visual Studio (если их несколько, метод вернет один из них, но вы не сможете управлять им).

Ответ 4

Я отправил ответ по этому аналогичному вопросу: Получить ссылку на объект DTE2 в Visual С# 2010.

Мой подход заключается в сравнении DTE2.Solution.FullName с исполняемым контуром сборки, тем самым находя правильный экземпляр Visual Studio, после использования того же перечисления ROT, что и в ответ Quickhorns, чтобы фильтровать возможных кандидатов.

Ответ 5

для всех, кто заинтересован в этом с F #, здесь в основном выполняется полное преобразование (в настоящее время установлено для запуска в linqpad):

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

//http://stackoverflow.com/info/6558789/how-to-convert-out-ref-extern-parameters-to-f
//http://stackoverflow.com/info/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