Huddled Masses

The internet home of Joel "Jaykul" Bennett...

ShowUI: the tutorial walkthrough

By Joel 'Jaykul' Bennett on 14-Jun-2011

Ok, let’s be clear: you’ve seen this before, and this isn’t going to be the last you’ll see of it, because we’ve got much more in store in ShowUI, but for the first release of ShowUI, it’s obviously time to update this simple walkthrough of building simple user interfaces in PowerShell!

An introduction to ShowUI

ShowUI is the next generation PowerShell module for building user interfaces in script. It’s the merger of my previous PowerShell UI project (called PowerBoots) with one by James Brundage, former Microsoft PowerShell team member and founder of Start-Automating (called WPK) which shipped in the Windows 7 resource kit.

If you want to follow along, you need to:

1. Get a copy of the ShowUI Module from CodePlex
2. Install it by putting the “ShowUI” folder in one of your “Modules” folders (list them by typing $Env:PSMODULEPATH in PowerShell v2, and feel free to create the one you want if it doesn’t exist). You should end up with something like C:\Users\Jaykul\Documents\WindowsPowerShell\Modules\ShowUI\ShowUI.psm1
3. Run PowerShell ISE, or use PowerShell.exe with the -STA switch (the best way to do this is to add it to the shortcut you use to launch PowerShell: open the properties dialog, and on the Shortcut tab, add “ -STA” to the Target)1.

Did I hear someone ask what is WPF? It was introduced as part of .Net 3.0 (and vastly improved in .Net 3.5, and again in 4.0), so you can expect to find it preinstalled on computers from Vista on, and of course you can download and install it on XP if it’s not already installed. The only thing you really need to know about WPF for the purposes of this tutorial is that it is the new GUI toolkit for .Net, and that it is container based — you put elements into other elements to control the layout, rather like HTML and Java Swing… you can pick up the rest as we go along.

A simple ShowUI program


New-Button -Content "Hello World" -Show
 

This first example is a little bit more verbose than it needs to be, because the -Content parameter is positional, so the first non-named argument you pass will be used for that. The same is true for the -Children parameter of panels, and in fact, each of the other similar content parameters: Items, Blocks, and Inlines.

Additionally, each control is aliased without the New- verb, so you could just call Button instead of New-Button … and of course, since our button doesn’t do anything, we could just as easily have used a Label, and written:


Label "Hello World" -Show
 

Note: “Label” is also the name of an executable for labelling drives in Windows, make sure ShowUI is imported before you run that command.

One last point: ShowUI brings a lot of features from WPK to the table, and one you’ll use a lot is the ability to skip specifying the window and put the -Show parameter on almost any WPF element. This is partly because we’re back to running PowerShell 2.0 with the -STA switch, but in any case, we can now skip the “New-BootsWindow” command, and our examples can be that much simpler.

We can put controls in a stack


Show-UI {
    StackPanel {
        Button "A bed of clams"
        Button "A coalition of cheetas"
        Button "A gulp of swallows"
    }
}
 

StackPanels are awesome. So are WrapPanels. Try that code with a WrapPanel instead of a StackPanel and see what the difference is (hint: try resizing the window). Actually, try it with a UniformGrid too. WPF has several other panel containers too: Grid, ToolBarPanel, TabPanel, DockPanel, Canvas … we’ll talk about some more of those later.

Ok, lets see some formatting

To scoot the buttons out from the edge we can use margins or padding: margins go on the outside of containers, padding goes on the inside. We can also specify the FontFamily, FontSize, FontWeight, and FontStyle, as well as Foreground and Background colors …


Show-UI {
    StackPanel -Margin 5 -Background Pink {
        Button -Margin 2 "A bed of clams" -FontFamily Consolas -FontSize 24 -FontWeight Bold
        Button -Margin 2 "A coalition of cheetas" -FontFamily Arial -FontSize 20 -FontStyle Italic
        Button -Margin 2 "A gulp of swallows" -FontFamily 'Segoe UI' -FontSize 18 -Foreground Crimson
    }
}
 

So you see, the pink background is on the StackPanel, which has a (default, white) margin around it. If you wanted the whole background of the window to be pink, you would need to set the background of the Window instead of the StackPanel.

An aside on Typography

ShowUI doesn’t need to create a full set of typography-specific top-level elements the way Shoes does, because we are based on WPF, which has a far more powerful typography system available than any we’ve used previously. So, with WPF we start by selecting a control based on how much text you want to put in it, and how much formatting you want to apply: Label and TextBox are the simplest, TextBlock supports limited text formattings, and FlowDocumentViewer or RichTextBox support full rich content. And of course, Hyperlink supports clicking ;)

For the typography elements, the content model changes a little bit. There are basically two types of typographical elements: Inline and Block elements (where inline elements can’t contain block elements). In fact, he TextBlock Content Model is similar to that of an inline element. It is actually a type-restricted “Items” container. Instead of being able to have anything as content, it can only contain Inline flow content elements such as AnchoredBlock, Bold, Hyperlink, InlineUIContainer, Italic, LineBreak, Run, Span, and Underline, and it will create a run automatically if you just put a text string in it.


Show-UI {
   StackPanel -Margin 10 -Children {
      TextBlock "A Question" -FontSize 42 -FontWeight Bold -Foreground "#FF0088"
      TextBlock -FontSize 24 -Inlines {
         Bold "Q. "
         "Are you starting to dig "
         Hyperlink "ShowUI?" -NavigateUri http://huddledmasses.org/tag/showui/ `
                                 -On_RequestNavigate { [Diagnostics.Process]::Start( $this.NavigateUri.ToString() ) }
      }
      TextBlock -FontSize 16 -Inlines {
         Span -FontSize 24 -FontWeight Bold -Inlines "A. "
         "Leave me alone, I'm hacking here!"
      }
}
}
 

Note: If you want support for the full document model (which allows Paragraphs and Lists), you need to use a FlowDocumentReader, FlowDocumentPageViewer, RichTextBox, or a FlowDocumentScrollViewer ... there’s lots more information about those on msdn.

Time for some artwork


Ellipse -Width 60 -Height 80 -Margin "20,10,60,20" -Fill Black -Show
 

In WPF, everything always starts out white, and you must position things based on the container. You can see that the Margin can be specified as a single value as in the previous example, or as separate values for Left, Top, Right, Bottom. Oddly, to satisfy PowerShell’s type-casting, you have to quote them so they’re a single comma-separated string, instead of four separate values.

Some more advanced drawing


Canvas -Height 100 -Width 100 -Children {
   Rectangle -Margin "10,10,0,0" -Width 45 -Height 45 -Stroke "#689945" -StrokeThickness 2 -Fill "#336699"
   Polygon -Stroke Pink -StrokeThickness 2 -Fill DarkRed -Points "10,60", "50,60", "50,50", "65,65",
                                                               "50,80", "50,70", "10,70", "10,60"
} -Show
 

When you want to start getting clever and overlapping things, you need to use a Canvas container. The Canvas can contain multiple items which are all absolutely positioned, but unlike most other containers, it doesn’t automatically expand to contain it’s children, so you typically have to set it’s size.

We also have to set the Stroke and Fill. These are the two colors that make up every object, and again, if we don’t set them, they default to white. Note that you can use named colors, or you can specify a hex value using “#RRGGBB” or “#AARRGGBB” to set the alpha channel. The StrokeThickness controls the line thickness.

One other thing to notice is that we positioned the Rectangle by using the Margin, but we positioned the arrow, which we built using a Polygon, based purely on the x,y coordinates of the points. The available shapes are Ellipse, Line, Path, Polygon, Polyline, and Rectangle. You can, of course, make nearly any shape you want with the Polygon.

There are other more advanced shapes available in external libraries, and we can even do 3D, use gradient or image fills…

We can even get images straight off the web


   Image -Source http://huddledmasses.org/images/PowerBoots/IMG_3298.jpg -MaxWidth 400 -AsJob
 

WPF loads the image on a background thread, and caches it in memory, so the window will show up and be responsive while you’re waiting for the image, and because we’ve specified -AsJob, you can actually continue using PowerShell while the image loads. Note: it will load much faster the second time you run that script. ;)

Events

If you were paying attention to the typography example, you’ll have noticed that we introduced event handling without making a big fuss about it. Event handlers in PowerBoots are specified in much the same way that Properties are, except that their parameter names always start with “On_” and they take a script block. The Hyperlink element in a WPF window doesn’t automatically open a browser (because you can use it to change “pages” in a WPF application), so to make simple web links work, you have to handle the “RequestNavigate” event as shown above.


WrapPanel -ControlName 'Click Counter' {
    Button "Push Me" -On_Click {
        ${Click Counter}.Tag = ${Click Counter}.Tag + 1
        $CountLabel.Content = "You clicked the button $(${Click Counter}.Tag) times!"
    }
    Label "Nothing pushed so far" -Name CountLabel
} -Show
 

In order to update your user interface when an event triggers, you’ll need to have variables that point at the control(s) you want to affect. In ShowUI event handlers, you get a $this variable which points at the object that caused the event, a $_ variable which is the event arguments, and a $window variable for the top-level window … you also get variables for each named control in your window.

WPF Elements all have a Tag property which can be used to store any object, so as you can see in the previous example, you can use that to keep track of things … but more importantly, the value of the Tag on the top-level control will be output. In other words, when the window is closed, this example actually outputs to the PowerShell pipeline how many times you clicked the button.

Working with User Input

There are helper functions in ShowUI for working with that output value: Set-UIValue and Get-UIValue, and Get-UIValue is particularly helpful because if the control it’s called on has no value, it collects the values of all the child controls, so you can use it to output a whole form at once:

I’ve made this example more complicated than it needed to be to demonstrate some best practices. We could have made it much simpler by using a UniformGrid control instead of a GridControl, thus avoiding needing to set the -Row and -Column special parameters, but I wanted to show those to you anyway, and the form looks a lot better when the two columns can have different sizes:


Grid -ControlName 'Your Information' -Columns Auto,* -Rows 7 -MinHeight 200 -MinWidth 250 {
    Label "First Name"
    New-TextBox -Name First -Column 1
   
    Label "Last Name" -Row 1
    New-TextBox -Name Last -Row 1 -Column 1

    Label "Address" -Row 2
    New-TextBox -Name Address -Row 2 -Column 1

    Label "City" -Row 3
    New-TextBox -Name City -Row 3 -Column 1

    Label "State" -Row 4
    New-TextBox -Name State -Row 4 -Column 1

    Label "Zip" -Row 5
    New-TextBox -Name Zip -Row 5 -Column 1

   
    New-Button "OK" -IsDefault -On_Click {
        Get-ParentControl | Set-UIValue -PassThru | Close-Control
    } -Row 6 -Column 1
} -Show -On_Loaded { $First.Focus() }
 

You’ll notice how easily I specified the width of the columns, but the COUNT of the rows in the Grid panel. If you provide an array of values, they’re converted to GridLengths, but if you provide just one, it’s treated as the count. You can make grid columnds and rows AUTOmatically size to their contents, and you can make them * width to make them take up any extra space. You can even split the extra space by setting * on more than one column (and specify the proportion by using numbers, like: 1*, 2*).

I should also point out that if you use MinHeight and MinWidth instead of the Height and Width values, your controls will be able to size up to fill space when the window is resized! Try that script with Width and Height instead and resize the window to see the difference.

We used the Set-UIValue shortcut in this form, which brings up another point: when creating data forms, you only need to name the controls you want output from. Then you’ll be able to just call Set-UIValue on the parent to collect all the values from the controls and output them as a hashtable!

Finally, remember your defaults: set the focus to something so the user doesn’t HAVE to click to get started, and set a button to -IsDefault with a On_Click handler so that when the user hits enter they can submit the form.

Further directions

There’s a lot more possible with ShowUI: we can use gradients for colors, create data templates and styles, and even make chromeless windows, but you have the basics for getting started already.

This first release of ShowUI is still missing some features from both WPK and PowerBoots, but we’ll get to them as we progress. For now we’d like to invite you to come download the latest release, and write up or vote up the features that you want the most in the next version. Keep an eye on the release page and on the discussions and we’ll be cranking out new releases on a monthly basis for now.

I hope you’ve enjoyed this tour through ShowUI, and will be able to start applying it soon for fun and profit.

1 The -STA switch is necessary because the .Net framework requires the STA threading model in order to do graphical user interfaces. PowerShell ISE doesn’t require it because unlike the PowerShell.exe command console (which is a Win32 native “console” application that hosts the .Net framework), PowerShell ISE is a .Net framework “graphical” application, and is therefore running in the STA model already.

Posted in Huddled | Tagged PowerBoots, PowerShell, ShowUI, Tutorial, UserInterface, WalkThrough | 4 Responses

The problem with calling legacy/native apps from PowerShell

By Joel 'Jaykul' Bennett on 10-Mar-2011

This post is an explanation of the major problems with invoking native apps from PowerShell 2.0, and the simple work-around. There is going to be a little bit of code and then quite a bit of sample output (along with some pointers so you don’t have to play spot the differences). In order to avoid keeping you waiting while I get to the workaround, I’ll let out the secret right here at the top, because it is really very simple: use Start-Process and a here-string to make sure PowerShell does not mess with your arguments (more on this later).

So, first, to demonstrate the differences, lets create an executable which will use various methods to access its command-line parameters and print them out. We will do this in C# although you could do it in C++ and see some variations because the .Net runtime does some parsing of it’s own, which is a little weird — but this simple example should be enough to demonstrate the differences.

The code here is in PowerShell format, so you can simply paste it into PowerShell and get an executable as output, which you can then move to C:\Windows\System32 for testing purposes.


Add-Type -Type @"
using System;
internal class ArgsTest
{
   private static void Main(string[] args)
   {
      Console.WriteLine();
      /*
         I've commented this out because (at least in C#)
         it is the same as Environment.GetCommandLineArgs()
         Except that GetCommandLineArgs shows parameter 0 as the executable path
      */
      // Console.WriteLine("
Using args:");
      // Console.WriteLine();
      // for (int i = 0; i < args.Length; i++)
      // {
      //    Console.WriteLine("
  Arg {0} is: {1}", i, args[i]);
      // }
      // Console.WriteLine();
      // Console.WriteLine();
     
      Console.WriteLine("
CommandLine:");
      Console.WriteLine();
      Console.WriteLine("
  " + Environment.CommandLine);
      Console.WriteLine();
      Console.WriteLine();
     
      Console.WriteLine("
CommandLineArgs:");
      Console.WriteLine();
      string[] arguments = Environment.GetCommandLineArgs();
      for (int i = 0; i < arguments.Length; i++)
      {
         Console.WriteLine("
  Arg {0} is: {1}", i, arguments[i]);
      }
      Console.WriteLine();
      Console.WriteLine();
     
      System.Threading.Thread.Sleep(10000);
   }
}
"
@ -OutputAssembly ArgsTest.exe -OutputType ConsoleApplication
 

To test things, we are going to invoke it in PowerShell, in CMD.exe (DOS), and using the Run dialog (and finally, using Start-Process, to demonstrate that it has the same output as the Run dialog).

A note about path problems

In order to see the differences in how paths are handled, I put ArgsTest.exe in C:\Windows\System32\ — this allows me to call it with no path at all, with a full path, or with a partial path. That turned out to be important because the first difference of PowerShell is that it always executes the app with a FULL path, even if you’re calling it with a partial path.

The reason for this is simple: PowerShell doesn’t ever set the “CurrentDirectory” in it’s operating evironment when you Set-Location (presumably because they figured that often that location would be in a non-FileSystem provider). Because it doesn’t set the current directory, it always needs to invoke applications and scripts with their full path name.

The test case

We will execute the following (long) command-line:


ArgsTest.exe -this is, a, $Home, %UserProfile%   /test  -extra spaces /slashes:"including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single $Home quotes'
 

And to get us started, lets look at what that looks like when you execute it from cmd:


CommandLine:

   ArgsTest.exe  -this is, a, $Home, C:\Users\jbennett   /test  -extra spaces /slashes:"including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single $Home quotes'


CommandLineArgs:

   Arg 0 is: ArgsTest.exe
   Arg 1 is: -this
   Arg 2 is: is,
   Arg 3 is: a,
   Arg 4 is: $Home,
   Arg 5 is: C:\Users\jbennett
   Arg 6 is: /test
   Arg 7 is: -extra
   Arg 8 is: spaces
   Arg 9 is: /slashes:including spaces
   Arg 10 is: -dashes:including 'quotes'
   Arg 11 is: -and:'some
   Arg 12 is: colons'
   Arg 13 is: /with:'single
   Arg 14 is: $Home
   Arg 15 is: quotes'
 

Notice that in this output, Environment.CommandLine is exactly what was typed!
Now, let’s see the same thing from the run dialog:


CommandLine:

   "C:\Windows\system32\ArgsTest.exe" -this is, a, $Home, C:\Users\jbennett   /test  -extra spaces /slashes:"including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single $Home quotes'


CommandLineArgs:

   Arg 0 is: C:\Windows\system32\ArgsTest.exe
   Arg 1 is: -this
   Arg 2 is: is,
   Arg 3 is: a,
   Arg 4 is: $Home,
   Arg 5 is: C:\Users\jbennett
   Arg 6 is: /test
   Arg 7 is: -extra
   Arg 8 is: spaces
   Arg 9 is: /slashes:including spaces
   Arg 10 is: -dashes:including 'quotes'
   Arg 11 is: -and:'some
   Arg 12 is: colons'
   Arg 13 is: /with:'single
   Arg 14 is: $Home
   Arg 15 is: quotes'
 

The thing I want you to notice is that the results are almost exactly the same. The only difference is that cmd shows you just “ArgsTest.exe” which is what was actually typed, whereas the Run dialog inserts the full path to the executable.

But now, take a look at that same command in PowerShell:


CommandLine:

   "C:\Windows\system32\ArgsTest.exe"  -this is a C:\Users\jbennett %UserProfile% /test -extra spaces "/slashes:including spaces" "-dashes:"including 'quotes'"" "-and:"some colons"" "/with:single $Home quotes"


CommandLineArgs:

   Arg 0 is: C:\Windows\system32\ArgsTest.exe
   Arg 1 is: -this
   Arg 2 is: is
   Arg 3 is: a
   Arg 4 is: C:\Users\jbennett
   Arg 5 is: %UserProfile%
   Arg 6 is: /test
   Arg 7 is: -extra
   Arg 8 is: spaces
   Arg 9 is: /slashes:including spaces
   Arg 10 is: -dashes:including
   Arg 11 is: 'quotes'
   Arg 12 is: -and:some
   Arg 13 is: colons
   Arg 14 is: /with:single $Home quotes
 

A Diagnosis

The one thing we expected, of course, is that Powershell interpreted the $Home variable instead of the UserProfile variable, but that’s hardly the most significant difference …

In an attempt to translate what you wrote into what they think you meant, PowerShell has rewritten all of the quoting in the command line!

  • First, since PowerShell understands that commas are simply array separators, they simply remove them completely when calling a “native” app. PowerShell is actually taking the array, and converting the array into a string with space separators. They don’t seem to respect $OFS when doing this, so I don’t really know what the justification is for doing it.
  • Second, PowerShell changes single quoted strings into double quoted strings (without expanding the variables) to make sure that what PowerShell thinks of as strings are also thought of as strings by the target application (of course, they wouldn’t be strings in DOS, but this is PowerShell). If your executable depends on getting single quotes in the arguments, you’re going to have some pain because of that, otherwise, you won’t even notice.
  • Third, PowerShell actually MOVES some quotes, like the one after /slashes … Obviously PowerShell doesn’t treat / as a switch, so it treats them as any other string character. Since in PowerShell, if you don’t quote the WHOLE string, you can still start quoting halfway through it when you realize you need to type spaces … they move the quote to the start of the string so that your app will see the same thing a cmdlet would have.
  • Finally, PowerShell INSERTS extra quotes around -dashes. This is the one thing I can’t really explain right now. PowerShell inserts extra quotes which result in what looks like a real problem in the CommandLine string due to double-double quotes … but somehow those escape parsing as an escaped double quote character when you look at the output of GetCommandLineArgs.

Work arounds

You can get the same results from GetCommandLineArgs when invoking from a PowerShell command by quoting your single quotes and commas, knowing that you need to use $Env: instead of %%, and escaping your dollar signs:


ArgsTest.exe -this "is," "a," "`$Home," $Env:UserProfile   /test  -extra spaces /slashes:"including spaces" "-dashes:including 'quotes'" "-and:'some" "colons'" "/with:'single" `$Home "quotes'"
 

However, if the app you’re calling relies on parsing CommandLine, instead of using args[] or GetCommandLineArgs(), then you have to do it differently, and you can’t ever know that until you try it…

The best workaround, therefore is to use Start-Process. Of course, if you have UserProfile or other environment variables, then you still have to replace those with $Env: and escape your other dollar signs, but if you don’t … you can just use a single-quote here-string. Note the minor difference:


Start-Process ArgsTest @'
-this is, a, $Home, %UserProfile%   /test  -extra spaces /slashes:"including spaces" -dashes:"including '
quotes'" -and:'some colons' /with:'single $Home quotes'
'
@
 

CommandLine:

   "C:\Windows\system32\ArgsTest.exe" -this is, a, $Home, %UserProfile%   /test  -extra spaces /slashes:"including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single $Home quotes'


CommandLineArgs:

   Arg 0 is: C:\Windows\system32\ArgsTest.exe
   Arg 1 is: -this
   Arg 2 is: is,
   Arg 3 is: a,
   Arg 4 is: $Home,
   Arg 5 is: %UserProfile%
   Arg 6 is: /test
   Arg 7 is: -extra
   Arg 8 is: spaces
   Arg 9 is: /slashes:including spaces
   Arg 10 is: -dashes:including 'quotes'
   Arg 11 is: -and:'some
   Arg 12 is: colons'
   Arg 13 is: /with:'single
   Arg 14 is: $Home
   Arg 15 is: quotes'
 

And finally, we have it!


Start-Process ArgsTest @"
-this is, a, `$Home, $Env:UserProfile   /test  -extra spaces /slashes:"
including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single `$Home quotes'
"
@
 

CommandLine:

   "C:\Windows\system32\ArgsTest.exe" -this is, a, $Home, C:\Users\jbennett   /test  -extra spaces /slashes:"including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single $Home quotes'


CommandLineArgs:

   Arg 0 is: C:\Windows\system32\ArgsTest.exe
   Arg 1 is: -this
   Arg 2 is: is,
   Arg 3 is: a,
   Arg 4 is: $Home,
   Arg 5 is: C:\Users\jbennett
   Arg 6 is: /test
   Arg 7 is: -extra
   Arg 8 is: spaces
   Arg 9 is: /slashes:including spaces
   Arg 10 is: -dashes:including 'quotes'
   Arg 11 is: -and:'some
   Arg 12 is: colons'
   Arg 13 is: /with:'single
   Arg 14 is: $Home
   Arg 15 is: quotes'

 

As you can see, the only difference is the value in Arg 5 … but this last attempt actually results in a completely compatible CommandLine and CommandLineArgs. Of course, if you need to capture the output, then you need to append: -Wait -RedirectStandardOutput StdOut.log; gc StdOut.log … but that won’t work if you need to use the application interactively (although, if you just want to script an interactive app, there is also a -RedirectStandardInput parameter).

In any case, if your app is non-interactive, you can use something like this:


&{
Start-Process ArgsTest @"
-this is, a, `$Home, $Env:UserProfile   /test  -extra spaces /slashes:"
including spaces" -dashes:"including 'quotes'" -and:'some colons' /with:'single `$Home quotes'
"
@ -NoNewWindow -Wait -RedirectStandardOutput stdout.log; gc stdout.log; rm stdout.log
}
 

But still

If you need to run an app in the PowerShell window, interact with it (e.g.: answer a prompt), and then capture, process, or redirect the output … you need to invoke the app “natively” ... not using Start-Process. The bottom line is that PowerShell still needs a way to invoke apps that works as simply as Start-Process, but that invokes them inline in the shell. In the meantime, you should generate that ArgsTest executable (from poshcode) to help you figure out the right combination of quotation magic to convince PowerShell 2.0 to pass the right values!

Posted in Huddled | Tagged Interoperability, PowerShell, Shell, Windows | 3 Responses

Goobye CardSpace, hello U-Prove. Does anyone care?

By Joel 'Jaykul' Bennett on 24-Feb-2011

This is just a short rant … so if you don’t care about federated identity, claims-based authentication, OpenID, CardSpace, etc … just skip it.

Last week Microsoft publicly admitted that CardSpace is dead. There’s a story about it at Redmond mag which mentions that although they’ve decided not to release CardSpace 2.0, they’re not giving up on federation and claims-based authentication just yet. Instead, they’re releasing a technology preview of U-Prove.

Now, I had a look through the FAQ and some of the demos for this, and I’m glad they’re working with IdCommons, and releasing open source code in multiple languages, and putting the spec in their Open Specifications promise … but I have to say: I’m pretty sure that they haven’t read Rob Connery’s rant about Open ID.

Here’s how it looks to me: OpenID is trying to solve these problems:

  1. Users have to remember lots of passwords
  1. Users have a different “names” everywhere they log in

But normal people don’t have the first problem, because they just use the same password everywhere. Yes, that’s bad (and they know it), and it leads to a third problem: your password isn’t secure. But users just don’t care about that, so from their point of view, it’s not a problem. Even my friends and family tend to use the same password on every site, and I harp on them continuously about using secure passwords. Insecure passwords thus become the web sites’ problem: that’s why so many websites proactively emailed users when Gawker was hacked, asking them to change their passwords.

In fact, most people don’t have the second problem either — not because they have nice unique nicknames that nobody else uses, but because they don’t really care. In fact, in many cases they don’t want their name to be the same everywhere. They want to use a different name on Facebook than they do on Twitter, and they certainly don’t want to use their Google or Facebook (or even their blog or twitter) identities as their login for disreputable sites they might visit.

So if they do use OpenID, then they have a fourth problem: they can be tracked (by their OpenID) across many websites.

From what I can see, U-Prove is going to solve those same two problems, plus the fourth problem raised by using a federated identity: they’ll give you an “Agent” to manage the dissemination of information and prevent your use of the information from being tracked by your identity provider. It looks useful … but as I see it, is that people still don’t have the first problem, nor the second problem … and since hardly anyone but hardcore geeks is using OpenID, nobody has the fourth problem either.

Maybe some of these recent attacks (like the Gawker hack, and WOW account hacking) will raise awareness of that third problem (insecure passwords) ... and perhaps trying to solve that will lead to users actually having the first problem (too many passwords), but until it does … I’m not sure there’s any value in U-Prove.

As a final note: from trying out the Contoso demo site it seems to me that this U-Prove is far more convoluted than even OpenID. It takes the involvement of three websites —some of which were loaded in popups— and involved lengthy waits for Silverlight. This solution to federated identity will only seem like a good idea compared to logging in to your bank’s website, and since in their example case, the third website which is providing the verified identity is a bank website, I can only suppose that it would be far more complicated in the real world than here, since I haven’t seen a bank with a simple username/password login in a very long time.

Posted in Huddled | Tagged CardSpace, Identity, Microsoft

PowerBoots (Next Generation UI) WPF for PowerShell slides

By Joel 'Jaykul' Bennett on 13-Feb-2011

I gave a LiveMeeting presentation on PowerBoots last week, and these are the slides. I’m sorry to say that the recording did not work out, so you’ll have to wait for the next time I present it to see the demos. Luckily, the demo scripts I used are mostly the samples which are available in the PowerBoots distributions.

  • PowerPoint Deck
  • PDF Deck with notes

Posted in Huddled | Tagged PowerBoots, PowerShell, ShowUI, UserInterface

Long-Path Module (Experimental.IO)

By Joel 'Jaykul' Bennett on 04-Feb-2011

Thanks to a late night on my part, and the efforts of the developers on the Microsoft BCL project who wrote the Experimental.IO LongPath assembly … with no further ado, I present a really basic module for dealing with long file paths.

There’s no help yet, and very little error handling. But if you needed long paths (I know one of you did, because Jacques Willemen asked for it), then here are: Get-LongPath, Copy-LongPath, Move-LongPath, Remove-LongPath, Get-ContentLongPath, Set-ContentLongPath … and … uhm … Format-Color

Download the Experimental.IO Long Path Module

The script source is on PoshCode so please help me: write the next version of this module by contributing there.

Posted in Huddled | Tagged .Net, IO, Path, PowerShell, Windows

More Custom Attributes for PowerShell (Parameter Transformation)

By Joel 'Jaykul' Bennett on 31-Jan-2011

I wrote a post awhile back about using custom attributes for PowerShell parameter validation but when I did it, I focused on the use of attributes to improve the error messages output by validation (specifically, by: ValidatePattern).

There are many other things that can be done with custom attributes. However, PowerShell ships with two base types for attributes which derive from the CmdletMetadataAttribute, and it applies special processing to parameters of functions or cmdlets which have these attributes: ValidateArguments and ArgumentTransformation, since I’ve already written about custom argument validation, I figured a post about argument transformations would be appropriate.

It just so happens that I had cause to write such an attribute recently:


using System;
using System.ComponentModel;
using System.Management.Automation;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Automation;

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class StaticFieldAttribute : ArgumentTransformationAttribute {
   private Type _class;

   public override string ToString() {
      return string.Format("[StaticField(OfClass='{0}')]", OfClass.FullName);
   }

   public override Object Transform( EngineIntrinsics engineIntrinsics, Object inputData) {
      if(inputData is string && !string.IsNullOrEmpty(inputData as string)) {
         System.Reflection.FieldInfo field = _class.GetField(inputData as string, BindingFlags.Static | BindingFlags.Public);
         if(field != null) {
            return field.GetValue(null);
         }
      }
      return inputData;
   }
   
   public StaticFieldAttribute( Type ofClass ) {
      OfClass = ofClass;
   }

   public Type OfClass {
      get { return _class; }
      set { _class = value; }
   }  
}

This was written in C#, but you can wrap it up in an Add-Type -TypeDefinition in PowerShell as I did in the latest preview of my Windows Automation Scripts for PowerShell … and basically embed it in a script, a module, or your profile. A note of warning: you should specify a namespace, really, to avoid collisions.

However, if you can’t read C#, or if you’re not experienced with reflection, you may not even be able to tell what that code does, so let me explain quickly.

Basically, an ArgumentTransformationAttribute is very simple: it just has to have a Transform method which converts an input object into an output object. The Transform method has access to the PowerShell EngineIntrinsics class which gives you access to the current Host, the current SessionState to get variable values, etc. as well as being able to Invoke Commands or PSProviders…

In the example above, I’m trying to transform a string into an object. There’s a collection of the type of objects that I want which are defined as static fields on a certain class, so I created an argument transformation which takes the string, looks for a field with that name, and returns it. I made the class that it looks on configurable, so it would be flexible, so the actual usage looks something like this:

param(
   [Parameter(Mandatory=$false)]
   [System.Windows.Automation.ControlType]
   [StaticField(([System.Windows.Automation.ControlType]))]$ControlType
)

This parameter will now take a ControlType object, or the NAME of one of the fields on ControlType (which it will look up and return), so instead of always having to call: Select-UIElement -ControlType [System.Windows.Automation.ControlType]::Button, I can just write: Select-UIElement -ControlType Button … which is clearly a bit nicer to use.

PowerShell has one of these argument transformations included for use with credentials, so whenever you write a script that has a PSCredential parameter, you should decorate it with the CredentialAttribute like this:

param(
   [Parameter(Mandatory=$false)]
   [System.Management.Automation.PSCredential]
   [System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty
)

That one’s a little confusing because you leave off the “Attribute” part of the attribute’s name (ie: you don’t have to specify [System.Management.Automation.CredentialAttribute()]), so at first glance, it looks like you’re specifying the Credential type twice. Of course, in reality this is another use of parenthesis in PowerShell. To specify an attribute, you use square braces as with types, but with parenthesis in them (even if the attribute doesn’t require any parameters).

Type then Attribute, and non-mandatory parameters

When you specify a transformation on a parameter, you must be careful to specify the type first and then the transformation attribute (although this may seem counter-intuitive if you’re a developer). This puts the transformation closest to the variable name, and ensures that it is called before the value is cast to the parameter type.

When you don’t specify a parameter as mandatory, and you do specify a transformation attribute, the attribute’s Transform method will still be called, even if the user doesn’t provide you with an input. This is so that the transform attribute can provide a default value if need be. However, it’s called with a null value for the input data — this means that your attribute needs to be able to deal with a null value and output something which the cmdlet or script can deal with.

Depending on your use cases, it may be enough to just output null, but in the case of the Credential parameter, passing an empty string causes the Credential entry dialog to pop up. That’s the desired behavior if you want it to be mandatory, but otherwise, you need to be sure to provide a default value such as the Empty credentials — this will supress the prompt, but it will return an empty credential object you can easily distinguish from a passed-in value. In any case, if nothing is passed and the parameter isn’t marked manadatory, but the transform object creates a default value, the $PSBoundParameters should still be empty.

One Last Hurrah

To make this post as useful as I can, I’ve written a TransformAttribute that takes a script block to do the transform with, and a few examples of using it. In these examples I always assume the input is a string which will be converted into an environment variable, a static field value (this example does exactly the same thing as the class above, but using the generic ScriptBlock-based Transform), and even transforming user names into email addresses (by looking them up in Active Directory). I’ll post the code in PowerShell format this time, with the requisite Add-Type wrapped around it:


Add-Type -TypeDefinition @"
using System;
using System.ComponentModel;
using System.Management.Automation;
using System.Collections.ObjectModel;

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class TransformAttribute : ArgumentTransformationAttribute {
   private ScriptBlock _scriptblock;
   private string _noOutputMessage = "
Transform Script had no output.";

   public override string ToString() {
      return string.Format("
[Transform(Script='{{{0}}}')]", Script);
   }

   public override Object Transform( EngineIntrinsics engine, Object inputData) {
      try {
         Collection<PSObject> output =
            engine.InvokeCommand.InvokeScript( engine.SessionState, Script, inputData );
         
         if(output.Count > 1) {
            Object[] transformed = new Object[output.Count];
            for(int i =0; i < output.Count;i++) {
               transformed[i] = output[i].BaseObject;
            }
            return transformed;
         } else if(output.Count == 1) {
            return output[0].BaseObject;
         } else {
            throw new ArgumentTransformationMetadataException(NoOutputMessage);
         }
      } catch (ArgumentTransformationMetadataException) {
         throw;
      } catch (Exception e) {
         throw new ArgumentTransformationMetadataException(string.Format("
Transform Script threw an exception ('{0}'). See `$Error[0].Exception.InnerException.InnerException for more details.",e.Message), e);
      }
   }
   
   public TransformAttribute() {
      this.Script = ScriptBlock.Create("
{`$args}");
   }
   
   public TransformAttribute( ScriptBlock Script ) {
      this.Script = Script;
   }

   public ScriptBlock Script {
      get { return _scriptblock; }
      set { _scriptblock = value; }
   }
   
   public string NoOutputMessage {
      get { return _noOutputMessage; }
      set { _noOutputMessage = value; }
   }  
}
"
@

## Some example transformations:

## Convert a string into the value of the named environment variable (or error)
function Test-TransformEnvironment {
param(
   [Parameter(Mandatory=$true)]
   [string]
   [Transform({ Get-Content "Env:$($args[0])" })]
   $Environment
)
process { Write-Host $Environment }
}

# Test TransformEnvironment
Test-TransformEnvironment UserName
# Test Error Message 1:
Test-TransformEnvironment "This is not an environment variable name"


Add-Type -Assembly UIAutomationTypes
function Test-TransformStaticFieldValue {
param(
   [Parameter(Mandatory=$true)]
   [System.Windows.Automation.ControlType]
   [Transform({
      param([Parameter(Mandatory=$true)][string]$FieldName)
      foreach($field in [System.Windows.Automation.ControlType].GetField( $FieldName, "IgnoreCase,Public,Static" ) | Where { $_ }) {
         $field.GetValue($null)
      }
   })]
   $ControlType
)
process { $ControlType }
}

# Test TransformStaticFieldValue
Test-TransformStaticFieldValue Button
# Test Error Message 2:
Test-TransformStaticFieldValue DoorHandle


function Test-TransformEmail {
param(
   [Parameter(Mandatory=$false)]
   [String[]]
   [Transform(NoOutputMessage = "Specified value is not an email address, and we could not find a user by that name", Script = {
      param([Parameter(Mandatory=$true)][string[]]$UserName)
      if(!$UserName) {
         $UserName = Read-Host "Username"
      }
      $ads = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
      foreach($a in $UserName){
         if("$a".Contains("@")) { write-output $a } else {
            $ads.filter = "(|(samAccountName=$a)(displayName=$a))"
            foreach($user in $ads.FindAll().GetEnumerator()) {
               $user.GetDirectoryEntry().Mail
            }
         }
      }
   })]
   $UserEmail
     
)
process { Write-Host $UserEmail }
}

# Test TransformEmail
Test-TransformEmail -User $Env:UserName
# Test Error Message 3:
Test-TransformEmail -User Gremlins

function Test-StackingTransforms {
param(
   [Parameter(Mandatory=$false)]
   [String[]]
   ## Transform UserNames to Email Adresses
   [Transform(NoOutputMessage = "Specified value is not an email address, and we could not find a user by that name", Script = {
      param([Parameter(Mandatory=$true)][string[]]$UserName)
      $ads = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
      foreach($a in $UserName){
         if("$a".Contains("@")) { write-output $a } else {
            $ads.filter = "(|(samAccountName=$a)(displayName=$a))"
            foreach($user in $ads.FindAll().GetEnumerator()) {
               $user.GetDirectoryEntry().Mail
            }
         }
      }
   })]
   ## Transform a path to it's content
   [Transform({ if($args[0]) { Get-Content $args[0] } else { Get-Content Env:\Username } })]
   $UserEmail
     
)
process { $UserEmail }
}

## Rely on the default value
Test-StackingTransforms

## Specify an environment variable
Test-StackingTransforms Env:\UserName

## Read from a file (first, build the list)
$Env:UserName > $pwd\users.txt
$Env:UserName >> $pwd\users.txt
$Env:UserName >> $pwd\users.txt
$Env:UserName >> $pwd\users.txt
Test-StackingTransforms $pwd\users.txt
 

I want to point out two things about these examples:

In the TransformAttribute there is custom error handling for the script: exceptions are handled and printed, the case where you get no output is handled and printed, and you can provide your own error message for that no output case.

Notice that not all of the transformed parameters are mandatory: in the last two I’ve dealt with null input specially by either prompting the user from inside the transform script, or using a default value when none is provided. These are powerful options which you should use with care. Prompting from inside the script will allow you to build up pretty much anything you need in the transformation attribute, and having a default value can be just as useful, but you need to make sure that you keep the assumptions in your script in line with what actually happens in the parameter processing.

Hopefully this will give you some ideas for how other transformations :) If you’re inspired, please feel free to share examples in the comments: just use a code tag around them: <code lang="posh">

Posted in Huddled | Tagged Attribute, C++, PowerShell, Transformation, Type Cast | 2 Responses

Parenthesis in PowerShell

By Joel 'Jaykul' Bennett on 22-Jan-2011

I was asked recently to clarify the different uses of parenthesis in PowerShell:

I struggle with $myvar.foo vs. ($myvar).foo vs. ($myvar).foo() vs. $myvar.foo(); vs. $myvar.foo(1), etc., etc.

How do I know which one to use for which case? And then how to put this output into a write-output statement?

There are basically three ways of using parenthesis in PowerShell: grouping, method calls, and sub-expressions. Hopefully the explanations and examples below will help. Feel free to ask questions in the comments if I’ve missed something.

Grouping

The simplest use of parenthesis is similar to the mathematics use of grouping constructs. In fact, in it’s simplest form, it’s exactly the same as what you learned in math, and we use it to group calculations to determine the order of operations:

2 + 2 * 4 # Output: 10
2 + (2 * 4) # Output: 10
(2 + 2) * 4 # Output: 16
 

But in PowerShell, it can also group calls to cmdlets and functions, and can allow you to invoke methods and properties on the output of those cmdlets. This allows you to use operators or call methods on the output of cmdlets without first storing them in variables. You can add and subtract dates:

(Get-Date 10/10/10) - (Get-date 10/1/10)

When doing a registry lookup, you can avoid getting all of the PS* properties, you can call a single property, for instance, to get the path to PowerShell.exe:

(Get-ItemProperty hklm:\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell Path).Path

You can cast the output of a command to another type. So for instance, if you wanted to pick 10 random letters, you might use the fact that 65..90 are the ASCII values for A..Z, and then cast them to the char type:

[char[]](65..90 | Get-Random -count 10)

Tips and Tricks

One side effect of this particular use of parentheses is that you can use them to cause an assigned value to leak: that is, you can assign the output of a command to a variable, and still output it. So for instance, this script actually returns 14 and 28:

($seven = 3 + 4) + $seven
$seven * 3

This is obviously a really useful shortcut sometimes, now that you know about it :) ...

Method calls.

All objects have methods. In order to invoke a method, you have to use parenthesis to “call: it. So for instance, strings have a bunch of different methods, which you can see with this command:

"hi" | Get-Member -Type Methods

For instance to convert a string to all upper case, you call the ToUpper method:

"hi".ToUpper()

Note that when you type the name of a method without the parenthesis, you don’t invoke it: "hi".ToUpper Instead, PowerShell does reflection on the method and gives you information about how to call it, what the overloads are, etc. You can see much of the same information in the output of Get-Member. When you have a method that takes parameters, you need to pass the parameters inside the parenthesis, separating them with commas if there’s more than one:

"This is a test".IndexOf("i") #returns 2 (0-based index)
"This is a test".IndexOf("i", 3) # start looking at 2, so returns 5, the NEXT i

Sub-Expressions

PowerShell also has another way to use parenthesis: with a leading $ (dollar sign), they become a sub-expression, which is calculated before the containing expression … so for instance, you will notice that the Write-Host inside the sub expression will output first:

(Get-Date) + $(Get-Date | Write-Host -fore yellow; [timespan]"2") | Write-Host

Of course, you can’t put two command pipelines (separated by the semicolon “;”) into a simple parenthesis, but you can in a sub-expression!

Posted in Huddled | Tagged PowerShell, Q&A, Syntax, Tips | 4 Responses

How to: Invoke PowerShell and use the results from C#?

By Joel 'Jaykul' Bennett on 06-Jan-2011

One of the questions that comes up a lot about PowerShell lately is how to use it from C#. For the sake of a simple example, I figured it’s past time I post the sort-of … Hello World of PowerShell hosting. I’m going to present this app plus a little bit more at the next Upstate NY PowerShell User Group, so let’s go just a little deeper than a greeting. Let’s assume that you want to run a cmdlet or a script and get the output back to display it in your application.

Of course, the simplest thing to do is just stick that stuff in a DataGrid, so we’ll just whip up a simple WPF window. Yes, WPF of course. I can’t go back to Windows Forms, even for just for a simple example like this, but it’s basically the same except you have to set the DataGrid DataSource property instead of the ItemsSource … anyway, here’s the XAML:


<Window x:Class="WpfPowerShell.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       Height="300" Width="700">
    <DataGrid Name="dataGrid1" />
</Window>
 

Just add a reference to System.Management.Automation (it’s in the GAC: C:\Windows\assembly\GAC_MSIL\System.Management.Automation\1.0.0.0__31bf3856ad364e35\System.Management.Automation.dll) and then in your window’s constructor (or wherever you like), put this code:


// Note: this takes a little while ...
var ps = PowerShell.Create();

// Let's get all the processes:
ps.AddCommand("Get-Process");

// Use the generic overload to cast the PSObjects to their base object:
panel.DataContext = ps.Invoke<Process>();
 

That’s it. We’re done.

And now for something slightly more interesting

What about if we also wanted an easy way to spot the biggest memory consumers? We could start by sorting the processes by the WorkingSet, and pick just the 15 worst offenders. But let’s also add a chart to visualize just how bad those top offenders are. In WPF, this is simple: I’m going to use the free amCharts in my example, but you can do the same thing with the free visifire or visiblox charts, or the (commercial only) Telerik RAD controls … etc. All of those work in both WPF and Silverlight (and all three of the “free” ones have commercial versions).


<Window x:Class="WpfPowerShell.MainWindow"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:am="http://schemas.amcharts.com/charts/wpf/2009/xaml"
       Title="PowerShell Demo" Height="400" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*" MaxWidth="350"/>
            <ColumnDefinition Width="4*"/>
        </Grid.ColumnDefinitions>
        <am:PieChart Name="pieChart1" ValueMemberPath="WorkingSet" TitleMemberPath="ProcessName" SlicesSource="{Binding}" />
        <DataGrid Grid.Column="1" Name="dataGrid1" ItemsSource="{Binding}" />
    </Grid>
</Window>
 

Of course, this got a little more complicated. We needed to put the controls in a panel of some sort (I chose to use a Grid mainly because it automatically causes the scroll viewer around the DataGrid, but that forces us to specify the ColumnDefinitions) and the PieChart needs bindings for the value and title of each slice. You’ll notice I set the ItemsSource of the DataGrid and the SlicesSource of the PieChart to “{Binding}” (side note: why can’t they just use “ItemsSource” like normal people?) ...

Setting the sources to {Binding} causes them to just look at their data context, so now in the code behind, I can bind them both at once by just setting the DataContext of the window (try that in Windows Forms).


// Note: this takes a little while ...
var ps = PowerShell.Create();

// Get the 15 biggest memory users. Note that we're using AddScript now,
// We should probably put this in a resource or a separate script file:
ps.AddScript("Get-Process | Sort WorkingSet | Select -Last 15");

// Use the generic overload to cast the PSObjects to their base object,
// Just set the DataContext of the window, the controls are data bound
DataContext = ps.Invoke<Process>();
 

That’s all there is to it. I can attach my solution after I give my presentation if people want it, but there’s really nothing else in it (so far) except the references to the amCharts and System.Management.Automation …

Posted in Huddled | Tagged APIs, C++, PoshConsole, PowerShell, PowerShell Host, Sample Code | 2 Responses

Solving the “failed to grant minimum permission requests” error

By Joel 'Jaykul' Bennett on 29-Nov-2010

If you’re getting the error: Import-Module : Could not load file or assembly … or one of its dependencies. Failed to grant minimum permission requests. (Exception from HRESULT: 0×80131417) because you’re trying to load a module from a network share … you need to read the PoshCode wiki page which explains how to use caspol to set the CAS policy for a UNC path file share.

I feel like I should apologize for this post, since it basically just serves to link to that wiki page, but people keep asking, and my blog has better pagerank than the wiki ;-)

Posted in Huddled | Tagged Code Access Security, Import-Module, Network Share, PowerShell, RemoteSigned

Open Source and Community-Developed Software

By Joel 'Jaykul' Bennett on 15-Nov-2010

If you didn’t know that there’s a feeling of community among open source developers, you may have been hiding under a rock for the past several years. However, depending on where you work, and how active you are in hobbyist software development, you may or may not realize how passionate the debates and arguments are about what constitutes open source software.

For all sorts of reasons beyond the scope of this article, the open-source community has long been divided into several camps, which often seem to be defined by people’s feelings are about the Free Software Foundation and it’s leader Richard M. Stallman ;-) but are frequently in substantive disagreement about what it means for software to be open source software.

It’s an all too common situation to find companies publishing software under an OSI-approved open source license and then being attacked by open source developers and community leaders who make the claim that their software is not truly an open source project. The problem goes deeper than just technicalities, and touches on what it really means to be open, and the reason I’m writing this is to suggest that we be more intentional and discriminating about our terminology.

My proposal is simple: In addition to the current definition of “Open Source Software,” which is well established by both the FSF and the OSI, we should define “Community-Driven Software” which is software which goes beyond open source licensing requirements to have what some would call an “open source development model.”

The FSF and OSI each have web pages where they explain at great length what free software and open source software are. They both agree on what the FSF calls (in homage to President Roosevelt):

The Four Freedoms (of software):

  1. The freedom to run the program for any purpose. This means the license can’t discriminate against certain uses, certain people or groups, or against use by people performing certain types of work.
  2. The freedom to study how the software works, and change it to make it do what you wish. This means the source code must be available, and the user must be allowed to modify it.
  3. The freedom to redistribute copies. This means that the software can’t have restrictions about giving copies away (modified or unmodified), and can’t charge royalties or license fees.
  4. The freedom to distribute modified versions. Essentially, this is the freedom to fork the software.

Although their respective pages go on about these at great length, and discuss other terms and conditions, these four freedoms are the simplest definition of what open source software is.

Moving the Finish Line

As more and more corporations have begun to accept the concept of open source software licenses, the community of open source developers, hobbyist, and users have begun to expect more than simple open license terms, and (as I said before), have even begun to claim that an open source license is not enough for a program to be considered open source.

Essentially, there has been a growing group which is redefining open source software to mean software which is developed through a “community process” whereby anyone can potentially submit patches and bug fixes which can eventually be incorporated into the original project. Of course, neither the FSF, nor the OSI, (nor any software license I am aware of) has attempted to define free software or open source as meaning that anyone can contribute alterations to the original project but only that you have the right to fork and create a new project.

I believe that it’s important to the integrity of the open source “movement” and the acceptance by corporations of the concept of not just using open source software but actually creating it, that we not try to redefine “open source” to mean something that it has not meant historically. It’s critical that even as we are, in essence, moving the finish line … we not leave corporations feeling that we have changed the rules mid-game.

There is really no upside to disputes about whether or not a particular software program is “really” open source — these debates alienate developers who must sometimes work very hard to get their corporations to accede even to a limited source code release, and then find their efforts bashed by the very people they thought would be most pleased.

Community-Developed Software

I would like to suggest that we allow the definition of “open source” to be simple, and to mean what it has always meant (software which is provided under legal terms which ensure the four freedoms). But lets push individuals and corporations who release open source software to make sure that they also have a community-driven software development model.

What does that mean? Well, I don’t really want to set that standard in stone myself right now — it’s certainly still being debated by many people — but I want to put something out there as a starting point. Of course, we’re starting from the basis that the project is under an open source software license, but what else is required in addition for a project to be considered a community project? Do we need to know who all the developers with check-in rights are? Are projects in a “benevolent dictator” system with just one authorized check-in user automatically not community projects?

I think we can probably break it down to just two additional freedoms (if we’re willing to twist the concept a little bit). Obviously the four freedoms above are all implied … but additionally, a community project should give you:

  1. Freedom from ignorance:
    • Users should be able to see what’s getting done on the project.
    • External developers should be able to see what still needs doing on the project.
  2. Freedom to contribute:
    • Users should be able to submit bugs and feature requests
    • External developers should be able to submit patches (and see them applied).

Personally, I believe that seeing what’s getting done, and being able to submit patches requires that the development team to be developing on a publicly visible source repository (or at least performing regular pushes to one). If you’re only pushing source to a public repository when you do a release, then users can’t see if there’s any progress happening on the project at all, and even if you’re pushing source once a month or once a week, depending on the speed of development, external developers will be reluctant to submit patches for bugs or features which might already be implemented by someone else.

Additionally, I think seeing what needs doing, and being able to submit bugs and feature requests requires a public tracker that is, at least, connected to the actual tracker used by the development team. There shouldn’t be a censorship layer between developers and users when discussing bugs and feature requests, and the public bug and feature request tracker should be updated when a fix is accepted or a feature is implemented, so that external developers don’t waste time creating patches for issues that are already resolved.

Obviously the point here is that users and external developers need to feel like they’re able to interact with the development team, and external developers should be able to submit patches which can be applied to the project. The idea is that end users and external developers shouldn’t have to go through a PR person or even a single public facing contact, and if I know how to fix a bug, or how to implement a features, I shouldn’t have to fork the project to get my fix applied.

None of that is meant to imply that every submitted feature request or patch will be applied — the development team always has the right to take their software in the direction they think it should go (even if users think they’re wrong). People who completely disagree with the direction a project is taking are still free to fork the project and go a different way.

What do you think?

Posted in Huddled | Tagged Community, Discussion, licensing, Open Source | 2 Responses

« PreviousNext »

Lijit Search

I Diigo...

  • jeresig/jquery.hotkeys - GitHub
  • Using the Web's Keyboard Shortcuts and Hotkeys? - Scott Hanselman
  • sausage jQuery plugin
  • FlexSlider - The Best Responsive jQuery Slider
  • FitText - A plugin for inflating web type
  • Isotope jQuery plugin
  • Bacon jQuery text wrapping plugin
  • The League of Moveable Type
  • MyFonts: Webfonts & Desktop Fonts
  • Behance Network Typography Gallery

Tags

.Net .Net 2008 Scripting Games Automation Bugs Design Development Funny Gadgets GeoShell GUI Huddled Masses Internet licensing Microsoft Modules My Software News Personal PInvoke Pipeline Politics PoshCode PoshConsole PowerBoots PowerShell PowerShell Functions PowerTips Rants Recommender Repository Scripting ShowUI Software Solutions Textile Tips User Group UserInterface WalkThrough WebHosting Windows 7 WordPress WPF Xml

About Huddled Masses

This is web site is dedicated to the musings of Joel Bennett (aka Jaykul) about technology, software, software development, the web, and the world.

Any resemblance of the views expressed and the views of my employer, my terminal, or the view out my window are purely coincidental. The resemblance between them and my own views is non-deterministic. The question of the existence of views in the absence of anyone to hold them is left as an exercise for the reader.

P.S.: I occasionally link to things I think are great. When I do, I occasionally find a "referral code" so I can make a little cash. I promise that I don't link to anything just because of that cash (I wouldn't cross the street for the amount of cash those links bring in, never mind write a whole blog post) ... but I do not promise that things I link to will stay great as time passes, nor that you will agree with me about their greatness!

Archives

  • January 2012
  • October 2011
  • August 2011
  • July 2011
  • June 2011
  • March 2011
  • February 2011
  • January 2011
  • November 2010
  • August 2010

Copyright © 2012 Joel Bennett.

Powered by WordPress and Hybrid.