PowerShell 2.0 RC: Working with .NET Callbacks – Part 1 - Synchronous

by oising 18. July 2009 08:22

updated 2009/7/20: added link to PSEventing for v1.0 event handling
updated 2009/7/24: added link to Get-Delegate script for v1.0 callbacks

There have been some nice improvements made in the latest build of PowerShell with respect to interop with the "callback” pattern in .NET. What exactly are callbacks anyway? It’s exactly what it sounds like, pretty much. In .NET there are sometimes APIs you need to call that expect you to hand them delegates (pointers to methods) which that API will call some time in the future, usually based on certain conditions being fulfilled. If that sounds a bit like .NET events, you’d be right. An event is a much gussied-up callback - it’s just a way of permitting multiple methods to be invoked in response to some condition.

Synchronous Callbacks in PowerShell v1.0

In .NET there are two types of callbacks: Asynchronous (non-blocking) and synchronous (blocking). In PowerShell v1.0, the only callbacks that were catered for were for EventHandler Delegates. This is the method signature that most of the Windows Forms controls expect to call back to in response to button clicks etc. You may have seen code similar to:

$button = new-object system.windows.forms.button
$button.add_Click( { $form.Close() } )

This works because in PowerShell v1.0, there is specialized support for this kind of callback to methods with the EventHandler signature, that is to say, methods with parameters of (object sender, EventArgs e). PowerShell is able to run the ScriptBlock in response to the button being clicked and will even pass the two arguments to the scriptblock for you. When the form is shown from a PowerShell script, there is a single thread that is running the message loop for the application. It is this same thread that handles running the script. In the PowerShell engine, there is a pool of threads created at startup, and each of them has its own “Runspace” for running scripts. Because it is one of the PowerShell threads that is running the application, that same thread is able to run the ScriptBlocks in its Runspace when called upon to do so. Although it is in a slightly roundabout way, this is an example of a synchronous callback. This single application thread is effectively waiting (blocked) for the callback to occur in response to a button click (in reality, it’s doing other things while waiting, but it’s still waiting.)

Some rather creative folks, namely one of the primary developers of PowerShell, wrote a delegate/scriptblock binder in pure script some years ago which you can use to pass script to a .NET api to be called back to in a synchronous manner.  See: Creating arbitrary delegates to ScriptBlocks.

If you want to work with .NET events in version 1.0 of PowerShell, you’ll need an add-on, like my PSEventing Snap-In.

Synchronous Callbacks in PowerShell v2.0

In the latest and greatest version of PowerShell, v2.0 RC (which comes with the public Windows 7 RC), synchronous callbacks got a whole lot easier. PowerShell is now able to deal with pretty much ANY delegate signature, automatically. Lets test this by using the .NET 3.5 System.Func<T, TResult> delegate. This is a generic delegate which lets us pass a method to an API expecting a callback to a method which has one parameters of type T1, and will return type T2. Because it’s generic, we get to pick which parameters. Lets demo creating a ScriptBlock that will be passed a DateTime and returns a String:

add-type –assembly system.core # load .net 3.5
$callback = [system.func[datetime, string]] { param($date); "the date is $date" }
$callback.Invoke( [datetime]::now )
# returns
”the date is 2009/07/14 21:50:35”

We can even take advantage of PowerShell’s super-versatile parameter binder by passing it in a string and having it get coerced to DateTime with ne’er a Parse in sight!

$callback.Invoke(“1/1/2009”)
”the date is 2009/01/01 00:00:00”

So, what’s the point of all this? Why go to all that trouble? Why not just write a function? The point is that functions cannot be called by .NET directly. So when would you need a callback like this in a PowerShell script?

Calling Web Services with Invalid, Untrusted or Expired SSL Certificates

The title says it all – sometimes you need to do this. Usually it’s because you’re working with self-signed, expired or otherwise invalid certificates on a QA or Development system. The New-WebServiceProxy Cmdlet in v2.0 is great for calling Web Services, but it doesn’t have a switch to ignore invalid certificates. If you were writing .NET code in C# or VB.NET, the way go about this is to pass a callback method to an API that expects a RemoteCertificateValidationCallback Delegate. This delegate is designed to point to a method that is passed a handful of arguments describing the attempted connection, and is expected to return a boolean; that is to say, true or false. True means “sure, the connection looks fine – go for it.” A value of False being returned tells .NET to stop the connection before it happens.

The amount of .NET code needed to do this is not a ton, but it’s still a fair handful of lines. Check out this example here: http://blog.jameshiggs.com/2008/05/01/c-how-to-accept-an-invalid-ssl-certificate-programmatically/

Now let’s see how much PowerShell script is needed for this same task:

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }

Bwahahahah! Eat that, Mr. C# coder! Again, PowerShell’s binder comes into play here and automatically casts our ScriptBlock to a RemoteCertificateValidateCallback delegate (it’s not really a cast – there is no conversion – it’s a sizeable chunk of code.) From this point on, any attempts to use the New-WebServiceProxy Cmdlet with dodgy SSL certificates will succeed without so much as a warning. In fact, pretty much any other classes, like WebRequest will behave the same way. It’s important to note that this only affects the current AppDomain, that is to say, the current PowerShell process. Other processes on the system will continue to stick their nose up at dodgy SSL certs. Quit PowerShell and restart it -- or set that ServerCertificateValidationCallback property to $null -- and all is right in the world again. This works as long as you use one of PowerShell’s threads to do the work of connecting; i.e. don’t use an asynchronous request. This ensures there is a Runspace available to execute this ScriptBlock.

Converting Synchronous Callbacks into Events

This is really quite straightforward. Taking the previous example, we would generate an event inside the scriptblock using New-Event, and then bind one or more event handlers using the Register-EngineEvent Cmdlet:

[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {
		new-event -SourceIdentifier SslCheck -MessageData $args > $null
		$true
	}
# dump out arguments to cert validate callback
Register-EngineEvent -SourceIdentifier SslCheck -Action { write-output $args }
Next time, we'll get into some meatier stuff. Have fun!

In Part Two: Asynchronous Callbacks in PowerShell v1.0 and v2.0

Tags:

.NET | CTP3 | Eventing | Monad | PowerShell | PowerShell 2.0 | PSCX | RC | Windows 7

PowerShell 2.0: Quick Tip – Capture Verbose, Error & Warning Streams

by oising 18. July 2009 04:02

update: missing backtick ` to escape the $verbosepreference variable

A question came up on stack overflow (you don’t know what that is? shame on you!) today from someone asking how they could capture the Verbose stream from a pipeline they ran in a C# program. As it turns out, the same technique is used in script, so I’ll give that example instead since I’m sure the C# guys and gals will have no problem converting the script.

The key is using the new (to v2.0) System.Management.Automation.PowerShell Type, which has a built-in Type Accelerator of [powershell]. It has a static method, Create, which is used to create an instance. This instance is pretty much ready to roll. It has a Streams property, which is of Type PSDataStreams. This Type has properties representing each collection of Error, Progress, Verbose, Debug and Warning.

$ps = [powershell]::create()
$ps.Commands.AddScript("`$verbosepreference='continue'; write-verbose 42")
$ps.invoke()
$ps.streams.verbose
Which yields the VerboseRecord that was written out:
Message InvocationInfo                              PipelineIterationInfo
------- --------------                              ---------------------
42      System.Management.Automation.InvocationInfo {0, 0}
What's important to note about the above example is that I had to set the $verbosepreference to at least "continue" (the default is silentlycontinue) in order for the verbose record to be written. Have fun!

Tags:

.NET | Cmdlets | Monad | PowerShell | PowerShell 2.0 | RC | Windows 7

PowerShell v2.0 – Differences Between CTP3/Win7Beta and Win7RC

by oising 22. May 2009 21:25

update #1 2009/5/23: noted that local jobs (start-job) no longer require an elevated shell.

update #2 2009/5/28: ISE object model and shortcut changes; update for set-psessionconfiguration cmdlet (new screenshot); module changes (highlighted new manifest member names, binary module can now be root module);

I’ve been meaning to write this for a while, but it’s been a busy time. This is a comparison of the significant differences between the standalone CTP3 or Windows 7 Beta version and the version that comes with Win7 RC (6.1.7100.0). For all intents and purposes, the CTP3 version (6.1.6469.0) is exactly the same as the Windows 7 Beta (6.1.7000.0) version.

This is not going to be an exhaustive list of differences, but I will continue to update this post as I find more things worth documenting. It should be safe to bookmark the permalink. One of the nicest things about this release is that it appears that, without exception, all built-in Cmdlets have help. A lot of things have been cleaned up and fixed in this build, from formatting of text to typos and minor bugs/glitches.

Cmdlet Differences

This is a table listing Cmdlets that have either changed (navy), been added (green, underlined) or removed (red, strike-through.) Changed Cmdlets have their parameters listed in the second column. A changed parameter means that its Type has been changed; e.g. it accepts a different .NET object than before. This is generally nothing to worry about since the corresponding source of such objects is usually changed to match – typically another Cmdlet. Parameters  and Cmdlets that have not changed, are not listed.

Cmdlet Parameters
Invoke-Command
Remove-Computer
Add-Computer
Rename-Computer
Test-Connection
Export-FormatData
Receive-Job
Start-Job
Get-Module
New-ModuleManifest
Set-PSBreakpoint
Enable-PSRemoting
Remove-PSSession
Enter-PSSession
New-PSSession
Export-PSSession
Import-PSSession
New-PSSessionOption
Get-WSManInstance
Set-PSSessionConfiguration ShowSecurityDescriptorUI

Alias Changes

Some tweaking of aliases here. Personally I find alias changes in general to get under my skin. Aliases are the first thing I learn and the first thing to trip me up when things change. Regardless, the changes appear to make sense and are perhaps a bit more mnemonic than before.

Removed

emm (Export-ModuleMember), which (Get-Command) and grid (Out-Gridview)

Added

ise (powershell_ise.exe), rmo (Remove-Module) and saps (Start-Process)

Changed

imo –> ipmo (Import-Module)

Language Enhancements

The major change that has come to light so far is that statements are now allowed on the right hand of an expression without having to use subexpressions. This is a great fix, and one that will reduce the margin for error (and confusion) by a large amount. Previously in CTP3, in order to use a statement like “if”, you had to use the following syntax:

$result = $( if ($true) { 42 } )

Now, you can drop the $( and ):

$result = if ($true) { 42 }

or

$sequence = foreach ($i in 0..15) { [math]::pow(2, $i) }

This is truly great stuff.

Jobs/Remoting

The best news here is the abundant help now available at your fingertips. Lots of examples and meaty information concerning PSSessions and PSSessionConfigurations.

Local Jobs

Local jobs created now with Start-Job { ... } use an IPC channel to talk to the local WinRM service to create jobs instead of using the more heavyweight HTTP channel. Yes, you can infer from this that local jobs are still out of process; they run in their own isolated runspace and have no access to the interactive session. What this means in simpler terms is that local jobs are a lot faster now to get started. The biggest win for local jobs is that they no longer require an elevated shell! you can submit local jobs as a regular user now – just not remote ones (unless the applicable remote PSSessionConfiguration is set to allow this - by default the ACL is admins only).

Remoting

A welcome addition to this build is a new, dedicated Enable-PSRemoting Cmdlet:

The Enable-PSRemoting cmdlet configures the computer to receive Windows PowerShell remote commands that are sent by using the WS-Management technology.

You need to run this command only once on each computer that will receive commands. You do not need to run it on computers that only send commands. Because the configuration activates listeners, it is prudent to run it only where it is needed.

The Enable-PSRemoting cmdlet performs the following operations:

  • Runs the Set-WSManQuickConfig cmdlet, which performs the following tasks:
    • Starts the WinRM service.
    • Sets the startup type on the WinRM service to Automatic.
    • Creates a listener to accept requests on any IP address.
    • Enables a firewall exception for WS-Management communications.
  • Enables all registered Windows PowerShell session configurations to receive instructions from a remote computer.
    • Registers the "Microsoft.PowerShell" session configuration, if it is not already registered.
    • Registers the "Microsoft.PowerShell32" session configuration on 64-bit computers, if it is not already registered.
    • Removes the "Deny Everyone" setting from the security descriptor for all the registered session configurations.
    • Restarts the WinRM service to make the preceding changes effective.

To run this cmdlet on Windows Vista, Windows Server 2008, and later versions of Windows, you must start Windows PowerShell with the "Run as administrator" option.

Session Configurations

There are a raft of Cmdlets dedicated to managing session configurations. So what is a session configuration? To qoute the ever-present help system:

A session configuration is a group of settings on the local computer that define the environment for the Windows PowerShell sessions that are created when remote users connect to the local computer.

Administrators of the computer can use session configurations to protect the computer and to define custom environments for users who connect to the computer.

Administrators can also use session configurations to determine the permissions that are required to connect to the computer remotely. By default, only members of the Administrators group have permission to use the session configuration to connect remotely, but you can change the default settings to allow all users, or selected users, to connect remotely to your computer.

Session configurations are a feature of Web Services for Management (WS-Management) based Windows PowerShell remoting. They are used only when you use the New-PSSession, Invoke-Command, or Enter-PSSession cmdlets to connect to a remote computer.

Note: To manage the session configurations on a computer that is running Windows Vista, Windows Server 2008, or a later version of Windows, start Windows PowerShell with the "Run as administrator" option.

image

That SecurityDescriptorSddl property looks like a lot of fun to modify, right? Don’t worry, the ShowSecurityDescriptorUI comes to the rescue:

image

Modules

Manifest Members

Modules have received some nice incremental improvements as well as some syntactic changes. You should have noticed above that the manifest fields (exposed as parameters on New-ModuleManifest)  have changed to reflect the two-stage process of how a module’s members get exposed to the importer’s scope. A module passively exports members with Export-ModuleMember, and the caller actively imports them with Import-Module; hence “FunctionsToExport,” which subtly says that this can be imported.

Binary Modules

A binary module is now allowed to be the root module in a manifest by pointing the ModuleToProcess key at the assembly, e.g. ModuleToProcess = “Pscx.dll.” This seems more intuitive than before with the [seemingly] arbitrary restriction that they must be secondary to a text based psm1.

Built-In Modules

Shipping with Windows 7 for PowerShell comes two new Modules along with the others you should have noticed that arrived with CTP3 (BitsTransfer/FileTransfer and PSDiagnostics.)

AppLocker

AppLocker provides simple, powerful, rule-based structures for specifying which applications can run that are centrally managed using Group Policy. It introduces "publisher rules" that are based on an application's digital signature, making it possible to build strong rules that account for application updates. For example, an organization can create a rule to "allow all versions greater than 1.0 of Microsoft Dynamics CRM to run if signed by Microsoft." With correctly structured rules, IT professionals can safely deploy updates to allowed applications without having to build a new rule for each version update.

About AppLocker on TechNet

TroubleshootingPack

Windows Troubleshooting Platform (WTP) provides ISVs, OEMs, and administrators the ability to write troubleshooting packs that discover and resolve software and hardware issues, such as configuration issues, failed hardware, network issues, and application compatibility issues. In WTP, an issue is referred to as a root cause. Previously, troubleshooting software and hardware issues was a manual process; however, using WTP you can automate the process of fixing the most common issues that the user might encounter.

About WTP on MSDN

PowerShell Integrated Scripting Environment

The ISE has been vastly improved in terms of usability and sharpness in this build. Difficult to isolate any one part of it that is much better; the whole experience is just way smoother and intuitive.

image

ISE Object Model

The object model naming has changed quite a bit to be more intuitive and friendly to beginners. Who needs to know about Runspaces? It’s a Tab, silly!

CTP3 RC
$psise.CurrentOpenedRunspace $psise.CurrentPowerShellTab
$psise.CurrentOpenedRunspace.ToolsMenu $psise.CurrentPowerShellTab.AddOnsMenu
$psise.CurrentOpenedRunspace.OpenedFiles $psise.CurrentPowerShellTab.Files
$psise.CurrentOpenedRunspace.OpenedFiles.RemoveUnsaved($file) $psise.CurrentPowerShellTab.Files.Remove($file,$true)
$psise.OpenedRunspaces $psise.PowerShellTabs
$psise.OpenedRunspaces[1].Execute("string") $psise.PowerShellTabs[1].Invoke({scriptBlock})*
$psise.CurrentOpenedFile $psise.CurrentFile
$psise.Options.LocalHelp $psise.Options.UseLocalHelp

* Note: You may only invoke script on a tab other than the tab you’re using to execute this command (because you can’t run two commands at the same time on the same tab!)

The PowerShell team posted a way to make the RC build fairly compatible with CTP3 by adding new members to the $PSISE object which will proxy attempts to use the old API to the new API. The post is called “Update-TypeData, ISE CTP3 vs ISE RC, and Teched2009 Demos.”

Shortcut Changes

The shortcut to jump to the Script Pane is now Ctrl+I. (use Ctrl+D to jump to the Command pane).

Summary

The RC build is all about bugfixes and incremental improvements. I’m sure there’ll be more fixes and additions as we near RTW/RTM and I’ll be here to post as much info about them as possible. This is still just a drop in the ocean of what PowerShell 2.0 can do, and I’ll post more technical demos of features over the next little while.

Tips: run “Get-Help about_*” to get a list of all the overview topics for the various features in PowerShell v2.

Have fun!

Tags:

.NET | Cmdlets | CTP3 | Microsoft | Modules | PowerShell | PowerShell 2.0 | RC | Windows 7

About the author

Oisin Grehan lives in Montreal, Canada and builds all sorts of crap for all sorts of people.

Month List

Page List