# Monday, August 11, 2008

Just a handy tip - you don't have to cast enum types in powershell usually. It will do that for you if that's the only constructor (or method) overload that makes sense:

$accessrule = New-Object system.security.AccessControl.FileSystemAccessRule $userName, `
     "Modify",  "ContainerInherit,ObjectInherit", "None", "Allow"

So when I say "only overload that makes sense," what do I mean by that? Take this method for example which has two overloads (meaning it has two different sets of parameters you could use):

void MyMethod(string a, string b, string c)

void MyMethod(string a, enum b, string c)

If you try to call that method with $o.MyMethod("foo", "foo", "foo"), it will pick the first version that takes three strings. In that case, you would have to cast/convert the enum to its native type so that powershell will pick the right method.

posted on Monday, August 11, 2008 12:38:22 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Sunday, August 10, 2008

With PowerShell’s new –STA startup switch, interacting with the Windows Forms object model was never so easy:

PS> $text = & {powershell –sta {add-type –a system.windows.forms; [windows.forms.clipboard]::GetText()}}

Easy, eh?

posted on Sunday, August 10, 2008 11:36:37 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Tuesday, August 05, 2008
blocked file properties

As we all [should] know, running scripts downloaded from the Internet is a risky business. But sometimes you know exactly where they came from, and you trust the source. The problem arrives when you’re on a server without any of your familiar utilities and you’ve just downloaded a zip of several ps1 scripts. Unzipping the zip via the windows built-in zip handler in explorer will preserve the Zone.Identifier information for the extracted files. This means that even if you have your Execution Policy set to RemoteSigned (which most people seem to have – it’s a sensible balance), the now “local” scripts are treated as remote and they will not run. Ideally you should “unblock” the zip file before extracting the files; all extracted files are then also “unblocked.” Unblocking a file is as simple as right-clicking it in Explorer and choosing “Properties.” (see figure 1).

Now, sometimes you don’t have this luxury. Either someone else downloaded/extracted the files or you are logged in remotely via PowerShell Remoting/WINRM for example. Thankfully, the annoyingly talented Mark Russinovich has written a great little tool for stripping NTFS ADS (alternate data streams – where the zone indentifier information is attached to a regular file) called streams.exe. He’s also made the tool easily available via a UNC path: \\live.sysinternals.com\tools\streams.exe Usage is simple: just start in the root directory of the extracted scripts and run: streams –s –d *.ps1 ; the –s means traverse subdirectories and –d instructs it to delete any alternate data streams from the files.

posted on Tuesday, August 05, 2008 11:35:42 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Monday, July 28, 2008

The CodePlex guys do it again. This is a great feature that will surely help large projects garner a bit more help and support through the use of better developer documentation for getting contributors up to speed. The only thing that’s missing is syntax highlighting support for the PowerShell language. Vote for the feature here - Wiki Syntax Highlighting for PowerShell

You’ll need a CodePlex account for this I think. More information on the July 22nd release:

Syntax highlighting has been added to project wiki pages. See the Wiki Markup Guide for details.
Also added is mailing list support for project discussions: start or respond to a discussion from your e-mail client; get notifications of new responses as they come in, or in daily digest format. Now users can subscribe to an entire project’s discussions, including all new discussions posted to the project. See Mailing Lists Documentation for more information.
Feature requests addressed in this release:
Syntax Highlighting Extension
Feature request: Mailing lists
Issues addressed in this release:
HTTPS should be dropped
HTTPS in forums causes browser warnings
Feature request: please let me input ENTER in release description textbox
Pressing enter while in textarea submits form

posted on Monday, July 28, 2008 4:29:46 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback

In the latest PowerShellCX changeset, archive read and extract support is finally available. Sorry there are no binary builds yet. Use the Extract-Archive cmdlet as a stand-alone to extract all files. Use Read-Archive to generate ArchiveEntry objects which can be piped through Where-Object for filtering and eventual consumption by Extract-Archive (which can accept ArchiveEntry as well as FileInfo pipeline input). I’ve got progress reporting working too for extract. I’ve added support for reading/extracting SevenZip, Arj, BZip2, Cab, Chm, Cpio, Deb, GZip, Iso, Lzh, Lzma, Nsis, Rar, Rpm, Tar, Wim, Z & Zip.  Yes, you did see ISO support in that list ;-) Write support is only zip, bzip2, tar and gzip still.

There are more features coming, including encrypted archive support.

posted on Monday, July 28, 2008 5:28:00 AM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Wednesday, July 09, 2008

I just hacked this one up a few minutes ago to let me get an Oracle, Access, Excel/CSV or SQL Server database connection quickly without fiddling with provider strings and other things that just fall out of my head as fast as they get put in. It uses an old trick of creating a zero-length file with an UDL extension and then executing it. The function returns an operable OleDbConnection object, albeit a closed one unless you specifiy –Open as a switch.

  1. function get-oledbconnection ([switch]$Open) {  
  2.     $null | set-content ($udl = "$([io.path]::GetTempPath())\temp.udl");  
  3.     $psi = new-object Diagnostics.ProcessStartInfo  
  4.     $psi.CreateNoWindow = $true 
  5.     $psi.UseShellExecute = $true 
  6.     $psi.FileName = $udl 
  7.     $pi = [System.Diagnostics.Process]::Start($psi)  
  8.     $pi.WaitForExit()  
  9.     write-host (gc $udl)  
  10.     if (gc $udl) {  
  11.         $conn = new-object data.oledb.oledbconnection (gc $udl)[2]  
  12.         if ($Open) { $conn.Open() }  
  13.     }  
  14.     $conn 
  15. }  

You’ll notice I said OleDbConnection. This means you should pick an OLEDB provider on the provider page for best (read: working) results. Cancelling returns nothing.

image

Ah, how I love the little blue box of typographical terrific-ness.

posted on Wednesday, July 09, 2008 8:18:44 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Tuesday, July 01, 2008

Why do such a thing? Well, if you’ve created your own aliases for commands and you try to give someone your ps1 script file, it will not run because they have not got the same aliases defined as you. Also, scripts that use fully-resolved names like “Get-ChildItem” are more readable for a newcomer to PowerShell than one that is using the unix-like “ls" alias for example. "Get-ChildItem" leads quite directly to the MSDN documentation, but "ls" might lead you anywhere. Before you publish a script online somewhere for the world to use, it’s important that you try to remove any aliases and replace them with the native command names.

Doing this kind of thing has been talked about before but it was always a very difficult thing to do with PowerShell v1.0, what with the lack of BNF documentation describing the grammar etc. Thankfully, it's a lot easier to do with PowerShell v2.0 (currently at release CTP2) because the team has exposed the Tokenizer for use in scripts. There's been suprisingly little use of it so far, so I figured I'd start the ball rolling with a series of articles based around it. So, let's look at an example script that uses aliases and put it through the meat grinder:

image

As you can see, it spits out the expanded script to the output stream. The informational messages are written to the host, so they won't interfere if you redirect the output to a file like: .\resolve-aliases.ps1 in.ps1 > out.ps1

Here's the Resolve-Aliases.ps1 script itself:

  1. #requires -version 2  
  2. param($filename = $(throw "need filename!"))  
  3.  
  4. $lines = $null 
  5. $path = Resolve-Path $filename -ErrorAction 0  
  6.  
  7. if ($path) {  
  8.     $lines = Get-Content $path.path  
  9. } else {  
  10.     Write-Warning "Could not find $filename" 
  11.     exit  
  12. }  
  13.  
  14. # Initialize  
  15. $parser = [system.management.automation.psparser]  
  16. $errors = new-object system.management.automation.psparseerror[] 0  
  17.  
  18. do {  
  19.     $tokens = $parser::tokenize($lines, [ref]$errors)     
  20.     $retokenize = $false 
  21.       
  22.     if ($errors.count -gt 0) {  
  23.         Write-Warning "$($errors.count) error(s) found in script." 
  24.         $errors 
  25.         exit  
  26.     }  
  27.  
  28.     # look through tokens for commands  
  29.     $tokens | % {  
  30.         if ($_.Type -eq "Command") {  
  31.             $name = $_.Content  
  32.               
  33.             # is it an alias?  
  34.             # we use -literal here so '?' isn't treated as wildcard  
  35.             if ((!($name -eq ".")) -and (Test-Path -LiteralPath alias:$name)) {  
  36.                   
  37.                 # gcm may return more than one match, so specify "alias"  
  38.                 # filtering against name kludges the '?' alias/wildcard   
  39.                 $command = gcm -CommandType alias $name | ? { $_.name -eq $name }  
  40.                               
  41.                 # resolve alias which may lead to another alias  
  42.                 # so loop until we reach a non-alias  
  43.                 do {  
  44.                     $command = Get-Command $command.definition  
  45.                 } while ($command.CommandType -eq "Alias")  
  46.                   
  47.                 Write-Host -NoNewline "Resolved " 
  48.                 Write-Host -NoNewline -ForegroundColor yellow $name 
  49.                 write-host -nonewline " to "   
  50.                 write-host -ForegroundColor green $command.name           
  51.                   
  52.                 # Use a stringbuilder to replace the alias in the line  
  53.                 # pointed to in the Token object. StringBuilder has a much  
  54.                 # more precise Replace method than String. This allows us to  
  55.                 # replace the token with 100% confidence.  
  56.                 $sb = New-Object text.stringbuilder $lines[$_.startline - 1]  
  57.                 $sb = $sb.replace($name, $command.Name, $_.startcolumn - 1, $_.length)  
  58.                 $lines[$_.startline - 1] = $sb.tostring()  
  59.                   
  60.                 # now that we've replaced a token, the script needs to be reparsed  
  61.                 # as offsets have changed on this line.   
  62.                 $retokenize = $true 
  63.                   
  64.                 # break out of pipeline, (not 'do' loop)  
  65.                 continue;  
  66.             }  
  67.         }  
  68.     }  
  69. } while ($retokenize)  
  70.  
  71. Write-Host "" # blank line  
  72.  
  73. # output our modified script  
  74. $lines 

Of course, this requires PowerShell v2.0 CTP2. Next in the series, I'll give you a script to check your ps1 scripts for backwards compatibility against PowerShell 1.0. That should be handy for those naughty admins out there who despite all the warnings have installed v2 in production. ;-)

posted on Tuesday, July 01, 2008 6:43:07 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2] Trackback
# Friday, June 27, 2008

Someone on a private mailing list I'm on lamented the problem with powershell's '>' redirection operator defaulting inflexibly to use unicode for encoding the output file. This is not very compatible for NT's ancient console subsystem which works best with ASCII data. Fortunately, there's an easy workaround to fix this:

Due to the magic of command discovery and the fact that > really does use out-file, you can "fix" this by placing the following in your $profile:

function out-file($FilePath, $Encoding, [switch]$Append) {
$input | microsoft.powershell.utility\out-file $filepath -encoding ascii `
     -append:$append
}


From now on, > will be forced to use ASCII encoding. This works because functions have higher precedence than built-in commands in powershell's command discovery search.

UPDATE: Rather annoyingly, I'm informed that this particular workaround doesn't work on v1.0 of PowerShell. I tested the above on v2.0CTP2 only. Doh.

posted on Friday, June 27, 2008 12:03:07 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1] Trackback
# Wednesday, June 25, 2008

Just another quick-fix post for any readers’ benefit. I have been using MOSS on Windows 2008 Server on VMWare for a while now and the display has always been sluggish and choppy even though VMWare tools is up-to-date and installed. I decided to take a quick peek at the display properties to see if perhaps hardware acceleration is off or something like that and I noticed that the display driver was “Standard VGA Display.” I thought to myself, “Shouldn’t that be a VMWare display driver?” so I clicked properties and drilled down to the “Update Driver…” dialog. Clickety-click and hey presto, it finds a newer driver, namely “VMWare SVGA II” and installs it. Display is now much better. On other guest OS’s like Win 2003 etc, VMWare tools installation updated the driver for you, but this time it didn’t. Not sure why.

One remaining problem I have is that the mouse is dodgy and sometimes the host mouse pointer gets de-synced with the guest’s. Anyone got that problem?  Fixed by starting device manager and going through nearly the same drill as the display driver. I manually chose “VMWare Pointing Device” and rebooted, replacing the default ps/2 mouse driver.

image

Update:

After I rebooted, it was still a little sluggish. Then I remembered that by default, hardware acceleration is only at one notch up. So, push it up to full!

posted on Wednesday, June 25, 2008 2:18:30 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback