Monday, June 17, 2013

How to write a loop, or lambda expression, in the immediate window, part 2

$dte is the key.

That whole nuget package manager console window, that is a powershell window, that gives you easy access to the visual studio automation model, is neat, very powerful, and, as I have discovered, a treasure trove of Visual Studio arcana.

EnvDTE.DTE is easy to get at when you are in the context of an add-in that you have written yourself. Exposing it in ready made powershell window, is a complex and involved trick. Writing that whole powershell console as an add-in inside of Visual Studio, after reading the source code at nuget.codeplex.com, appears to be an elegant work of coding art.

But.. back to $dte, my favorite powershell-nuget-visualstudio variable. Here is the code that worked the magic and populated the variable inside of powershell:

 
        [System.Diagnostics.CodeAnalysis.SuppressMessage(
           
"Microsoft.Reliability",
           
"CA2000:Dispose objects before losing scope",
            Justification =
"We can't dispose it if we want to return it.")]
       
private static Tuple CreateRunspace(IConsole console, string hostName)
        {
            DTE dte = ServiceLocator.GetInstance();
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
            initialSessionState.Variables.Add(
               
new SessionStateVariableEntry(
                   
"DTE",
                    (DTE2)dte,
                   
"Visual Studio DTE automation object",
                    ScopedItemOptions.AllScope | ScopedItemOptions.Constant)
            );
// this is used by the functional tests
           
var packageManagerFactory = ServiceLocator.GetInstance();
           
var pmfTuple = Tuple.Create<string, object>("packageManagerFactory", packageManagerFactory);
Tuple<string, object>[] privateData = new Tuple<string, object>[] { pmfTuple };
var host = new NuGetPSHost(hostName, privateData)
            {
                ActiveConsole = console
            };
var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState);
            runspace.ThreadOptions = PSThreadOptions.Default;
            runspace.Open();
//
           
// Set this runspace as DefaultRunspace so I can script DTE events.
           
//
           
// WARNING: MSDN says this is unsafe. The runspace must not be shared across
           
// threads. I need this to be able to use ScriptBlock for DTE events. The
           
// ScriptBlock event handlers execute on DefaultRunspace.
           
//
            Runspace.DefaultRunspace = runspace;
return Tuple.Create(new RunspaceDispatcher(runspace), host);
        }
 
 
 
So here is what I learned from strolling down this rabbit hole.
  1. They brute forced the whole console window. They didn't recycle any sort of existing powershell or command window. They wrote a new one and dealt with all the keypress by keypress nastiness that is involved therein.
  2. $dte is actually spelled $DTE, and just a variable passed into a Runspace through its InitialSessionState.
  3. OpenSource code is fun.

Also, and unrelated to codeplex and nuget, if you want to create a headless $dte object, or rather one connected to a new instance of visual studio 2012, that is simply:

$dte = New-Object -comobject VisualStudio.DTE.11.0

Getting the running obect is done like this:

$dte = [System.Runtime.InteropSrevices.Marshal]::GetActiveObject("VisualStudio.DTE.11.0")

No comments:

Post a Comment