I wrote a set of PowerShell cmdlets for signing unsigned assemblies with the SNK of your choice. Certain platforms (I'm looking at you, SharePoint) just work better with SN assemblies, but this constrains dependencies to NuGet packages that contain only signed assemblies.
Many people believe using strong names is more of a hindrance than a help (myself included,) but corporate policies often dictate their usage. My strong naming package makes it simple to sign assemblies, even without the source code. No more begging package authors to release signed packages, or fiddling with github repos.
If you sign 3rd party NuGet package assemblies, I suggest signing them in-situ, in their original locations, so you can continue to reference the package and so not risk missing out on updates. When you update a package, simply resign the assemblies and compile away.
To get started, open the NuGet PM console and type:
Install-Package Nivot.StrongNaming
You must have a solution open at this time. This package is not tied to any project; it just adds new commands to the PM console (documented below.)
Nivot.StrongNaming
- v1.0.0 [2013/04/29]
- v1.0.1 [2013/04/29]
- v1.0.2 [2013/04/30]
- Added license and project URL.
- Added readme.MD
About
A set of PowerShell Cmdlets to facilitate signing of unsigned 3rd party assemblies with a key of your choice, to allow them to be referenced by strongly named projects.
A NuGet package is available at: https://nuget.org/packages/Nivot.StrongNaming
Syntax
All cmdlets accept pipeline input. The AssemblyFile parameter is aliased to PSPath, so it will bind to piped files.
-
Test-StrongName [-AssemblyFile] <string[]> [<CommonParameters>]
Returns true if an assembly has a strong name.
-
Import-StrongNameKeyPair [-KeyFile] <string> [<CommonParameters>]
-
Import-StrongNameKeyPair [-KeyFile] <string> -Password <securestring> [<CommonParameters>]
Imports a simple unprotected SNK or a password-protected PFX, returning a StrongNameKeyPair instance for consumption by Set-StrongName. If your PFX file has a blank password, you must provide a SecureString of the empty string "". SecureString instances are returned from the Read-Host cmdlet with the -AsSecureString parameter.
-
Set-StrongName [-AssemblyFile] <string[]> -KeyPair <StrongNameKeyPair> [-NoBackup] [-Passthru] [-Force] [-DelaySign] [-WhatIf] [-Confirm] [<CommonParameters>]
Assigns a strong name identity to an assembly.
The -KeyPair parameter accepts a System.Reflection.StrongNameKeyPair output from the Import-StrongNameKeyPair cmdlet., which accepts either simple unprotected SNK files or password-protected PFX files.
The -NoBackup switch directs the cmdlet to skip creating a .bak file alongside the newly signed assembly.
The -Passthru switch will output a FileInfo representing the newly signed assembly to the pipeline.
The -DelaySign switch will create a delay-signed assembly from a public key only SNK (it can also create one if the SNK contains both private and public keys.) This is useful if you can't get access to the full private key at your company. This will allow you to compile against previously unsigned nuget packages at least.
The -Force switch will allow you to overwrite an existing strong name on an assembly.
NOTE: You may supply -WhatIf to see what would be done, without actually doing it.
-
Get-AssemblyName [-AssemblyFile] <string[]> [<CommonParameters>]
Returns a System.Reflection.AssemblyName instance from any assembly file.
FAQ: How Do I?
Get the default package root folder
PM> $root = join-path (split-path $dte.solution.filename) packages
Load an unprotected snk
PM> $key = Import-StrongNameKeyPair -KeyFile .\folder\key.snk
PM> dir *.dll | Set-StrongName -KeyPair $key -Verbose
Load a password-protected PFX
PM> $key = Import-StrongNameKeyPair -KeyFile .\folder\key.pfx -Password (Read-Host -AsSecureString)
******
Sign some unsigned assemblies
PM> cd (join-path $root unsignedPackage)
PM> dir -rec *.dll | set-strongname -keypair $key -verbose
(Re)sign some assemblies forcefully
PM> dir -rec *.dll | set-strongname -keypair $key -force
Sign only unsigned assemblies
PM> dir -rec *.dll | where { -not (test-strongname $_) } | set-strongname -keypair $key -verbose
I wrote this a good long time ago now, but somehow I never bothered to blog it. I guess I thought it was a bit too specific to be of general interest to people, but my good friend and long time MVP Karl Prosser tells me otherwise. This is the Wiki page from bitbucket repository, the link for which you'll find at the foot of the page. Basically, it's a module that lets you "peek" at objects to view and manipulate their internals. That is to say, you can access non-public methods, fields and properties just like they were public. You can also create instances of non-public types easily. I originally wrote this to help me when debugging things and to explore PowerShell itself from the inside out, interactively. It turns out to be a pretty powerful tool. I hope you enjoy it.
Here you'll find examples of how to peek and poke objects using the Poke module.
Version History
- 1.0.1 - Compatibility fixes for v3 beta / .net 4.5
- 1.0 - Initial release
Examples
# peek at a Job instance using pipeline syntax
$job = start-job { 42 } | peek
$job | get-member
which results in the new extended output format for get-member:
TypeName:
Pokeable.System.Management.Automation.PSRemotingJob#676f9716-c167-47c6-ab0d-4d8cedbbe44d
Name Modifier MemberType Definition
---- -------- ---------- ----------
Equals public Method bool Equals(System.Object obj)
GetHashCode public Method int GetHashCode()
GetType public Method type GetType()
CheckDisconnectedAndUpdateState private Method* void CheckDisconnectedAndUpdateState(System....
CommonInit private Method* void CommonInit(int throttleLimit, System.Co...
ConnectJob internal Method* void ConnectJob(guid runspaceInstanceId)
ConnectJobs internal Method* void ConnectJobs()
ConstructLocation private Method* string ConstructLocation()
Dispose protected Method* void Dispose(bool disposing)
FindDisconnectedChildJob private Method* System.Management.Automation.PSRemotingChild...
GetAssociatedPowerShellObject internal Method* powershell GetAssociatedPowerShellObject(gui...
GetJobsForComputer internal Method* System.Collections.Generic.List[System.Manag...
GetJobsForOperation internal Method* System.Collections.Generic.List[System.Manag...
GetJobsForRunspace internal Method* System.Collections.Generic.List[System.Manag...
GetRunspaces internal Method* System.Collections.Generic.IEnumerable`1[[Sy...
HandleChildJobStateChanged private Method* void HandleChildJobStateChanged(System.Objec...
HandleJobUnblocked private Method* void HandleJobUnblocked(System.Object sender...
InternalStopJob internal Method* void InternalStopJob()
SetStatusMessage private Method* void SetStatusMessage()
StopJob public Method* void StopJob()
SubmitAndWaitForConnect private Method* void SubmitAndWaitForConnect(System.Collecti...
ToString public Method* string ToString()
__GetBaseObject - Method* System.Management.Automation.PSRemotingJob, ...
__GetModuleInfo - Method* psmoduleinfo __GetModuleInfo()
atleastOneChildJobFailed private Field* bool atleastOneChildJobFailed
blockedChildJobsCount private Field* int blockedChildJobsCount
CanDisconnect internal Property* bool CanDisconnect { get; set; }
disconnectedChildJobsCount private Field* int disconnectedChildJobsCount
finishedChildJobsCount private Field* int finishedChildJobsCount
HasMoreData public Property* bool HasMoreData { get; set; }
HideComputerName internal Property* bool HideComputerName { get; set; }
isDisposed private Field* bool isDisposed
Location public Property* string Location { get; set; }
moreData private Field* bool moreData
StatusMessage public Property* string StatusMessage { get; set; }
throttleManager private Field* System.Management.Automation.Remoting.Thrott...
_stopIsCalled private Field* bool _stopIsCalled
_syncObject private Field* System.Object _syncObject
You can call methods, set fields and properties (if they have setters - it doesn't matter if they're private, protected or internal.)
You can proxy/peek Types as well as instances:
# proxy a public type by piping it
$type = [text.stringbuilder] | peek
TypeName: Pokeable.System.RuntimeType#System.Text.StringBuilder
Name Modifier MemberType Definition
---- -------- ---------- ----------
Equals public Method bool Equals(System.Object obj)
GetHashCode public Method int GetHashCode()
GetType public Method type GetType()
FormatError private Method* static void FormatError()
ThreadSafeCopy private Method* static void ThreadSafeCopy(System.Char*, mscorlib, Version=4...
ToString public Method* string ToString()
__CreateInstance - Method* .ctor (), .ctor (int capacity), .ctor (string value), .ctor ...
__GetBaseObject - Method* type __GetBaseObject()
__GetModuleInfo - Method* psmoduleinfo __GetModuleInfo()
CapacityField private Field* string CapacityField
DefaultCapacity internal Field* int DefaultCapacity
MaxCapacityField private Field* string MaxCapacityField
MaxChunkSize internal Field* int MaxChunkSize
StringValueField private Field* string StringValueField
ThreadIDField private Field* string ThreadIDField
Peeking at non-public types:
# nonpublic types can't be specified using type literal
# syntax, so in this case you should use the -name parameter
$type = peek -name MS.Internal.Xml.XPath.XPathParser
# nonpublic objects returned from methods, properties or fields
# are not "peeked" themselves, so you may need to peek the return value:
$manager = peek (start-job { 42 } | peek).throttlemanager
$manager.throttlelimit = 64 # bump throttle limit ;)
Of course, you can peek instances too:
$sb = new-object system.text.stringbuilder
$proxy = peek $sb
$proxy | gm
TypeName: Pokeable.System.Text.StringBuilder#45f12364-1906-45b3-b48b-a77acd81e3f0
Name Modifier MemberType Definition
---- -------- ---------- ----------
GetHashCode public Method int GetHashCode()
GetType public Method type GetType()
Append public Method* System.Text.StringBu...
AppendFormat public Method* System.Text.StringBu...
AppendHelper private Method* void AppendHelper(st...
AppendLine public Method* System.Text.StringBu...
Clear public Method* System.Text.StringBu...
CopyTo public Method* void CopyTo(int sour...
EnsureCapacity public Method* int EnsureCapacity(i...
Equals public Method* bool Equals(System.T...
ExpandByABlock private Method* void ExpandByABlock(...
FindChunkForByte private Method* System.Text.StringBu...
FindChunkForIndex private Method* System.Text.StringBu...
Insert public Method* System.Text.StringBu...
InternalCopy internal Method* void InternalCopy(Sy...
MakeRoom private Method* void MakeRoom(int in...
Next private Method* System.Text.StringBu...
Remove private Method* System.Text.StringBu...
Replace public Method* System.Text.StringBu...
ReplaceAllInChunk private Method* void ReplaceAllInChu...
ReplaceBufferAnsiInternal internal Method* void ReplaceBufferAn...
ReplaceBufferInternal internal Method* void ReplaceBufferIn...
ReplaceInPlaceAtChunk private Method* void ReplaceInPlaceA...
StartsWith private Method* bool StartsWith(Syst...
System.Runtime.Serialization.ISerializable.GetObjectData private Method* void System.Runtime....
ToString public Method* string ToString()
VerifyClassInvariant private Method* void VerifyClassInva...
__GetBaseObject - Method* System.Text.StringBu...
__GetModuleInfo - Method* psmoduleinfo __GetMo...
Capacity public Property* int Capacity { get; ...
Chars public Property* char Chars { get; se...
Length public Property* int Length { get; se...
MaxCapacity public Property* int MaxCapacity { ge...
m_ChunkChars internal Field* char[] m_ChunkChars
m_ChunkLength internal Field* int m_ChunkLength
m_ChunkOffset internal Field* int m_ChunkOffset
m_ChunkPrevious internal Field* System.Text.StringBu...
m_MaxCapacity internal Field* int m_MaxCapacity
Have fun!
View or download the Poke module from bitbucket.
Update 2013/4/3: This is an April Fools post. I think I got a bit too subtle here despite best efforts to make this kind of ridiculous. Apparently being tagged “OMGPONIES” is not enough to give it away. Yes, the script actually works, but it’s more of a roundabout way to compare the “awkward” AWK syntax with the more readable, idiomatic PowerShell. Thanks for reading.
Sometimes PowerShell’s object pipeline just gets in the way. Often I find myself needing to pipe a few commands together and while I lay it out in my head, I am already unconsciously preparing the regular expressions I’ll need to convert the text to a structure required by the receiving command. Other times I am idly flicking through a well-thumbed and worn binder of common commands and their output on sheets of grid paper so I can figure out what columns I need to munge. Muscle memory is a hard beast to tame, so with that in mind, I started out designing a module that would let me scratch that regex itch, and also keep things kind of “PowerShelly.” It then dawned on me that I could probably extend this further into a full POSIX* compatibility layer, with the codename for this being WARD (Windows Adaptable Reusable Development). My first implementation for AWK/WARD looked a little like this:
function awk {
$input
}
While that implementation works well for a surprising number of cases, there are others which are a little trickier, so I figured I'd add a little intelligence to the commands for some of the more common use cases for AWK. Some examples:
# count lines in a file
cat text.txt | awk 'END{print NR}'
# Add all fields in ALL lines and print the sum
#
# sheet is:
#
# a, b, c, d
# 1, 2, 3, 4
# 1, 2, 3, 4
# 1, 2, 3, 4
cat sheet.txt | awk '{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}'
# Print 25 "A" characters
awk 'BEGIN{while (a++<25) s=s "A"; print s}'
Here's the current source of the AWKWARD BAPS module:
function awk ($expression) {
switch ($expression) {
# count lines
'END{print NR}' {
$input | measure
}
# print the total number of fields ("words") in all lines
'{ total = total + NF }; END {print total}' {
$input | measure -word
}
# print the sums of the fields of every line
'{s=0; for (i=1; i<=NF; i++) s=s+$i; print s}' {
$input | convertfrom-csv | measure -sum * | select property, sum
}
# add all fields in ALL lines and print the sum
'{for (i=1; i<=NF; i++) s=s+$i}; END{print s}' {
$input | convertfrom-csv | measure -sum * | measure -sum sum
}
# create a string of a specific length (e.g., generate N spaces)
([regex]'BEGIN{while \(a\+\+<(\d+)\) s=s "(.+?)"; print s}') {
$matches[2] * $matches[1]
}
# print first N lines of file (emulates behavior of "head")
([regex]'NR < (\d+)') {
$input | select -first $matches[1]
}
# print the last 2 lines of a file
'{y=x "\n" $0; x=$0};END{print y}' {
$input | select -last 2
}
# print the last line of a file
'END{print}' {
$input | select -last 1
}
# print the last N lines of a file (circular buffer)
([regex]'{a\[NR%(\d+)\]=\$0}END{for(i=NR+1;i<=NR+(?:\d+);i++)print a\[i%(?:\d+)\]}') {
$input | select -last $matches[1]
}
# emit matching lines for regex
{ $_ -as [regex] } {
$input | where { ($expression -as [regex]).ismatch($_) }
}
default {
write-warning "unsupported expression"
$input
}
}
}
}
If you want to play around with the module, you can autoinstall it by running the following one-liner:
iex (New-Object Net.WebClient).DownloadString(“http://bit.ly/e0Mw9w”)
Have fun!
Someone on Jabbr earlier today was struggling a bit with a NuGet init.ps1 script while trying to work with the IIS module. They thought the NuGet Package Manager shell was 64 bit, but in fact it’s a 32 bit shell as Visual Studio is itself 32 bit and will remain that way for the foreseeable future. So, if I have a dependency on some 64 bit binary module, how can I ensure that the script will get run in the right environment? Well, here’s some “stub” script that you can put at the top of your ps1 file that will detect its environment and relaunch itself in a 64 bit shell, passing along any arguments that were given.
I’d suggest keeping your 64 bit scripts in a separate ps1 and calling them from init.ps1 to do the work. Because the script will be relaunched in a 64 bit shell, you won’t have access to the package manager console intrinsics like $DTE, nor can you pass any “live” objects to the essentially external script. Stick to strings and other primitives like [int] and [bool] etc.
# am I running in 32 bit shell?
if ($pshome -like "*syswow64*") {
write-warning "Restarting script under 64 bit powershell"
# relaunch this script under 64 bit shell
# if you want powershell 2.0, add -version 2 *before* -file parameter
& (join-path ($pshome -replace "syswow64", "sysnative") powershell.exe) -file `
(join-path $psscriptroot $myinvocation.mycommand) @args
# exit 32 bit script
exit
}
# start of script for 64 bit powershell
write-warning "hello from $pshome"
write-warning "My original arguments $args"
Also available on poshcode: http://poshcode.com/3827
Disclaimer: I’ve tested this with PowerShell 3.0 only.
Updated 2012/9/24: $type.Name -> $type.FullName (otherwise only types directly in System namespace are found... oops!)
Here’s something I just knocked up recently to have a nice and simple way to import groups of functions temporarily from a .NET Type as a module. When you’re done with them, you can unload at any time using Remove-Module. As the inline help mentions, you must pipe the output of this function into Import-Module to make the functions available. You have to be somewhat familiar with the methods you’re converting in order to know what order to pass parameters. Some are obvious in that they only take one argument (like Sin, Cos or Tan) but others you’ll have to double check yourself. It would have been nice to convert .NET parameters and overloads into parameter sets, but the differences between how PowerShell and the .NET compilers resolve ambiguities can be very different and would only work for mostly simple cases. Here’s how I like to quickly check method syntax:
PS C:\projects> [math]::Log
OverloadDefinitions
-------------------
static double Log(double d)
static double Log(double a, double newBase)
Here's the function itself ( also available on poshcode (updated) )
function ConvertTo-Module {
<#
.SYNOPSIS
Quickly convert a .NET type's static methods into functions
.DESCRIPTION
Quickly convert a .NET type's static methods into functions.
This function returns a PSModuleInfo, so you should pipe its
output to Import-Module to use the exported functions.
.PARAMETER Type
The type from which to import static methods.
.INPUTS
System.String, System.Type
.OUTPUTS
PSModuleInfo
.EXAMPLE
ConvertTo-Module System.Math | Import-Module -Verbose
.EXAMPLE
[math] | ConvertTo-Module | Import-Module -Verbose
#>
[outputtype([psmoduleinfo])]
param(
[parameter(
position=0,
valuefrompipeline=$true,
mandatory=$true)]
[validatenotnull()]
[type]$Type
)
new-module {
param($type)
($exports = $type.getmethods("static,public").Name | sort -uniq) | `
% {
$func = $_
new-item "function:script:$($_)" `
-Value {
# look mom! no [scriptblock]::create!
($type.FullName -as [type])::$func.invoke($args)
}.GetNewClosure() # capture the value of $func
}
export-modulemember -function $exports
} -name $type.Name -ArgumentList $type
}
Have fun!
This is a module that takes advantage of a new hook added to PowerShell 3.0 that allows you to completely take over the readline API. To do so, you must define a function like:
function PSConsoleHostReadline {
[Console]::Readline()
}
The is an example of a very simple implementation. You may think it's enough, until you realise that there is absolutely zero line editing: The cursor keys don't work, no home/end, no tab completion, nothing! There's a lot more to do than just grabbing a line of text.
This module tries to emulate the Unix Bash/GNU Readline experience. Tab completion works by dumping out a long line of space-separated matches, and will only complete the current line up to the maximum amount of shared leading letters for all matches based on the current token.
Thankfully, you will also get Bash style tab completion for types, cmdlets, parameters and their values as the PSReadline module uses PowerShell 3.0's powerful and fast tab completion APIs.
This is a beta release, so expect a glitch or two. Currently all of the bindings are based on EMACS. The next release will let you define your own bindings. The EMACS bindings are documented below.
Have fun!

Installation
=============
- Download ZIP file, unblock with unblock-file cmdlet.
- Extract to ~\documents\windowspowershell\modules\
-- This should result in a PSReadline folder
PS> Import-Module PSReadline
Known issues:
=============
- <esc> does not clear the current line
- does not use powershell history (so get-history returns nothing)
- doesn't support fancy prompt functions with newlines and/or those
that use write-host; single line prompt function only
Credits
==============
- Miguel de Icaza (getline.cs)
Thanks to his unending masochism & for donating
the guts of the Readline emulation, which was torn kicking &
screaming from an old Mono REPL C# shell. Much massaging and
poking was needed for NT & PowerShell, but it works.
Common Bindings
=========================================
Home Cursor Home
LeftArrow Cursor Left
RightArrow Cursor Right
UpArrow History - Previous
DownArrow History - Next
Enter Done
Backspace Backspace
Delete Delete Character
Tab Tab / Tab Complete
EMACS Bindings
=========================================
Ctrl+A Home
Ctrl+E End
Ctrl+B Left
Ctrl+F Right
Ctrl+P History - Previous
Ctrl+N History - Next
Ctrl+K Kill to EOL
Ctrl+Y Yank
Ctrl+D Delete Character
Ctrl+L Refresh
Ctrl+R Reverse Search History
Alt+B Word - Backwards
Alt+F Word - Forwards
Alt+D Word - Delete
Alt+BkSpc Word - Delete Backwards
=========================================
Ctrl+Q Quote
=========================================
A question came up on an MVP mailing list about passing delegates of .NET methods to managed APIs in PowerShell. I covered callbacks to script blocks in a previous post, but I've never covered anything on passing regular .NET methods to APIs so here's a function I wrote to easily create Action, Action<> or Func<> delegates for any given static or instance method. The syntax is pipeline friendly and very flexible. As methods may be overloaded, you must provide a means to select an overload for the delegate. This is done by either providing a specific delegate type you wish to create, or by passing an array of types that should be used to find a compatible overload. The latter technique is more flexible because you only need to provide compatible parameters; the explicit delegate technique needs an exact match for the overload. Here are some examples of the syntax:
# Gets a delegate for a matching overload with string,string parameters.
# It will actually return func<string,object,string> which is the correct
# signature for invoking string.format with string,string.
$delegate = [string]::format | Get-Delegate string,string
# Gets a delegate for a matching overload with no parameters.
$delegate = [console]::beep | Get-Delegate @()
# Gets a delegate for a matching overload with @(int,int) parameters.
$delegate = [console]::beep | get-delegate int,int
# Gets a delegate for an explicit func[].
$delegate = [string]::format | Get-Delegate -Delegate 'func[string,object,string]'
# Gets a delegate for an explicit action[].
$delegate = [console]::writeline | Get-Delegate -Delegate 'action[int]'
# For a method with no overloads, we will choose the default method and
# create a corresponding action, action[] or func[].
$delegate = [string]::isnullorempty | get-delegate
# Gets a delegate to an instance method of a stringbuilder: Append(string)
$sb = new-object text.stringbuilder
$delegate = $sb.append | get-delegate string
Here is the function definition itself. It requires PowerShell 3.0 (in public beta right now) due to some new language features but in theory it could be modified to support PowerShell 2.0. It's also available on PoshCode.
#requires -version 3
function Get-Delegate {
<#
.SYNOPSIS
Create an action[] or func[] delegate for a psmethod reference.
.DESCRIPTION
Create an action[] or func[] delegate for a psmethod reference.
.PARAMETER Method
A PSMethod reference to create a delegate for. This parameter accepts pipeline input.
.PARAMETER ParameterType
An array of types to use for method overload resolution. If there are no overloaded methods
then this array will be ignored but a warning will be omitted if the desired parameters were
not compatible.
.PARAMETER DelegateType
The delegate to create for the corresponding method. Example: [string]::format | get-delegate -delegatetype func[int,string]
.INPUTS System.Management.Automation.PSMethod, System.Type[]
.EXAMPLE
$delegate = [string]::format | Get-Delegate string,string
Gets a delegate for a matching overload with string,string parameters.
It will actually return func
Here are some tests to demonstrate the various cases covered. They were pretty much essential while I was tweaking the script.
# general test function
function Assert-True {
param(
[parameter(position=0, mandatory=$true)]
[validatenotnull()]
[scriptblock]$Script,
[parameter(position=1)]
[validatenotnullorempty()]
[string]$Name = "Assert-True"
)
$eap = $ErrorActionPreference
Write-Host -NoNewline "Assert-True [ $Name ] "
try {
$erroractionpreference = "stop"
if ((& $script) -eq $true) {
write-host -ForegroundColor Green "[PASS]"
return
}
$reason = "Assert failed."
}
catch {
$reason = "Error: $_"
}
finally {
$ErrorActionPreference = $eap
}
write-host -ForegroundColor Red "[FAIL] " -NoNewline
write-host "Reason: '$reason'"
}
#
# static methods
#
assert-true {
$delegate = [string]::format | Get-Delegate -Delegate 'func[string,object,string]'
$delegate.invoke("hello, {0}", "world") -eq "hello, world"
} -name "[string]::format | get-delegate -delegate 'func[string,object,string]'"
assert-true {
$delegate = [console]::writeline | Get-Delegate -Delegate 'action[int]'
$delegate -is [action[int]]
} -name "[console]::writeline | get-delegate -delegate 'action[int]'"
assert-true {
$delegate = [string]::format | Get-Delegate string,string
$delegate.invoke("hello, {0}", "world") -eq "hello, world"
} -name "[string]::format | get-delegate string,string"
assert-true {
$delegate = [console]::beep | Get-Delegate @()
$delegate -is [action]
} -name "[console]::beep | get-delegate @()"
assert-true {
$delegate = [console]::beep | Get-Delegate -DelegateType action
$delegate -is [action]
} -name "[console]::beep | Get-Delegate -DelegateType action"
assert-true {
$delegate = [string]::IsNullOrEmpty | get-delegate
$delegate -is [func[string,bool]]
} -name "[string]::IsNullOrEmpty | get-delegate # single overload"
assert-true {
$delegate = [string]::IsNullOrEmpty | get-delegate string
$delegate -is [func[string,bool]]
} -name "[string]::IsNullOrEmpty | get-delegate string # single overload"
#
# instance methods
#
assert-true {
$sb = new-object text.stringbuilder
$delegate = $sb.Append | get-delegate string
$delegate -is [System.Func[string,System.Text.StringBuilder]]
} -name "`$sb.Append | get-delegate string"
assert-true {
$sb = new-object text.stringbuilder
$delegate = $sb.AppendFormat | get-delegate string, int, int
$delegate -is [System.Func[string,object,object,System.Text.StringBuilder]]
} -name "`$sb.AppendFormat | get-delegate string, int, int"
There are many new improvements to the language and parser in v3 (some of which I hope to cover over the next few posts) but one of my favourites is what Microsoft are calling singleton/array enumeration (or something equally obtuse.) I am hereby christening it “Property Unrolling” as it works similarly to how PowerShell does automatic collection unrolling when piping an enumerable (list, collection, array.)
This powershell 3.0 technique is where you can take a variable that contains an array (or collection, list or anything else that is enumerable) like $myarray and if you want to access a property on each element in that array, you no longer need to use foreach-object with $_.propertyName to access it. Instead, you can simply type $myarray.propertyName and powershell will return that property from each element in the array, but only if the array itself does not have that property. For example if you had an array of strings, asking for $arr.length would return the length of the array, and not the length of each string. The best way to show this is with some examples:
Array of files
# the older way (still works)
dir | foreach-object { $_.lastwritetime } | sort
# now, here's the shortcut way for v3
(dir).lastwritetime | sort
XML
Here's an example on how working with XML just got ten times easier. Here's some XML:
42
43
44
Here's a script that dumps the prop value in each element:
# the older way
$xml = [xml]" ... "
$xml.root.element | foreach-object { $_.prop }
# the v3 way ;)
$xml.root.element.prop
This is such a time saver. Thank you Microsoft!
Many businesses are averse to moving away from a restricted execution policy because they don't really understand it. As Microsoft will tell you, It's not a security boundary - it's just an extra hoop to jump through so you don't shoot yourself in the foot by running something you shouldn’t. If you want to run ps1 scripts in your own application, simply host your own Runspace and use the base authorization manager which pays no heed to system execution policy, even if it’s controlled by group policy and immune to powershell –bypass and set-executiopolicy:
Bypassing in Code
InitialSessionState initial = InitialSessionState.CreateDefault();
// Replace PSAuthorizationManager with a null manager
// which ignores execution policy
initial.AuthorizationManager = new
System.Management.Automation.AuthorizationManager("MyShellId");
// Extract psm1 from resource, save locally
// ...
// load my extracted module with my commands
initial.ImportPSModule(new[] { <path_to_psm1> });
// open runspace
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
RunspaceInvoke invoker = new RunspaceInvoke(runspace);
// execute a command from my module
Collection<PSObject> results = invoker.Invoke("my-command");
// or run a ps1 script
Collection<PSObject> results = invoker.Invoke(@"c:\program files\myapp\my.ps1");
By using this null authorization manager, execution policy is completed ignored. Remember - this is not some "hack" because execution policy is something for protecting users against themselves. It's not for protecting against malicious third parties, like some kind of script firewall. Whatever could be put in a script, could be run by hand in a dozen different ways using invoke-expression, and even file based scripts can be executed this way: invoke-expression (get-content .\foo.ps1).
Bypassing in Script
Now this is a little more hackish because it involves manipulating powershell.exe internals at runtime. This is a useful one-liner (if you can memorise it) when you find yourself in one of those clients who has GPO controlled execution policy. It’s pushing it for a one-liner, I know, but hey:
function Disable-ExecutionPolicy {
($ctx = $executioncontext.gettype().getfield(
"_context","nonpublic,instance").getvalue(
$executioncontext)).gettype().getfield(
"_authorizationManager","nonpublic,instance").setvalue(
$ctx, (new-object System.Management.Automation.AuthorizationManager
"Microsoft.PowerShell"))
}
This function will swap out the powershell host’s AuthorizationManager implementation (PSAuthorizationManager) with the null, policy-ignoring version. Execution policy will be effectively unrestricted, regardless of enterprise, machine or user level attempts to set it to restricted. This is an in-memory bypass only – when powershell.exe is closed and restarted, it’s back to business (or lack thereof.)
Have fun!
I’m seeing a few errant companies have their installers throw their modules into ${env:systemroot}\WindowsPowerShell\1.0\Modules but this is not the right place. The only things that should go there are core operating system modules from Microsoft. So, where should you install them?
How to: Install a module for all users
- Create the folder ${env:programfiles}\YourProduct\PowerShell\Modules\
- Place your module (or modules) under this folder
- Add the folder from step 1 to the system scoped environment variable PSModulePath; consider embedding %ProgramFiles% to keep the environment string as short as possible
- Profit.
How to: Install a module for the current user
- Test for, and create if necessary the folder which is the result of this call (or equivalent in managed code): join-path ([environment]::GetFolderPath("MyDocuments")) WindowsPowerShell\Modules
- Copy your module or (modules) to folder at above
- Profit.
It’s as easy as that.