Generate Monkey Code Smartly with StudioShell

§ October 28, 2011 01:09 by beefarino |

There is no shortage of code generation techniques available in Visual Studio.  There are even some neat PowerShell solutions that are based on the DTE.  Let me show you an approach I’ve used a few times – it’s a fairly special case, but it’s one of those problems that requires a high volume of monkey code that I frankly don’t want to spend a lot of time on.  It also demonstrates a few automation techniques you may not have considered before – such as driving other Visual Studio extensions like ReSharper.  My hope is that it gives you some fodder for exploring your own creative automation solutions with StudioShell.

The scenario: I need to take a COM interface and wrap it in a .NET class.  Why?  Long story for another post, but it has to do with getting PowerShell format definitions working for said COM interfaces. 

imageFor example, I’ve recently been working on a PowerShell Provider around the Windows Task Scheduler.  The Task Scheduler API is COM-based and consists of a handful of about 40 interfaces that need wrapping to work in my provider framework.

My standard workflow to create one of these wrappers is fairly wrote at this point:

  1. Define the wrapper class in C#;
  2. Add a private field holding a reference to the COM interface;
  3. Initialize the field in the wrapper class constructor;
  4. Use ReSharper’s Generate Delegating Members code generation feature to recreate the COM interface implementation in C#.

Not hard, but lots of easy that needs to get done.  First I’ll show how I accomplish this manually, then at how StudioShell can take the pain out of this boring work…

Manual Workflow

Here is how I would implement the wrapper for the simple IAction interface in the Task Scheduler COM type library.  The COM interface is defined as follows in C# code:

1 public interface IAction 2 { 3 string Id 4 { 5 get; 6 set; 7 } 8 9 _TASK_ACTION_TYPE Type 10 { 11 get; 12 } 13 }

Step 1: Define the wrapper class in C#

The C# wrapper classes I create are usually named after the interfaces by replacing the “I” in the interface name with “Shell”.  So, IAction becomes “ShellAction” in implementation:

1 public class ShellAction 2 { 3 }

Step 2: Add a private field holding the COM reference

With a basic class structure in place, I add a private field holding the IAction reference:

1 using TaskScheduler; 2 3 public class ShellAction 4 { 5 IAction _item; 6 }

Step 3: Initialize the COM reference in the class constructor

The IAction _item field needs to be initialized in the class constructor:

1 public class ShellAction 2 { 3 IAction _item; 4 5 public ShellAction(IAction item) 6 { 7 _item = item; 8 } 9 }

And to be honest, I don’t write this code – I use ReSharper to generate it for me.  I hit [alt]-[insert] and ReSharper pops up a list of code generations it can perform on the class, and I select Generate Constructor, and it does the magic for me.

imageStep 4: Generate delegating members using ReSharper

Once the basic class and field code is in place, I turn again to my second-favorite Visual Studio extension.  ReSharper ships with a badass function called “Generate Delegating Members” that does exactly what the name implies – it can generate method and property calls that forward to a class member.

Using a dialog to allow me to select what members I want to create delegating calls for, and once I click Finish to run this little gem, the class is code complete:

1 public class ShellAction 2 { 3 IAction _item; 4 5 public ShellAction(IAction item) 6 { 7 _item = item; 8 } 9 10 public string Id 11 { 12 get { return _item.Id; } 13 set { _item.Id = value; } 14 } 15 16 public _TASK_ACTION_TYPE Type 17 { 18 get { return _item.Type; } 19 } 20 }

Rocket science it ain’t.  Still, I have about 39 more of these things to create.  Time to automate.  Heh.  Poetry.

Automated Workflow

In a nutshell, I want to use StudioShell to drive this four step process for each interface in the Task Scheduler type library.  So, the first thing I need is a list of every interface I can find in that type library.  This is pretty easy to do since PowerShell has access to the entire .NET underpinnings:

1 §> $t = [appdomain]::currentdomain.getassemblies()` 2 | where { $_.fullname -match 'tasksch' } ` 3 | %{ $_.gettypes() } ` 4 | where { ` 5 $_.isinterface -and $_.name -match 'i' ` 6 }


This pipeline looks complicated, but it’s pretty simple when you break it down.  In English, it reads as “From all loaded assemblies (line 1) whose fullname contains the string ‘tasksch’ (line 2) get the public types (line 3) that are interfaces and start with the letter ‘I’ (line 5).”  The goal is to get the public interfaces from the Task Scheduler type library.  The results are stored in the variable $t, which contains what we expect:

1 § >$t 2 3 IsPublic IsSerial Name 4 -------- -------- ---- 5 True False ITaskFolderCollection 6 True False ITaskFolder 7 True False IRegisteredTask 8 True False IRunningTask 9 ...

Cool.  Now we have the list of interfaces we need to wrap.  Let’s start hacking.

Step 1: Define the wrapper class in C# ( 40 times )

To keep things simple, I’m going to dump all of these generated classes into a single new code file.  I can always break them out later if I choose to.  I navigate StudioShell to the project I’m working in:

§ >cd DTE:\solution\projects\scheduledtasks

and create a new code file:

§ >new-item -type codefile -name test.cs

Then I navigate StudioShell into the code model for this new empty file:

§ >cd ./test.cs/codemodel

Time to bring the awesome.  I want a new public class for each interface in $t, and I want the class name to be the same of the interface with the initial 'I’ replaced with “Shell”:

§ >$t | foreach { new-item -type class ` -name ($_.name -replace '^I','Shell') ` -access public } Location: dte:\...\test.cs\codemodel File: test.cs Code Container: Available Operations: d+ < Kind Name ---- ---- vsCMElementClass ShellTaskFolderCollection vsCMElementClass ShellTaskFolder vsCMElementClass ShellRegisteredTask ...

Taking a quick peek at the code now in test.cs, it would seem I have my 40 classes declared:

public class ShellTaskFolderCollection { } public class ShellTaskFolder { } public class ShellRegisteredTask { } // ...

Step 2: Add a private field holding the COM reference ( 40 times )

Next item we need is the private COM reference in each class.  I can accomplish this using the standard new-item cmdlet to create private member variables inside of each class:

1 § >$t | foreach { ` 2 $c = $_.name -replace '^I','Shell'; ` 3 new-item -path $c ` 4 -name _item ` 5 -membertype $_.name ` 6 -type variable } 7 8 Location: dte:\...\test.cs\codemodel\ 9 File: test.cs 10 Code Container: Class ShellTaskFolderCollection 11 Available Operations: d+ < 12 13 14 Kind Name 15 ---- ---- 16 vsCMElementVariable _item 17 18 ...

For each interface in $t, I derive the class name using the same replace logic I used in step 1; I store this class name in the variable $c (line 2).  My current location is the code model for the test.cs file, so I can use this class name as a relative path to the new-item cmdlet to create class members.  In lines 3-6, I do just that to create a private field named “_item.”  The type of field _item is set to the name of the Task Scheduler interface being processed.

Huh, looking at the code file I can see I need to add a using statement to let the compiler resolve those TaskScheduler type library references; I’ll just do that by hand to get it done:

using TaskScheduler; public class ShellTaskFolderCollection { ITaskFolderCollection _item; } public class ShellTaskFolder { ITaskFolder _item; } public class ShellRegisteredTask { IRegisteredTask _item; } //...

So far so good.

Step 3: Initialize the COM reference in the class constructor ( 40 times )

Now things get interesting.  I need to add a constructor that initializes the value of the COM reference.  I used ReSharper to do this before, but there’s no way to automate ReSharper is there?  Um, yeah, there is.

All Visual Studio extensions (and Visual Studio itself) expose handles to their functionality in the form of Command objects.  StudioShell dutifully exposes these commands as part of the DTE: drive.  If we can find the command, we can pass it to the standard invoke-item cmdlet to make it run.

So let’s find the command.  Knowing that it’s a ReSharper command, and that it has something to do with generating constructors, I can narrow my search using intuition:

§ >ls dte:/commands | where {` $_.name -match 'resharper' ` -and ` $_.name -match 'constructor' } Location: dte:\commands Available Operations: d+ < Name ---- ReSharper.ReSharper_Constructor2FactoryMethodAction ReSharper_Generate_Constructor

Hmmm, I’m not a betting man, but I’d still put money that the ReSharper_Generate_Constructor command is the one I’m after.

Now, a little Visual Studio SDK sidebar – when a command executes, it uses the context of the current user activity.  In the case of this particular ReSharper command, it will only execute successfully if I’m currently in a code editor window working on a class.  Moreover, I need to move the cursor to the class I want the command to operate on.  Starting to sound like more trouble than its worth?

Fear not grasshopper – StudioShell has your back yet again!  One of the newer features of StudioShell is the ability to “navigate” to specific code model elements using the invoke-item cmdlet.  This is a recent feature that will be available in the upcoming 1.2 release.

So back on track, we want to invoke the ReSharper_Generate_Constructor command on each of our classes in test.cs.  So, that’s what we do:

1 § >ls -recurse | where {$_ -match 'class' } | foreach { 2 $_ |invoke-item; 3 invoke-item ` 4 dte:/commands/ReSharper_Generate_Constructor 5 }

imageLine 1 isolates the classes in test.cs; each class code item is first “invoked,” causing the containing document to activate and the cursor to move to the class code (line 2), then the ReSharper Generate Constructor command is invoked (lines 3-4). 

The result – the ReSharper Generate Constructor dialog is opened once for each class.  Unfortunately there is no way I know of to automatically select what members to initialize and then dismiss the dialog.  I’m sure there is, and I’d love to hear about it in the comments.  But right now I’d rather be more done than smart.  So, I hit [space]-[enter] 40 times like a schlep.

And the code looks right:

1 using TaskScheduler; 2 3 public class ShellTaskFolderCollection 4 { 5 ITaskFolderCollection _item; 6 7 public ShellTaskFolderCollection(ITaskFolderCollection item) 8 { 9 _item = item; 10 } 11 } 12 13 public class ShellTaskFolder 14 { 15 ITaskFolder _item; 16 17 public ShellTaskFolder(ITaskFolder item) 18 { 19 _item = item; 20 } 21 } 22 23 public class ShellRegisteredTask 24 { 25 IRegisteredTask _item; 26 27 public ShellRegisteredTask(IRegisteredTask item) 28 { 29 _item = item; 30 } 31 } 32 33 // ...

Step 4: Generate delegating members using ReSharper ( 40 times )

One last step and I’m done.  I need to run the ReSharper Generate Delegating Members command on each class.  This isn’t that different from what I had to do in step 3, and since I’ve taken the mystique out of automating ReSharper I know exactly what to do.

First, find the command.  I’ll use the same intuition that worked for me in step 3:

1 § >ls dte:/commands | where { ` 2 $_.name -match 'resharper' ` 3 -and ` 4 $_.name -match 'delegat' } 5 6 7 Location: PSDTE::dte:\commands 8 Available Operations: d+ < 9 10 Name 11 ---- 12 ReSharper_Generate_Delegating

Well lookey what we have here…  Hello there ReSharper_Generate_Delegating command!  Prepare to be automated!

This command works like the ReSharper_Generate_Constructor command – input focus has to be in a class code element in a text editor window.  But after solving step 3 this is no issue.  In fact, the solution for step 4 looks almost identical to step 3:

1 § >ls -recurse | where {$_ -match 'class' } | foreach { 2 $_ |invoke-item; 3 invoke-item ` 4 dte:/commands/ReSharper_Generate_Delegating 5 }

The only thing I had to change was the name of the command.

Again I get about 40 dialogs, one for each class, asking me what fields I want to create delegating members for, and as before I just type [space]-[enter] until the UIs disappear.

The code looks accurate:

1 using TaskScheduler; 2 3 public class ShellTaskFolderCollection 4 { 5 ITaskFolderCollection _item; 6 7 public ShellTaskFolderCollection(ITaskFolderCollection item) 8 { 9 _item = item; 10 } 11 12 public IEnumerator GetEnumerator() 13 { 14 return _item.GetEnumerator(); 15 } 16 17 public int Count 18 { 19 get { return _item.Count; } 20 } 21 22 public ITaskFolder this[object index] 23 { 24 get { return _item[index]; } 25 } 26 } 27 28 public class ShellTaskFolder 29 { 30 ITaskFolder _item; 31 32 public ShellTaskFolder(ITaskFolder item) 33 { 34 _item = item; 35 } 36 37 public ITaskFolder GetFolder(string Path) 38 { 39 return _item.GetFolder(Path); 40 } 41 42 public ITaskFolderCollection GetFolders(int flags) 43 { 44 return _item.GetFolders(flags); 45 } 46 47 // ... 48 } 49 50 public class ShellRegisteredTask 51 { 52 IRegisteredTask _item; 53 54 public ShellRegisteredTask(IRegisteredTask item) 55 { 56 _item = item; 57 } 58 59 public IRunningTask Run(object @params) 60 { 61 return _item.Run(@params); 62 } 63 64 public IRunningTask RunEx(object @params, int flags, int sessionID, string user) 65 { 66 return _item.RunEx(@params, flags, sessionID, user); 67 } 68 69 //... 70 } 71 72 // ...

 

… and it even builds (read: SHIP IT!!).  2500+ lines of monkey code created in 5 lines of powershell, all in all about 10 minutes of exploring. 

Hindsight

This little demo show some pretty powerful automation techniques – honestly I find the notion of automating other automation tools compelling.  A few things I’d like you to keep in mind as you explore your own automations:

  1. I’m automating my workflow, not my coding.  That is, my focus is on replicating the activities I do manually in Visual Studio to yield the code I want in the end.  In this case that means driving another automation tool.  I’m not looking for fancy or even “best practice” with this, I’m looking for done. 
  2. I’m not plotting a general solution I plan to reuse.  Sure I’ll employ some of the techniques again, but it’s actually easier (for me at least) to treat each new workflow as a new automation opportunity.  Moreover, trying to generalize this solution now would be a waste of time, since experience tells me that the next situation will be just different enough to force me to change my approach.
  3. I don’t focus on automation alone – that is, when I encounter a simple snag – such as the missing “using TaskScheduler;” statement in the code file, I just fix it by hand and move on instead of revisiting my automation technique to make it work perfectly.
  4. I’m automating something I’ve done manually several times.  So I have clear expectations of what needs doing and the path to follow.  My rule of thumb is not to automate until I can recite the steps I’ve taken before my memory.

Enjoy!



grok-posh: Interactive .NET

§ May 5, 2011 01:51 by beefarino |

grokbulb1

This post is part of my grok-posh series describing why developers should learn PowerShell.

A PowerShell console is basically an doorway into the entire realm that is .NET, COM, and WMI.  As I say in my talks – you can think of PowerShell as an interactive way to discover, obtain, and manipulate objects.

This makes it a great place to explore the things you can do and to learn about these objects by manipulating them.

Here’s a great example - the other day @shaylevy posted this little gem on twitter:

$error | % { (New-Object -ComObject SAPI.SPVoice).Speak($_.Message) }

Shay’s code is using the Microsoft Speech API to have the computer “read” the error message(s) raised by the last PowerShell command executed.  Slick.

I’ve never played with the Speech API.  Let’s see how I can use PowerShell to interactively learn what I can do with this object:

1 PS > $voice = New-Object -ComObject SAPI.SPVoice 2 PS > $voice | get-member 3 4 5 TypeName: System.__ComObject#{269316d8-57bd-11d2-9eee-00c04f797396} 6 7 Name MemberType Definition 8 ---- ---------- ---------- 9 DisplayUI Method void DisplayUI (int, string, string, Variant) 10 GetAudioOutputs Method ISpeechObjectTokens GetAudioOutputs (string, string) 11 GetVoices Method ISpeechObjectTokens GetVoices (string, string) 12 IsUISupported Method bool IsUISupported (string, Variant) 13 Pause Method void Pause () 14 Resume Method void Resume () 15 Skip Method int Skip (string, int) 16 Speak Method int Speak (string, SpeechVoiceSpeakFlags) 17 SpeakCompleteEvent Method int SpeakCompleteEvent () 18 SpeakStream Method int SpeakStream (ISpeechBaseStream, SpeechVoiceSpeakFlags) 19 WaitUntilDone Method bool WaitUntilDone (int) 20 AlertBoundary Property SpeechVoiceEvents AlertBoundary () {get} {set} 21 AllowAudioOutputFormatChangesOnNextSet Property bool AllowAudioOutputFormatChangesOnNextSet () {get} {set} 22 AudioOutput Property ISpeechObjectToken AudioOutput () {get} {set by ref} 23 AudioOutputStream Property ISpeechBaseStream AudioOutputStream () {get} {set by ref} 24 EventInterests Property SpeechVoiceEvents EventInterests () {get} {set} 25 Priority Property SpeechVoicePriority Priority () {get} {set} 26 Rate Property int Rate () {get} {set} 27 Status Property ISpeechVoiceStatus Status () {get} 28 SynchronousSpeakTimeout Property int SynchronousSpeakTimeout () {get} {set} 29 Voice Property ISpeechObjectToken Voice () {get} {set by ref} 30 Volume Property int Volume () {get} {set} 31

Line 1 creates a new instance of the SAPI.SPVoice object in the shell variable $voice.  Line 2 passes the $voice variable to the get-member cmdlet, which outputs all the methods and properties that the COM object supports.

Lots of opaque methods there – some UI elements it looks like, some asynchronous hooks.  Looks like we can change the rate at which the speech is spoken, the volume, oooohhh – looks like there are other voice options too!  Let’s see what voices we have available:

PS > $voice.GetVoices() Id DataKey Category -- ------- -------- HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Token... System.__ComObject System.__ComObject

Aw, sad trombone …  we have only one voice available.  Hey, what’s it sound like anyway?

PS > $voice.Speak( 'Hello Beef!' )
Hello yourself!  Not bad.  What does that Rate property do?

PS > $voice.Rate 0 PS > $voice.Rate = 5 PS > $voice.Speak( 'The build has failed!' ) 1 PS > # oh dear, that has the potential to raise my stress level PS > # let's try to get more of a southern drawl going... PS > $voice.Rate = -5 PS > $voice.Speak( 'Oh no! The build, she has failed!' ) 1 PS > # now that's more like it!

Neat.  As much fun as I’m having, I’m learning a bit about the Speech API.  PowerShell allows me to interact with the object, trying things out and testing theories.  Will I eventually need to turn to MSDN if I want to do anything serious?  Probably.  But consider what I’ve learned so far using nothing but PowerShell for about 60 seconds:

  1. I know the methods and properties exposed by the Speech API voice object;
  2. I know that voices are somehow linked to my registry, and the API suggests that it’s possible to install and remove voices, or create my own;
  3. Based on the methods of the voice object, I can assume there is a way to have the API generate speech asynchronously;
  4. I can manipulate the properties of a voice to my liking.  In fact, using PowerShell I can get immediate feedback on what the voice sounds like.

You can generalize this interaction to almost any object if you know a few key PowerShell cmdlets.  Here I use these key cmdlets to discover how I can work with windows services in my PowerShell session:

1 PS >#what commands apply to services? 2 PS >get-command *service 3 4 CommandType Name Definition 5 ----------- ---- ---------- 6 Cmdlet Get-Service Get-Service [[-Name] <String[]>] [-ComputerName <String[]>] [-DependentServices... 7 Cmdlet New-Service New-Service [-Name] <String> [-BinaryPathName] <String> [-DisplayName <String>]... 8 Cmdlet Restart-Service Restart-Service [-Name] <String[]> [-Force] [-PassThru] [-Include <String[]>] [... 9 Cmdlet Resume-Service Resume-Service [-Name] <String[]> [-PassThru] [-Include <String[]>] [-Exclude <... 10 Cmdlet Set-Service Set-Service [-Name] <String> [-ComputerName <String[]>] [-DisplayName <String>]... 11 Cmdlet Start-Service Start-Service [-Name] <String[]> [-PassThru] [-Include <String[]>] [-Exclude <S... 12 Cmdlet Stop-Service Stop-Service [-Name] <String[]> [-Force] [-PassThru] [-Include <String[]>] [-Ex... 13 Cmdlet Suspend-Service Suspend-Service [-Name] <String[]> [-PassThru] [-Include <String[]>] [-Exclude ... 14 15 16 PS >#looks promising. what does get-service do? 17 PS >get-help get-service 18 19 NAME 20 Get-Service 21 22 SYNOPSIS 23 Gets the services on a local or remote computer. 24 25 26 SYNTAX 27 Get-Service [[-Name] <string[]>] [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParameters>] 28 29 Get-Service -DisplayName <string[]> [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParameters>] 30 31 Get-Service [-InputObject <ServiceController[]>] [-ComputerName <string[]>] [-DependentServices] [-Exclude <string[]>] [-Include <string[]>] [-RequiredServices] [<CommonParamete 32 rs>] 33 34 35 DESCRIPTION 36 The Get-Service cmdlet gets objects that represent the services on a local computer or on a remote computer, including running and stopped services. 37 38 You can direct Get-Service to get only particular services by specifying the service name or display name of the services, or you can pipe service objects to Get-Service. 39 40 41 RELATED LINKS 42 Online version: http://go.microsoft.com/fwlink/?LinkID=113332 43 Start-Service 44 Stop-Service 45 Restart-Service 46 Resume-Service 47 Suspend-Service 48 Set-Service 49 New-Service 50 51 REMARKS 52 To see the examples, type: "get-help Get-Service -examples". 53 For more information, type: "get-help Get-Service -detailed". 54 For technical information, type: "get-help Get-Service -full". 55 56 57 58 PS >#ok, now let's see what we can do with a service... 59 PS >get-service | get-member 60 61 62 TypeName: System.ServiceProcess.ServiceController 63 64 Name MemberType Definition 65 ---- ---------- ---------- 66 Name AliasProperty Name = ServiceName 67 RequiredServices AliasProperty RequiredServices = ServicesDependedOn 68 Disposed Event System.EventHandler Disposed(System.Object, System.EventArgs) 69 Close Method System.Void Close() 70 Continue Method System.Void Continue() 71 CreateObjRef Method System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType) 72 Dispose Method System.Void Dispose() 73 Equals Method bool Equals(System.Object obj) 74 ExecuteCommand Method System.Void ExecuteCommand(int command) 75 GetHashCode Method int GetHashCode() 76 GetLifetimeService Method System.Object GetLifetimeService() 77 GetType Method type GetType() 78 InitializeLifetimeService Method System.Object InitializeLifetimeService() 79 Pause Method System.Void Pause() 80 Refresh Method System.Void Refresh() 81 Start Method System.Void Start(), System.Void Start(string[] args) 82 Stop Method System.Void Stop() 83 ToString Method string ToString() 84 WaitForStatus Method System.Void WaitForStatus(System.ServiceProcess.ServiceControllerStatus desiredStatus), System.Void WaitForStatus(System.ServiceProcess.Se... 85 CanPauseAndContinue Property System.Boolean CanPauseAndContinue {get;} 86 CanShutdown Property System.Boolean CanShutdown {get;} 87 CanStop Property System.Boolean CanStop {get;} 88 Container Property System.ComponentModel.IContainer Container {get;} 89 DependentServices Property System.ServiceProcess.ServiceController[] DependentServices {get;} 90 DisplayName Property System.String DisplayName {get;set;} 91 MachineName Property System.String MachineName {get;set;} 92 ServiceHandle Property System.Runtime.InteropServices.SafeHandle ServiceHandle {get;} 93 ServiceName Property System.String ServiceName {get;set;} 94 ServicesDependedOn Property System.ServiceProcess.ServiceController[] ServicesDependedOn {get;} 95 ServiceType Property System.ServiceProcess.ServiceType ServiceType {get;} 96 Site Property System.ComponentModel.ISite Site {get;set;} 97 Status Property System.ServiceProcess.ServiceControllerStatus Status {get;} 98 99 100 PS > 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

The key PowerShell commands: get-command (line 2), get-help (line 17), and get-member (line 59) help you find the objects you’re looking for and tell you what it is they can do.



Why Developers Need to Know PowerShell

§ May 5, 2011 00:23 by beefarino |

smartbulbA few weeks back a colleague of mine asked a great question on twitter:

powershell sounds cool, but i don't use it. why should i learn as a dev?

This is the question I’ve been trying to answer for the development community at user groups and conferences for the last two years.  The short answer is that groking PowerShell makes your life easier.  Technically this is true for any Windows user, so let’s explore the benefits that are specific to developers…

This “grok-posh” series of posts will outline why you should get familiar with PowerShell now.  Some of these will be specific to developers, some more general to powerusers.  As I complete posts I’ll update the links below.

  • Interactive .NET
  • Awesome Build and Test Tools
  • Typing is Faster than Searching
  • It’s Glue that Sticks to Everything
  • Windows Management and Support
  • Agile Applications (no, not that Agile, the other Agile from the 90’s)
  • Immediate Integration with Awesome Tools
  • Command Pattern Platform
  • Repository Pattern Platform

If there is anything you think should be added to this list, please drop me a note via the contact form or twitter.



Announcing StudioShell

§ January 11, 2011 16:33 by beefarino |

Last September I came up with a project idea, and since the end of the year tends to slow down for contractors like myself I made plans to pursue the project and see how far I could take it in a few weeks.  So it is with great pleasure that I introduce you to my latest brainchild: StudioShell. 

studioshellStudioShell is a deeply integrated PowerShell host available inside of Visual Studio 2010 and 2008.  It’s goal is to fundamentally change the way you interact with your IDE and your code. 

You’re probably thinking “we already have nuGet” or “we already have PowerGui VSX.”  So why another PowerShell host in Visual Studio?   

If you’ve ever implemented a Visual Studio extension, such as an add-in or a package, you know how convoluted this space has become.  You have to be aware of the many commandments of COM.  You have to research the various services offered by the shell.  The API is inconsistent and opaque.  The documentation is lax.  In short, you have to become an expert in your tooling if you want to change it.

StudioShell exposes many of Visual Studio’s extensibility points in a simple and consistent way, and it makes the Visual Studio DTE interactive and discoverable.  What an add-in does in a compiled binary, StudioShell can accomplish with a one-liner.

I’ve made a few screencasts to show off what StudioShell can do.  I’d love to hear any feedback you want to provide…

If you’d like to stay on beat with this project, please follow StudioShell on twitter and keep an eye on this blog.  I’ll be soliciting beta participants in a few weeks.