Posts Tagged ‘Scripting’

In July of last year I wrote a PowerShell script with the goal of allowing me to generate XML from PowerShell with a simple markup that would look a little like the resulting XML ... this week I was using that script again, and had a couple of issues that made me go back and look at the source.

While I was playing with the source and tweaking things a little bit to improve the way it handles namespaces, I started playing with the idea that I could improve the syntax. At the very least, I thought, I ought to be able to do away with all those “xe” aliases…

Well, I was able to. And what’s more, I managed to dramatically clean up the way namespaces work, and make it so that really, the only ugly part of the syntax is the initial declaration of namespaces! I’m going to start with two examples, and use them to walk you through the features :)

Example 1

The simplest example I could think of is to list all the files in a folder, with the file size and last modified stamp:

[string]$xml = New-XDocument folder -path $pwd {
   foreach($file in Get-ChildItem) {
      file -Modified $file.LastWriteTimeUtc -Size $file.Length { $file.Name }
   }
}

The output of that, when run on my formats folder, looks like this:

<folder path="C:\Users\Jaykul\Documents\WindowsPowerShell\formats">
  <file modified="2009-11-07T07:27:00Z" size="30474">CliXml.xsd</file>
  <file modified="2009-11-07T07:27:40.48001Z" size="14314">format.xsd</file>
  <file modified="2010-01-16T21:30:06.0562796Z" size="18275">NppExternalLexers.xml</file>
  <file modified="2009-03-18T21:28:51.6579351Z" size="5802">Recommender.Types.Format.ps1xml</file>
  <file modified="2009-11-07T07:27:40.518029Z" size="5107">types.xsd</file>
</folder>

You can immediately see what the script does: New-XDocument (which is aliased as ‘xml’) actually generates the root xml node, so the first argument to it is the name of that node, and any other arguments become attributes … except for the script block. That script block turns into the contents of the node.

Inside the script block, PowerShell code is parsed as usual, but whenever a command that doesn’t exist is encountered, it is turned into an xml node! Pretty simple, right? Of course, if you wanted to create a node with a name that’s already taken by a PowerShell command, you can just replace file with New-XElement file, or (using aliases) xe 'file', which explicitly creates an xml node with the given name.

That’s pretty much it for our first example, so let’s look at a more complicated example, with multiple namespaces, and deeper nesting.

Example 2

This time, we’ll create an Atom document, and we’ll include some namespace extensions (including a made up one for listing my files as we did above):

New-XDocument (([XNamespace]"http://www.w3.org/2005/Atom") + "feed")          `
              -fi ([XNamespace]"http://huddledmasses.org/schemas/FileInfo")   `
              -dc ([XNamespace]"http://purl.org/dc/elements/1.1")             `
              -$([XNamespace]::Xml +'lang') "en-US" -Encoding "UTF-16"        `
{
   title {"Huddled Masses: You can do more than breathe for free..."}
   link {"http://HuddledMasses.org/"}
   updated {(Get-Date -f u) -replace " ","T"}
   author {
      name {"Joel Bennett"}
      uri {"http://HuddledMasses.org/"}
   }
   id {"http://HuddledMasses.org/" }

   entry {
      title {"A DSL for XML in PowerShell: New-XDocument"}
      link {"http://HuddledMasses.org/A-DSL-for-XML-in-PowerShell-New-XDocument/" }
      id {"http://HuddledMasses.org/A-DSL-for-XML-in-PowerShell-New-XDocument/" }
      updated {(Get-Date 2010/03/03 -f u) -replace " ","T"}
      summary {"A while back, I posted a simple mini language for generating XML from PowerShell script. However, I was using it the other day, and I really just felt that the markup was ugly, since it was littered with 'xe' marks and such."}
      link -rel license -href "http://creativecommons.org/licenses/by/3.0/" -title "CC By-Attribution"
      dc:rights { "Copyright 2010, Some rights reserved (licensed under the Creative Commons Attribution 3.0 Unported license)" }
      category -scheme "http://huddledmasses.org/tag/" -term "xml"
      category -scheme "http://huddledmasses.org/tag/" -term "PowerShell"
      category -scheme "http://huddledmasses.org/tag/" -term "DSL"
      fi:folder -Path "~\Formats" {
         foreach($file in Get-ChildItem (Join-Path (Split-Path $profile) Formats)) {
            fi:file -Created $file.CreationTimeUtc -Modified $file.LastWriteTimeUtc -Size $file.Length { $file.Name }
         }
      }
   }
} | % { $_.Declaration.ToString(); $_.ToString() }

There are four things you should notice, in particular:

First: the initial tag has a [XNamespace] added to it. You can specify a tag name that has a namespace by adding them together this way, or by embedding the namespace in the string like "{http://www.w3.org/2005/Atom}feed" instead. Either way works. This initial namespace becomes the default namespace for the document. If you don’t specify a namespace on tags later, they automatically belong to that one.

Second: when you want to add additional namespaces, you can do so with a custom prefix like: -dc ([XNamespace]"http://purl.org/dc/elements/1.1"), and that prefix (dc) takes on a special meaning. When you want to have a tag later on that is part of that namespace, you just prefix the tag, like dc:rights —the same way you would in XML.

Third: any number of attributes can be specified using the -name value syntax, but anything in a {scriptblock} becomes the content — and is subject to the same rules as the outer sections.

Fourth: This generates an XDocument. When you cast an XDocument to string, the xml declaration is left off, so if you want it, you need to manually add it via $XDocument.Declaration. Incidentally, XDocuments are not XMLDocuments, but they are trivially castable to them.

The output of that particular section of New-XDocument is this:

<feed xmlns:dc="http://purl.org/dc/elements/1.1" xmlns:fi="http://huddledmasses.org/schemas/FileInfo" xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
 
  <link />http://HuddledMasses.org/
  <updated>2010-03-04T00:44:31Z</updated>
  <author>
    <name>Joel Bennett</name>
    <uri>http://HuddledMasses.org/</uri>
  </author>
  <id>http://HuddledMasses.org/</id>
  <entry>
   
    <link />http://HuddledMasses.org/A-DSL-for-XML-in-PowerShell-New-XDocument/
    <id>http://HuddledMasses.org/A-DSL-for-XML-in-PowerShell-New-XDocument/</id>
    <updated>2010-03-03T00:00:00Z</updated>
    <summary>A while back, I posted a simple mini language for generating XML from PowerShell script. However, I was using it the other day, and I really just felt that the markup was ugly, since it was littered with 'xe' marks and such.</summary>
    <link rel="license" href="http://creativecommons.org/licenses/by/3.0/" title="CC By-Attribution" />
    <dc:rights>Copyright 2010, Some rights reserved (licensed under the Creative Commons Attribution 3.0 Unported license)</dc:rights>
    <category scheme="http://huddledmasses.org/tag/" term="xml">
    <category scheme="http://huddledmasses.org/tag/" term="PowerShell">
    <category scheme="http://huddledmasses.org/tag/" term="DSL">
    <fi:folder path="~\Formats">
      <fi:file created="2009-11-07T07:27:00Z" modified="2009-11-07T07:27:00Z" size="30474">CliXml.xsd</fi:file>
      <fi:file created="2009-11-07T07:27:40.4529965Z" modified="2009-11-07T07:27:40.48001Z" size="14314">format.xsd</fi:file>
      <fi:file created="2009-02-07T13:56:12Z" modified="2010-01-16T21:30:06.0562796Z" size="18275">NppExternalLexers.xml</fi:file>
      <fi:file created="2009-08-09T19:10:06.3647094Z" modified="2009-03-18T21:28:51.6579351Z" size="5802">Recommender.Types.Format.ps1xml</fi:file>
      <fi:file created="2009-11-07T07:27:40.4970185Z" modified="2009-11-07T07:27:40.518029Z" size="5107">types.xsd</fi:file>
    </fi:folder>
  </category></category></category></entry>
</feed>

The New-XDocument script itself is on PoshCode in the Xml Module 4 along with a few interesting functions like Select-XML (which improves over the built-in by being able to ignore namespaces when you write XPath) and Remove-XmlNamespace (which was instrumental in removing namespaces for Select-Xml). There’s also a Format-Xml for pretty-printing, and a Convert-Xml for processing XSL transformations.

I’ll probably post some more examples of this in the next week or two, and I really should write some commentary about the function itself, which uses the tokenizer to discover which “commands” are really xml nodes … but for now, I’ll leave you to enjoy.

Reblog this post [with Zemanta]

I just put up a poll on the PowerShell Virtual Group to see if people are interested in a low-planning brown-bag event.

The initial question is: would you attend a weekly (or monthly) virtual brown-bag lunch if I put one together.

The idea is that we would start each session with a short collection of interesting links, tips and tricks, or connect issues, and then have a presentation or discussion or script club or open-mic session, depending on interest.

Basically, this is meant to flow, on a week-to-week basis, from script-club to formal user group presentations.

Some weeks we would have presenters from various local user groups share content they had prepared for their local groups. We would encourage you to take ownership of this time, request topics, and prepare presentations (no matter how short).

Some weeks we would have an open-format script club using our private pastebin, IRC channel, and LiveMeeting voice chat. This time could vary from real-world problem solving to scripting games and “project Euler”-style challenges.

Some weeks we would have open-mic time and solicit feedback for the PowerShell team from anyone who cared to give it. I would make an effort to make sure I wasn’t the only MVP there, so you could feel that even if there wasn’t a Microsoft employee present on a given day, your voice could be heard when you make suggestions or vent frustrations …

The point is: the format would vary a bit, and we would adjust it over time to fit whatever works the best within our virtual meeting and time constraints.

So, what do you think? Are you interested?

There are so many fun things you can do in Windows when your scripting language allows you to make PInvoke calls to Win32 APIs … but I have to say it’s amazing how many things have been added to Windows recently and still left out of the .Net framework …

Anyway, on to the Aero Peek stuff. If you haven’t seen it, Aero Peek is a feature of Windows 7, which lets you get a peek at your desktop, or at a single window for a moment. Basically, you can press Win+Space (the Windows logo key and the space bar) and all of your open windows instantly turn transparent, revealing … whatever was on your desktop: wallpaper, icons, and gadgets. You can also use it by hovering your mouse on the right corner of the taskbar, or you can peek at a single window by hovering over it’s taskbar button and then over it’s thumbnail.

In any case, I have a couple of windows which I would like to have stay visible on the desktop when I hit the aero peek hotkey: Rainlendar and Miranda. It turns out there’s a simple API call for this: DwmSetWindowAttribute which lets you set the DWMWA_EXCLUDED_FROM_PEEK attribute to ENABLED … causing a window to no longer hide when you press that hotkey. Of course, that API call should be made by those apps, in response to a user setting (so I’ve told their authors about it), but it doesn’t have to be (so I wrote a script to do it myself).

In the old days, I would have written a little systray app which would give you a popup list of all windows, or perhaps added a menu item to a window’s right-click menu … and I would have had to deal with creating some way to persist which apps you wanted to apply this to, and then I could have applied the setting to them whenever you opened them.

But now, I have PowerShell. I don’t need to give you menus and store settings, because I can just let you edit a little script instead.

So here’s a script which will let you turn off Aero Peek transparency for windows by window title and/or process name … Once you have this function available, you can keep Rainlendar’s calendar, tasks, and event windows all visible by just running Remove-AeroPeek -Process Rainlendar2 or you can keep your Miranda contact list visible by running Remove-AeroPeek "Miranda IM" (although you should not that depends on the window title matching just that one window — and Miranda lets you change what your title is, so you may have to adjust it).

Of course, that script really deserves explanation, because it’s showing off quite a few advanced things…

The first thing is that I’m using a Try/Catch block in the BEGIN block to make sure I only execute that code once. You can’t call Add-Type with the same code multiple times in a single PowerShell session, because the type will already exist when you call it the second time. So the code in the try block will throw an exception if the type doesn’t already exist, and in the catch handler, we’ll create the type, and define the other function we need.

Add-Type is a super-powerful cmdlet which compiles code on the fly (or imports types from pre-compiled assemblies). In this case we’re using it to import a little class called Dwm which I started writing myself from PInvoke.net and the MSDN documentation, but then eventually copied most of from a NeoWin forum thread… All this class really does is define the API function and the flags we need to pass to it, and then provides a wrapper for the DwmSetWindowAttribute call. We could have written that call in PowerShell, but at the end of the day, once you start compiling C# code in PowerShell, it’s hard to know when to stop ;)

The Select-Window function is (yet another customized version of) a function I wrote awhile back on PoshCode as part of my (still in progress) rewrite of WASP to use the UIAutomationClient … I’ve just modified it to add only the three properties of the window that I’m interested in: Title and ProcessId (for identifying the correct windows) and Handle (for passing to the DwmSetWindowAttribute call). It uses the RootElement property of System.Windows.Automation.AutomationElement to do a search, and then a series of GetCurrentPropertyValue calls to determine the Name, ProcessId, and NativeWindowHandle of the windows it finds.

That’s pretty much all there is to it, other than filtering out the window(s) that we want and actually calling the API. I think I’m going to have to play a little bit more with this to see what else we can do — I’ve already realized that this means we can make little widgets with PowerBoots and set them to stick around just like regular desktop gadgets …

Reblog this post [with Zemanta]

Well, I’m back already with an update for the Growl module I posted yesterday

This new version is a true PowerShell 2.0 only module, because I found that the Growl callbacks can only be handled in PowerShell 2.0 anyway, so in order to add support for that, I went ahead and upgraded the rest.

The Growl module is now designed to be used BY your PowerShell scripts and modules. The idea is that if you wrote, say, a script/module to check for email and called it PoshMail … you could start up Growl like this:


Import-Module Growl

## At least once (e.g.: on the first Type you register) you should include an AppIcon :)
Register-GrowlType PoshMail NewMail -AppIcon $PoshMailFolder\Email-48.png
## If you want to, you can still override the icon per notice type
Register-GrowlType PoshMail Hotmail -Icon $PoshMailFolder\Hotmail-48.png
Register-GrowlType PoshMail GMail -Icon $PoshMailFolder\GMail-48.png
 

Now, technically that’s all we have to do. At that point, we could pop up Growl notices for either Hotmail or GMail … let’s say our fictitious script (which is running in the background on an event timer) discovers a new message … you could notify the user with a Url callback. Let’s assume that you have a few variables set after checking for email:

  • $Number is the number of email messages
  • $Subjects is an array of email subject lines
  • $Urls is an array of links to the emails


Send-Growl PoshMail GMail "You have $number new messages" ($Summary[0..2] -join "`n") -Url $Urls[0]
# OR ...
Send-Growl PoshMail Hotmail "You have $number new messages" ($Summary[0..2] -join "`n") -Url $Urls[0]
 

Of course, if you wanted to launch your Outlook 2010 preview because you discovered new POP or IMAP mail … or because you want to use Outlook to read your Hotmail/GMail … then a callback URL isn’t going to cut it. In that case, you want to handle the click event yourself:



## We would need it to launch something appropriate on receipt of new POP3 email, for instance.
Register-GrowlCallback {
   PARAM( $response, $context )
   # This is just here for your sake, because I know you want to know what else is in there:
   Write-Host $("Response Type: {0}`nNotification ID: {1}`nCallback Data: {2}`nCallback Data Type: {3}" -f $context.Result, $context.NotificationID, $context.Data, $context.Type) -fore Yellow
   if($context.Result -eq "Click") {
      ## Start the default email client
      Start-Process $(
         $MailTo = (gp Registry::HKEY_CLASSES_ROOT\mailto\shell\open\command)."(default)" -split " "
         for($i=0;$i-lt$mailto.Count;$i++) {
            $email = "$($mailto[0..$i])".Trim('"')
            if(Resolve-Path $email){ return $email }
         }
      )

   }
}
 

Something like that should work regardless of your actual email client, and then you just have to pass a callback value to make sure your function gets called:


# This would trigger the callback REGARDLESS of whether it was clicked.
Send-Growl PoshMail NewMail "You have $number new messages" ($Summary[0..2] -join "`n") -CallbackData "Data" "POP3 Callback"
 

There are a lot of other possibilities here, from alerting when long running commands finish (think PSJobs, or even remote jobs) to … writing popup-based PowerShell instant messengers, or even … using Growl as a ghetto inter-process communication medium which works on multiple PCs. Ok, that’s maybe a bit much, but the point is: sky’s the limit. Have a little fun. Note that the machine SENDING the popups doesn’t necessarily have to have Growl installed — you could just copy the libraries over and then send remote growls….

Growl

This is just a quick post (as I promised, recently) to let you all know that I’ve published the first release of my Growl for Windows module for PowerShell over on PoshCode.

I haven’t been able to get callbacks to work, and I spent way too much time playing with them instead of publishing this, or working on remote growls … but nonetheless, it’s sufficient to let you pop up growl notifications from scripts, which you can, of course, customize in Growl itself.

You can add a few additional notice types very simply (you just hard code them in the script, copying lines 19 and 21 — remember you need to use the same notice names each time anyway) ... and then tweak them in Growl to have different display types, or to make some of your notice types sticky, or have them forwarded to your iPhone or whatever.

Here’s the first version of the script:

Note: if you want to use this on PowerShell 1.0, just comment out the [Parameter( lines, and dot-source it, it should work fine. In PowerShell 2.0 you’ll want to load it as a module, because doing so will hide some of those ugly script variables.

  • Have you ever wanted to be able to ask your computer questions and have it answer you out loud?
  • Have you ever wondered if your computer could be more like the ones running the Star Trek Enterprise, responding to voice queries and commands?
  • Have you played with ZWave or X10 home automation and thought that voice control of your devices would be an obvious next step?

Well, ok … I’m not going to show you how to turn on lights or work with home automation — but that’s the main thing that keeps me thinking about this voice-recognition stuff. What geek doesn’t want to walk into the living room and say “Computer: Lights On” and have it work?

Instead, as a first step to all of that, let me show you how to use PowerShell to do simple voice command recognition scripts … which can fire off any PowerShell script you care to write! Read the rest of this entry »

So a lot of people seem to be taking the latest missteps by Twitter’s management (and the accompanying admission of bad design) as an opportunity to try out some alternatives. Many of them seem to be coming over to FriendFeed (which has been better than Twitter for a long time, but nevermind that) ... so I thought I’d update and release a PowerShell 2.0 script I wrote to create imaginary friends out of your friends that stay on Twitter.

The first part of it is a WatiN script (that automates your browser) called New-ImaginaryFriend which takes three parameters: a name for the imaginary friend, a url for an avatar for the friend, and a HashTable… Of course, we sort-of cheat by using the HashTable … it’s basically a bunch of key-value pairs of remote services and user names. You can use it to add twitter ID’s like twitter="jsnover" or blogs like blog="http://HuddledMasses.org/" etc. You can even add multiple sources (eg: twitter + diigo, two blogs, etc) to a single new imaginary friend 8)...

This script is done using WatiN because the FriendFeed API doesn’t support creating imaginary friends yet, and as a result it’s slow, and requires IE (and doesn’t seem to work very well with IE8 — at least, I couldn’t get it to set the avatars using IE 8 on Windows 7, so I commented out the avatar part of the next-to-last line).

The other part of the script is a pair of functions: the first is Get-FriendFeedFriends which retrieves profile information for all your friends in a slick format that includes all their services and such … you may find other uses for this later ;-) , the second is Get-TwitterFriends … Both have an -Exclude parameter so you can pass it a list of people to ignore.

When you put these three functions together, you can just import the FriendFeed module, and start creating friends (don’t forget this version of the scripts only works with IE6 or IE7 for the purpose of avatars, as WatiN can’t seem to set the file upload value in IE8 yet).


Import-Module FriendFeed
## Get any twitter friends who aren't on friendfeed
## Make sure you use FriendFeed's built in "add all your twitter friends" first
$twits = Get-TwitterFriends `
         -Nickname jaykul
         -Exclude $(Get-FriendFeedFriends jaykul | select -expand twitter)

## Add them to friend feed
foreach($twit in $twits) {
   New-ImaginaryFriend $twit.name @{twitter=$twit.screen_name} $twit.profile_image_url
}

You can download all of the required modules at once (7z), or grab the latest versions of them from PoshCode: FriendFeed, HttpRest, and WatiN … but if you do that, you’ll still need to get the binaries separately :-/

Reblog this post [with Zemanta]

This is an update to a previous article.

Someone asked (on Twitter) about using WatiN from PowerShell, and pointed to this old post by Scott Hanselman saying he was having the same problems … so I wrote this to help them out:

WatiN requires -STA mode

Note: WatiN requires Single Threaded Apartment mode, so you need to be using PowerShell 2.0 (currently in CTP3) in order for any of this to work, and you need to pass the -STA parameter to PowerShell. Regardless, I thought I’d throw two tips out here:

Don’t use LoadFile, use LoadFrom

I’m not 100% sure when it’s appropriate to use LoadFile in PowerShell, but I can tell you that if your assembly is in a folder with a bunch of other assemblies upon which it depends … you need to use [Reflection.Assembly]::LoadFrom( $path ) instead — because the .net loader will be able to find the dependencies.

Generate some functions to help yourself out

It’s trivial to do code-generation in PowerShell, and WatiN is not friendly to the PowerShell syntax, so you’re going to want to generate a bunch of them. To get you started, here’s a set of Find-* functions to let you find each type of element that WatiN recognizes and automates… by name, id, class, style … well, by any attribute, really:

Read the rest of this entry »

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 »

So earlier today someone asked how they could tell if PowerBoots graphics would be hardware accelerated on their system … and I found the question painful to answer because the answer is that you take the high-order word of the RenderCapability.Tier property, and that indicates 0, 1, or 2 … where a higher number indicates a higher level of hardware acceleration:

  • Rendering Tier 0 No graphics hardware acceleration. The DirectX version level is less than version 7.0.
  • Rendering Tier 1 Partial graphics hardware acceleration. The DirectX version level is greater than or equal to version 7.0, and lesser than version 9.0.
  • Rendering Tier 2 Most graphics features use graphics hardware acceleration. The DirectX version level is greater than or equal to version 9.0.
Logical and arithmetic rotate one bit left
Shift-Left 1, via Wikipedia

The problem is that in PowerShell, getting the “high-order word” of an integer is a little annoying, because the normal way to do that is to right-shift the integer to throw away the low-order word … and PowerShell is missing the shift operators. Why? I don’t know. In any case, I figured, well, I’ll just write it as a function with a call out to C# to make my life simpler. The one catch is that the Add-Type cmdlet that lets you inject C# classes is new in PowerShell 2.0, so if you want to use this script in PowerShell 1.0 you need to get the New-Type function from PoshCode and replace Add-Type with New-Type in the script.

Read the rest of this entry »
Search My Content