Showing posts with label Visual Studio. Show all posts
Showing posts with label Visual Studio. Show all posts

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")

Wednesday, June 12, 2013

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

First, you can't write lambda expressions in the immediate window. There are some very good, boring reasons why this is the case. Jared Par explains here...

However, what you really wanted to do is write a loop inside the immediate window because there is some particular debugging goal that you are trying to achieve. This post is about how you can do that.

For example, let's say we have an array of a large number of elements, 1000 or so should do. Let's also say that there is one piece of data in that array that is relevant. We wish to inspect each element individually and test the validity of each item. The following code snipped creates this situation.

static void Main(string[] args)
{ 
  List<int> ints = new List<int>();
  for (int i = 0; i < 1000; i++)
  {
    ints.Add(i);
  }
 
 
  Random random = new Random();
  ints[random.Next(1000)] = -12;
  Console.ReadLine();
}

In this artificial case we're looking for the index of the element in the array that is equal to -12.

The immediate window forces us to do this one item in the array at a time. Potentially taking 1000 user-interactive steps.

Since that method sucks, here's an alternative:
  1. Run the program in the debugger and set a breakpoint on the ReadLine() statement.
  2. Open up the nuget package manager console.
  3. Write some clever powershell that does the evaluation and looping for us.
The clever bit of powershell code that is necessary starts with something like this:

$dte.Debugger.GetExpression("expression", 1, $true)

$dte is the variable that contains the Visual Studio automation model.
Documentation about $dte is here.

In our case, the completed powershell expression looks like this:

for($i = 0; $i -lt 1000; $i++) { $a = $dte.Debugger.GetExpression("ints[$i]"); if ($a.Value -ne $i) { Write-Host $a.Value $i } }

Or, broken into multiple, more readable lines:

for($i = 0; $i -lt 1000; $i++) {
  $a = $dte.Debugger.GetExpression("ints[$i]");
  if ($a.Value -ne $i) {
    Write-Host $a.Value $i
  }
}

This rather neatly writes out the index we're looking for and the value found there. 



















The anomalous value (-12) wound up in the 604th element of the array. Groovy.

Most programmers aren't going to do this. It requires good knowledge of powershell, some knowledge of the Visual Studio automation model, and an interesting and difficult debugging problem before it is useful.

But it is fun.

Monday, June 10, 2013

Edit.FormatDocument

Also goes by Cntrl-E + D.

I had a document with all the formatting messed up. Tabs were spaces, and they were 4 instead of 2 which is the standard here. I typed these 3 keys, and everything was magically fixed. Groovy.

The stack overflow answer that got me here.

This is just another time saving shortcut that I'm trying to remember.

Tuesday, July 17, 2012

How to load SOS into Visual Studio

How to load SOS debugging extensions into the immediate window of Visual Studio (any version).

.load sos

For some reason, this little gem has escaped me until now. Will I ever use WinDBG again? Do I feel sad or glad about that?

Tuesday, August 9, 2011

Powershell locks loaded assemblies. Remove-Module doesn't help.

I was testing an assembly generated by Visual Studio. I imported the module into powershell via Import-Module, created the class I wanted, invoked its members, and viewed the results. Then I tweaked the project in Visual Studio and rebuilt. Powershell had the output assembly loaded and locked, so the build failed.

No news there.

The first workaround was to just close powershell and reopen it.

But! I figured I could call Remove-Module and get passed the lock without actually closing Powershell...
While this seemed like a good idea. It failed. Powershell didn't remove the lock it had on the imported assembly and it was still listed in the modules that Powershell was using. Remove-Module didn't remove the module. It just stopped showing up in Get-Modules. In fact, the variables created using the .net dll were still alive in the powershell process. Remove-Module was no solution.

Next! I figured that I would load the module inside of a powershell session and then unload the session.
Enable-PSRemoting
$session = $New-PSSession
Enter-PSSession $session
#Do Stuff
Exit-PSSession
Remove-PSSession $session

This failed as well. :(

Next I figured that I could create an AppDomain, create a Runspace in the AppDomain, do stuff, and then unload the AppDomain. I'm certain that this approach would work, but it isn't worth the time, especially since the following pseudo-shadow copy solution will work just fine:

$fileName = [System.IO.Path]::GetTempFileName()
[Environment]::CurrentDirectory = "myProjectDirectory\bin\x86\Debug"
[System.IO.File]::Copy("MyDll.dll", $fileName + ".dll")
Import-Module ($filename + ".dll")

And thus the module is loaded, and doesn't lock the Visual Studio output files.

Sunday, March 20, 2011

CoffeeScript and Chirpy

I've found some things that I think are cool:
CoffeeScript 
and
Chirpy

CoffeeScript allows me to use a language other than javascript to write javascript.
Chirpy lets me write that not-javascript inside of Visual Studio. It also does neat things with javascript minimization and packaging.

Tuesday, September 15, 2009

Fun with lc.exe

Third party components suck. Or they are misused. Or something. But they annoy me terribly. I've never met a third party component that I liked. Infragistics, Syncfusion, ComponentOne, etc. They all find ways of complicating my life, wasting the time that they were meant to be saving.

The latest travesty inflicted upon me is an annoying popup dialog that rears its ill-thought out head every time I compile a project that contains it. Which just happens to be 22 times during a normal compile of my most commonly used solution. This happens because I reinstalled my OS and didn't reinstall the component library, but I don't really care why or why it is difficult for me to get another license or behave like a reasonable adult. This is my computer and you're infecting it with malware. You suck.

Step one: turn Visual Studio on itself and break the code when the annoying dialog is in my face.

As it turns out, VS is calling an external process called lc.exe interesting...

What to do... Rename lc.exe to lc.exe.old, copy notepad and rename it lc.exe, and copy my new notepad-lc.exe into the directory where lc.exe was. Then recompile.

Notepad pops up instead of my annoying dialog. What fun. But no less annoying really.

Step two: create a console project that does absolutely nothing. Name it lc.exe. Copy it to the directory where I found lc.exe. Result: happiness.

However, my happiness was relatively short lived. It turns out that a subsequent program vbc.exe is looking for the lc.exe's output. Something called <filename>.exe.licenses.

Step three: alter my lc.exe program to create an empty file called <filename>.exe.licenses in the output directory.

That time it worked and I shall never be bothered by that particular annoying popup again. I will probably run across another third party component that was clever enough to withstand this fix eventually, but until then, my lc.exe shall make me smile and compile much faster and with far less annoyance.


 1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Console.WriteLine("End.");
6 string path = string.Empty;
7 string dllName = string.Empty;
8
9 //'/target:WIN.Contest.Host.dll /complist:Properties\licenses.licx /outdir:"obj\Debug - Fast\\"
10 foreach(string arg in args)
11 {
12 Console.WriteLine(arg);
13 System.Diagnostics.Debug.WriteLine(arg);
14
15 if (arg.StartsWith("/target:"))
16 {
17 dllName = arg.Substring(8);
18 }
19 else if (arg.StartsWith("/outdir:"))
20 {
21 path = arg.Substring(8);
22 }
23 }
24
25 string outputFilename = System.IO.Path.Combine(path, dllName);
26 outputFilename += ".licenses";
27
28 System.IO.File.CreateText(outputFilename);
29
30 Console.ReadLine();
31 }
32 }


This worked for a while, but failed when I actually tried to use some of the irritating gui components that I hate. So I settled on another solution.
AutoHotKey
I wrote an autohot key script that lies in wait for the dialog to pop up, then it closes it. So now I never see the dialog and don't have to explore the guts of Visual Studio further, nor do I have to play nice with the irritating 3rd party.