Someone asked on the PowerShell Newsgroup about writing Advanced Functions, and specifically:
looking for a … guide to putting together an advanced function that is visible and usable every time I start Powershell. By visible I mean that when I do a ‘get-command’ I want my [advanced function]s to be listed alongside all the regular cmdlets. What makes that possible? ... what do I need to do to make that happen? Whats the difference between an [advanced function] and a module?
There are lots of articles on the Microsoft PowerShell team blog about both topics, but it seems there’s not really been any sort of step-by-step written, so I posted this to the newsgroup, and since the person who asked the original question said he found it useful, I figured I’d share it here…
Read the rest of this entry »Awhile back I wrote a series of posts about WPF From PowerShell From PowerShell” which were about how you could load XAML in previous PowerShell 2 CTPs to create WPF user interfaces … a few people have mentioned loading XAML in PowerBoots, and a couple of people have posted other samples showing XAML even since I published the most recent release, so I figure it’s time to point out that you really can load that XAML into Boots, and get all the threading and other support.
Just for fun, I’m going to rehash an earlier post about updating windows to show how you can go about this using PowerBoots, and hopefully show that it’s a little easier (and a lot more async). Compare and contrast the code in this article with that one, just for fun.
Unlike the original article, most this code (except where explicitly mentioned) works on either PowerShell v2 with PowerBoots, or PowerShell 1.0 with PoshWPF, a snapin that is part of the PowerBoots module but is also released separately… Also, unlike those previous posts, this does not require you to be running PowerShell.exe with the -STA switch, since the New-BootsWindow cmdlet takes care of threading for us.
Read the rest of this entry »WPF uses a concept called “Attached Properties” to handle certain things, like when you put controls into a DockPanel. Basically, anything you put inside a DockPanel has a property “Dock” which you can set … but because the property is actually defined by the DockPanel, it doesn’t show up in PowerBoots, so you can’t just say -Dock "Bottom" when you create the child items … at least, not yet.
I’m still thinking about the best way for me to expose this in PowerBoots, because the problem is that attached properties can get attached several ways, and not just on direct children of an element. So I wanted to show you a way that you can do it for now so you can at least use the DockPanel effectively:
Here’s what I’m thinking about for the next release of PowerBoots (you can take this and use it now, if you like it, but I’m really looking for, uhm … better ideas). Basically, I have written a function: Set-AttachedProperty, which takes an attached property and a value, and passes through the element on the pipeline. You may want to use this in conjunction with the module I published awhile back for creating type accelerators, because it lets you run a line like this:
Which will let you substitute [DockPanel] for [System.Windows.Controls.DockPanel] … and of course, you can use it for all the types you want to use attached properties from, and it’s a real lifesaver if you need to use a bunch of them. Of course, in this particular example, we really only need to use a single attached property, so it’s enough to define that property ahead of time:
Then you can use the Set-AttachedProperty function through an alias sap, and rewrite that huge block above like this:
Pretty slick, right? And it will even print out the list of values in the error message if you invoke it with an invalid value against an enum property like dock: sap $DockProperty "" … The problem I have with it is that you have to predefine your $DockProperty variable, and you can’t just define it against the root class. So I’m trying to find a way to tweak the dynamic property generation to make it so that the pipe into | sap $DockProperty Bottom can just be a parameter to the original element: -Dock Bottom … if I can’t find that, in the worst case scenario, I’ll just add an -AttachedProperties parameter with a hashtable of $DockProperty,“Bottom” or something … what do you think?
In any case, here’s the sap function, for now, and remember to use it as part of the pipeline:
This release of PowerBoots is the most exciting release software I’ve cranked out in awhile. It finally has almost all of the features that I have thought of so far (we’re still missing proper support for attached properties).
-Async in their own threads, or inline in PoshConsole, or as synchronous dialogs that return values when they close.You can read the “and much much more” between the lines right?
Read the rest of this entry »[new] You should check out the new release of PowerBoots, and the walkthrough PowerBoots Tutorial that I wrote up … this post is getting out of date.
If you haven’t seen the Ruby Shoes graphical framework, you should check it out. In fact, go read the tutorial and come back, because the rest of this will make a lot more sense then.
It’s a very slick toolkit, right?
From my previous post introducing PowerBoots
I hacked some things together in binary form, and Jeffrey Snover did some pure magic in a script, and after playing with various different mixes of it, I’m going to release the pure-script, CTP3-only, PowerBoots module now, and I’ll follow up with the backwards-compatible Out-WPF that gives you external threading later on.
Unpack the “PowerBoots” folder into one of your Module folders, and then just run imo PowerBoots and you’re off to the races. Imo is not short for immolate, nor for “in my opinion” ... it’s the alias for the newly renamed Import-Module cmdlet.
Once you’ve got that set up, check out the Demo-PowerBoots.ps1 script. Don’t just run it — open it in an editor and check it out, then run a few of the individual sections, and play with them to get a feel for how this works.
I’ll have more very soon, but for now, let me tantalize you with this little demo:
Awhile back I wrote a Get-Web script, which I later converted to a compiled Get-Web cmdlet and module (pssnapin) ... adding support for POST forms. At the time, it seemed like GET and POST with some string variables pretty much covered what I’d ever want to do with web pages, however I found a few places where I needed to POST a file attachment, and then a couple of full-on REST APIs, which use not just GET and POST, but PUT and DELETE as well.
While I was playing with scripting the REST API for DekiWiki … I experimented with the MindTouch Dream SDK … and as a result, I’ve refactored the part of that module that actually does the HTTP work out into a little PowerShell 1.0 compatible script library. Of course, it depends on several dll’s (mindtouch.core.dll, mindtouch.dream.dll, and SgmlReaderDll.dll and log4net.dll) from the Dream SDK, which you can download from SourceForge.
There are basically two main functions: Invoke-Http and Receive-Http. There are also a few extra functions which are mostly just for use by these two functions, and then Set-HttpDefaultUrl and Set-HttpCredential which you can use to simplify your scripts. I intend to rewrite these as cmdlets in C# and contribute them to the PowerShell Community Extensions along with some of my other networking cmdlets (Get-DNS), so if you have any suggestions, I’d love to hear them!
Invokes HTTP verbs (like GET, POST, PUT, and DELETE) against a URL with optional parameters (eg: name = value), input, etc … and returns the output as a MindTouch.Dream.Result
Invoke-Http [-Verb] <String> [[-Path] <String>] [[-With] <Hashtable>] [[-Content] <Object>] [[-Type] <String>] [[-Authenticate] <String>] [-WaitForResponse]
The HTTP verb you want to invoke. Defaults to GET, but can be anything your HTTP server supports.
The URL you want to invoke against. Can be a partial URL, if you first call Set-HttpDefaultURL first. You can also specify this as an array or a hashtable. This is particularly useful if you’re specifying a partial URL, and your URL is built based on logic in your code. If you pass an array, the values in the array are joined with forward-slashes. If you pass a hashtable, the keys and values are appended joined with forward-slashes, but the values are double encoded (by UrlEncode), ie: $key/$(UrlEncode(UrlEncode($value))
The parameters to be passed with the verb call, as a hashtable. (these are particularly used for POST, etc). These are passed in the query as named parameters, with the values encoded.
Anything you want to pass as content. In actuality, we really only handle three things: XML Documents, file paths (the file will be used as content), and everything else is passed as simple text. If you need other data types, the “DreamMessage” that the scripts depend on can handle all sorts of data types, so you’ll just need to add it around line 50 of the script.
This is the type of content. We can automatically deduce this for XML or File objects, but if you want to force a type, you should use this parameter. For instance, to fake a web form submission, you need to specify the type as FORM or FORM_URLENCODED but you can also specify the type as any of: ATOM, BINARY, BMP, CSS, DREAM_EXCEPTION, FORM_URLENCODED, GIF, HTML, HTTP, JPEG, JS, JSON, MULTIPART_MIXED, NOTHING (mimetype -/-), ANY (mimetype */*), PDF, PHP, PNG, RDF, RELAXNG, SVG, ANY_TEXT (mimetype text/*), TEXT (mimetype text/plain, with the ISO-8859-1 encoding), TEXT_UTF8 (mimetype text/plain, with UTF8 encoding), TEXT_XML, TIFF, VCAL, VERSIT, XHTML, XML, XRDS.
You can pass either a boolean (in which case the HTTPRest scripts will use a default $global:HttpRestCredential which it will prompt for if you haven’t set it), or an actual PSCredential or NetworkCredential object. In any case, this causes the invoke to contain network credentials. Note that sometimes you may need to pre-authenticate before making calls to a web-service.
A switch parameter that causes the query to wait until the response completes downloading, and return a DreamResponse instead of returning an incomplete Result
Receives the response or result from a Dream.Result or DreamMessage. as XML, XDoc, text, raw bytes, or even to file. This function is really only useful to receive the output of Invoke-Http when you need the response content.
Receive-Http [[-Output] <string:[Xml|File|Text|Bytes]>] [[-Path] <string>] [[-InputObject] <MindTouch.Dream.Result<MindTouch.Dream.DreamMessage>>]
Receive-Http [[-Output] <string:[Xml|File|Text|Bytes]>] [[-Path] <string>] [[-InputObject] <MindTouch.Dream.DreamMessage>]
The TYPE of output you want:
A string containing the path for file output or XPath selector for XML, XDoc, and Text output
The input must be either a Result<DreamMessage> or a DreamMessage itself. Values passed on the pipeline are accepted into this parameter.
This takes the passed URI and sets it as the base (default) URL for the Invoke-Http function (if you set this, any path passed to Invoke-Http is treated as relative to this path).
This takes a PSCredential or NetworkCredential and stores it as a PSCredential for use by the Invoke-Http function. Particularly useful when you want to script a bunch of authenticated methods without being re-prompted. Note that if you don’t pass a credential, you’ll be prompted for one the usual way, so calling just Set-HttpCredential is sufficient when working on the command-line.
There are other functions in here, a few should be considered “internal” functions (when this is a module, they would not be exported: Get-DreamMessage, Get-DreamPlug, Get-HttpCredential) and a few of which came out of my utilities functions (like Encode-Twice, and Join-Url, Get-FileName, Get-UtcTime, and Get-CredentialBetter)...
Hashtables are IEnumerable, but they don’t behave that way in PowerShell … this seems to cause all sorts of odd behavior and such, so I thought I’d write up all the examples I can think of in one place. That means this post is going to be a little bit rambling, so please bear with me.
It turns out that in the case of Hashtables, PowerShell does NOT enumerate them into the pipeline. Instead, it passes the entire Hashtable object. Of course, nobody realizes this … because the ouput cmdlets unwrap them (what?!).
The first bug is in Add-Member, which doesn’t work on Hashtables until you’ve already used the Hashtable.
NOT ONLY does Add-Member not work the first time, it’s not just a matter of calling it twice: you just have to try to access something in the hashtable before you can use Add-Member on it:
Here’s another buggy manifestation, in the Formatting cmdlets. This time, the Format-* cmdlets unroll the hashtable … to make it look like it’s being enumerated the way it should be.
Clearly, the Format-* cmdlets have magic code that unwraps hashtables. Which just leads to even more confusion: $table looks the same (in the console output) as $table.GetEnumerator() ... but it doesn’t behave the same way, EXCEPT to the format cmdlets.
In PowerShell 2.0, the PowerShell team is adding another special feature based on hashtables (which appears at first to be based on IEnumerables):
Jeffrey Snover gave an example of splatting in his PDC presentation. Splatting is where a collection is unwrapped so that you can take an array of values and pass one to each parameter of a cmdlet or function. But in Jeffrey Snover’s demo, he splatted a hashtable. Basically, the hashtable keys are matched up to parameter names as though they had been specified by name. That made me wonder why splatting can’t work with custom objects, but after investigating a bit, I’m actually frustrated with the inconsistency of how hashtables are treated in Posh.
The new splatting feature seems to only work with simple arrays and hashtables … adding yet another scenario where the hashtable is being treated specially (even though it doesn’t need to be: if they just splatted IEnumerable, we could work with List
You know what would be cool? If I could splat any object (like a custom PSObject that I have added members to), and have it’s property names matched to parameter names as though all the parameters had ValueFromPipelineByPropertyName set.
You know what would be really cool? If I could specify that I want pipeline objects splatted, forcing ALL parameters to be treated as ValueFromPipelineByPropertyName, without needing to use: ForEach-Object { Test-Splatting @_ } … maybe a syntax like: Get-HashTablesToSplat | Test-Splatting @@ …
I’ll write up more information later, but a couple people have asked for this in #PowerShell on irc.freenode.net, and I had it already written, so here you go … my ConvertFrom-Html cmdlet (in a Huddled.HtmlSnapin). It converts HTML to valid xml using the SGML Parser which was available on GotDotNet years ago. It only works with files (doesn’t do URL downloads yet). Use it like this:
The source code to my plugin may be considered public domain, and is included in the Huddled HTML SnapIn Zip.
However, the SgmlReader library is a Microsoft Sample which is licensed under the old MS Samples license which doesn’t allow reuse with viral open source software. I’ve seen some work being done on an HtmlAgilityPack on CodePlex (using a Creative Commons ASA license) but I have not really looked at it except to see that it has a several active issues related to entity encoding and dropping malformed tags which I haven’t encountered in SgmlReader …
The SGMLReader library has been re-released on MSDN Code Gallery under an Ms-PL license, and all is well with the world.
Well, the first alpha CTP release of PowerShell 2.0 is out, and there’s a lot of new stuff in it … but I won’t repeat the list from the PowerShell blog, because I’m sure you’ve seen it five or six times already. Instead, lets just skip straight to talking about one of the features we’ve been hearing about the longest: in PowerShell 2, you can create Cmdlets in script … bringing nearly full parity between whats possible in a C# cmdlet and what’s possible in script.
There are a few caveats still (Parameter Sets aren’t working yet, and neither is help, really), and a few surprises … there’s a few downsides to PowerShell script vs C# ... but in this particular context one thing that stands out is that in C# the BeginProcessing, ProcessRecord, and EndProcessing blocks are actually methods which can call each other, and as demonstrated in my tutorial for writing cmdlets that work in the pipeline, they can be recursive — without getting duplicate variables.
In the interests of being the first to publish an interesting script cmdlet
and to continue my recent trend of talking about writing for the PowerShell pipeline, I’ve merged the logic of my script function and my pipeline cmdlet into a single sample script cmdlet for PowerShell 2.0 and it works great!
A few observations from the process, in no particular order:
$CommandLineParameters.ContainsKey because parameter variables keep their values through recursion if you don’t explicitly pass a value.$CommandLineParameters.ContainsKey works differently in the Begin block where it will return $false for arguments which will get their values from the pipeline, than in the Process block where it will treat values which were passed as CommandLineParameters the same as those which were passed via the pipeline.Cmdlet which takes a name (which must have a – in it) and a couple of other parameters followed by a function script block.Invoke-Expression.In a continuation of what is, sadly, becoming a series on how the PowerShell Pipeline works … Karl Prosser brought to my attention that certain powershell commands which have an -InputObject parameter don’t actually work when you pass something into it … so I thought I should create a cmdlet to show you how to correctly handle the InputObject parameter with the ValueFromPipeline set so you can pass the input in either way.
This should expose two weirdnesses about how the Select-Object cmdlet works:
The big problem with this behavior is that there’s essentially no hint that you’ve done something wrong — there’s actually no way to make Select-Object work properly except by passing the objects in via the pipeline. The bigger problem is that it would have been simple for the Microsoft team to catch this and alert you, but they didn’t — so you probably won’t even notice there’s a problem until you run it on a trivial data set like my example. The even bigger problem is that it doesn’t just affect Select-Object (try it with Where-Object, just for instance). Read the rest of this entry »