| Arild さんのプロフィールRandom thoughts about An...ブログリスト | ヘルプ |
|
7月3日 Powershell script blocks as .NET event handlersScott Hanselman posts an interesting script where he uses the NSvn library (the .NET wrapper library for Subversion we wrote for AnkhSVN) directly from Powershell to either checkout from a Subversion repository or update an existing working copy. This is a great way to use NSvn and one we definitely didn’t have in mind when writing it. He also asks: “How do you do a delegate from within PowerShell if a .NET Assembly needs a delegate with a certain signature? This library will call me back with status updates I'd like to broker to write-progress.” NSvn’s NSvn.Core.Client class has an event called Notification, which is raised for every notification callback we get from the underlying Subversion C library. Subversion calls back for various reasons, including whenever a file has been downloaded as part of an update operation (in Subversion, a checkout is really just a special case of an update where the working copy has none of the files). In the Notification event, the NotificationEventArgs object’s Path property contains the path to the file or directory that has been updated, while the .Action property indicates what kind of operation has been performed against the path. will usually contain NotifyAction.UpdateUpdate or NotifyAction.UpdateAdd (in the case of a checkout, all callbacks will be of the NotifyAction.UpdateAdd type). See here for a listing of the various action types. Now, the question is, how does one consume this event from a Powershell script? Powershell supports anonymous functions called script blocks. These are roughly equivalent to C#’s anonymous delegates. Script blocks can be stored in a variable and invoked at any given time using the & operator:
It turns out that a script block can be cast to any arbitrary delegate and used as an event handler:
In our case, we want a delegate of type NotificationDelegate (ok, not a good name for an event handler delegate, or so FXCop tells me, but anyway…), which has the following signature:
The one thing that’s not obvious about scriptblocks as event handlers is how you get hold of the eventargs instance. The documentation does not go out of its way to tell you about it either, so I started experimenting. My first attempt looked like this:
The param syntax is one of the ways you declare parameters to a regular function or script. If it worked as expected, it would have printed out the path for each item being updated or checked out. Of course, “as expected” being the operative phrase here. $e turned out to be null. Another way in Powershell to get hold of arguments to a function is through the special $args array. Unfortunately, this didn’t seem to work either:
Another Reading of The Fine Manual (why is this thing in a Word document, anyway?) finally turned up an example, in which they use the $_ special variable to get hold of the EventArgs instance passed as a second parameter to all (most?) .NET events. This seems like a weird choice to me, $_ is generally used to hold the “current” object in a pipeline. Why did they overload it like this? To make things even worse, the sender argument is available in another special variable called $this. Why “$this”? It’s not like the usage in any way resembles the way the this keyword is used in other languages. Anyway, armed with this hard-won knowledge, rewriting Scott’s script to “broker” the notification events to Write-Progress proves to be rather simple. This is the revised script: param ([string]$svnurl = $(read-host "Please specify the path to SVN"), [string]$svnlocalpath = $(read-host "Please specify the local path") ) if ([System.IO.Path]::IsPathRooted($svnlocalpath) -eq $false) { throw "Please specific a local absolute path" } [System.Reflection.Assembly]::LoadFrom((join-Path "$env:PROGRAMFILES\AnkhSVN" -childPath NSvn.Common.dll)) [System.Reflection.Assembly]::LoadFrom((join-Path "$env:PROGRAMFILES\AnkhSVN" -childPath NSvn.Core.dll)) $PRIVATE:svnclient = new-object NSvn.Core.Client $PRIVATE:notificationcallback = [NSvn.Core.NotificationDelegate]{ Write-Progress -status ("{0}: {1}" -f ($_.Action, $_.Path)) -activity "Updating Working Copy" } $PRIVATE:svnclient.add_Notification($notificationcallback) $PRIVATE:svnclient.AuthBaton.Add( [NSvn.Core.AuthenticationProvider]::GetWindowsSimpleProvider() ) if ((test-Path $svnlocalpath) -eq $true ) { write-progress -status "Updating from $svnurl" -activity "Updating Working Copy" $PRIVATE:svnclient.Update($svnlocalpath, [NSvn.Core.Revision]::Head, $true) } else { write-progress -status "Checkout from $svnurl" -activity "Updating Working Copy" $PRIVATE:svnclient.Checkout($svnurl, $svnlocalpath, [NSvn.Core.Revision]::Head, $true) } コメント (8 件)
トラックバック (1 件)この記事のトラックバックの URL は次のとおりです。 http://arildf.spaces.live.com/blog/cns!E99F8B43533149B0!127.trak この記事を参照しているブログ
|
|
|