Getting the current EnvDTE or IServiceProvider when NOT coding an Addin
Asked Answered
L

5

11

I am coding up some design time code. I want to use this snippet: (Found here)

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

Problem is that this does not compile. (GetService is not a known method call) I tried adding Microsoft.VisualStudio.Shell (and Microsoft.VisualStudio.Shell.10.0) but it did not help.

In looking around on the internet I found that you need a IServiceProvider to call this.

But all the examples that show how to get an IServiceProvider use a EnvDTE.

So, to get the current EnvDTE I need IServiceProvider. But to get an IServiceProvider I need an EnvDTE. (There is a hole in my bucket...)

So, here is my question:

In a normal WPF Application, how can I get the current instance of EnvDTE?

NOTE: I am not looking for any old instance of EnvDTE. I need the one for my current Visual Studio instance (I run 3-4 instances of Visual Studio at a time.)

Lussi answered 2/6, 2012 at 17:48 Comment(4)
Is your WPF code here a separate process entirely, or WPF code still running inside Visual Studio via some other mechanism?Celsacelsius
@jason - Just a normal wpf app. I plan to run it at design time. But I would be happy with an example that would work at run time (in an OnClick event for example). (Make a new wpf app, drag a button on the window and double click on it.)Lussi
You need to call GetActiveObject with a proper moniker to the VS instance you want. Is this the kind of information you need: msdn.microsoft.com/en-us/library/ms228755.aspx ?Randazzo
I found the solution, see: #4724881Sparks
P
8

This question has the answer to which you're looking.

Get the reference of the DTE2 object in Visual C# 2010

Specifically

https://mcmap.net/q/969478/-get-the-reference-of-the-dte2-object-in-visual-c-2010

Here is the code:

Usings:

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

Method:

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

As the other answer indicates, this does not work while debugging.

Prospective answered 12/6, 2012 at 14:24 Comment(1)
Hi Vaccano, i just did read your comment on my post, and i'm glad that my answer did help you!!!Sparks
O
4

You need an IServiceProvider and then you can call its GetService method.
dte = (DTE)serviceProvider.GetService(typeof(DTE));

So the question is how to get a reference to the IServiceProvider interface.

If you are creating a VsPackage with a tool window (that inherits from ToolWindowPane), for example, then the ToolWindow class itself implements IServiceProvider. In such case when you want to use IServiceProvider in your WPF control, you create its instance on your tool window constructor and simply pass this as an argument to your control's constructor.

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

Your control's constructor gets IServiceProvider as an argument:

public MyControl(IServiceProvider _serviceProvider)
Odessaodetta answered 12/6, 2012 at 6:38 Comment(3)
Alas, as I said, this is for a "normal WPF app". Nothing that implements a tool or package.Lussi
@Lussi If you are running a standalone WPF application, based on what do you want to select the EnvDTE instance? What's an "old/new instance of EnvDTE" for you? Is it an old/new version of EnvDTE or are you referring to the last Visual Studio instance that was invoked chronologically?Odessaodetta
I can see where I was confusing. I am doing this because I want to add some design time elements to a WPF application (normal WPF app). But I don't have a template or add-in. Just a normal WPF app. The answer from Quickhorn ended up working for me.Lussi
C
1

for anyone interested in doing this with F# a mostly complete conversion is here ( currently set to run in linqpad):

open System;
open System.Runtime.InteropServices;
open System.Runtime.InteropServices.ComTypes;
open EnvDTE;
open System.Diagnostics;
//https://mcmap.net/q/995801/-getting-the-current-envdte-or-iserviceprovider-when-not-coding-an-addin

//https://mcmap.net/q/1014484/-how-to-convert-out-ref-extern-parameters-to-f
//https://mcmap.net/q/1014485/-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
Caylor answered 2/5, 2014 at 21:13 Comment(0)
U
0

We have done this successfully using this code:

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

It's not perfect though -- it requires that exactly 1 instance of Visual Studio be running (if there are more than one, the method will return one of them, but you cannot control which one).

Unbidden answered 5/6, 2012 at 22:36 Comment(1)
shoot - just re-read your post and saw that last part about multiple instances, and needing a specific one. Sorry -- I don't have anything for that.Unbidden
F
0

I have posted an answer on this similar question: Get the reference of the DTE2 object in Visual C# 2010.

My approach is to compare DTE2.Solution.FullName with the executing assembly path, that way finding the right Visual Studio instance, after using the same ROT enumeration as in Quickhorns answer to filter the possible candidates.

Fife answered 23/10, 2012 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.