Saturday, September 15, 2007

PowerShell Vulnerability

Some time ago I put together a template for a file that can be interpreted as both a batch command script for cmd.exe and a SQL script for SQL Server Management Studio and sqlcmd.exe. It works really well for enabling someone unfamiliar with SQL Server to deploy database update scripts.

I've been working with PowerShell quite a lot lately and I was wondering if it would be possible to have a file that would be interpreted by both cmd.exe and PowerShell.exe. It turns out that Jay Bazuzi, a developer at Microsoft, has already found an elegant solution. However, upon inspecting the code I became concerned how it would be effected by PowerShell's default setting to disable all scripts and especially disable unsigned scripts from remote sources.

I have since worked with Jay's sample on my home machine and discovered that it effectively bypasses PowerShell's anti-script security by piping the commands to it's interactive mode. As a result, it was relatively trivial to write a batch command script that would enable PowerShell to run all future scripts and modify the user's PowerShell profile to ensure it stays that way.

Thankfully, security in the OS and other applications is making it harder to get a command script on to another computer and get the user to run it but with the capabilities of PowerShell potentially available for malicious use it's just one more reason to not run as an administrator and minimise the damage.

 Friday, September 14, 2007

The Theory Of Thirds

It can be expected that for any blog about a difficulty experienced by a user of  Windows that the third comment to be posted will recommend the Linux or Mac OS as the solution to the problem. The first two comments preceding the third usually add actual value for other readers of the blog.

The theory is not restricted to just Windows and could equally be applied to a language (eg VB), a concept (eg Agile), a product (eg iPod), or any topic with distinct followers and opposers. Obviously, in these cases the suggested solution corresponds to the exchanged topic. For example, an article about VB's failings will be responded to with "You should use C# instead".

 Wednesday, September 12, 2007

PowerShell Regular Expression Compiler

As an exercise in the mostly useless, I decided it would be interesting to try my hand at writing a PowerShell script to compile a regular expression pattern to a .NET assembly. I sure someone has already created a NAnt or MSBuild task to do this but I felt this would be a good way to increase my familiarity with PowerShell.

The script requires the RegEx pattern and an output type name and full namespace to work. You can optionally pass an AssemblyName but if omitted the type and namespace will be used to form the output file name. You can also specify the -ignoreCase or -multiLine switches to enable that behaviour on your expression. There are other options that probably should be supported but they can be easily added if you actually have a use for this. Without further delay, here is the code:

param
(
    [string] $pattern = "",
    [string] $typeName = "",
    [string] $fullNamespace = "",
    [System.Reflection.AssemblyName] $assemblyName = $null,
    [switch] $ignoreCase,
    [switch] $multiLine
)

if ($pattern -eq "") { throw ("-pattern required"); }
if ($typeName -eq "") { throw ("-typeName required"); }
if ($fullNamespace -eq "") { throw ("-fullNamespace required"); }

if ($assemblyName -eq $null)
{
    $assemblyName = New-Object System.Reflection.AssemblyName `
        ($fullNamespace + "." + $typeName);
    $assemblyName.Version = New-Object System.Version (1, 0);
}

$sysRx = @{};
$sysRx.Namespace = "System.Text.RegularExpressions";
$sysRx.RegEx = [System.Text.RegularExpressions.RegEx];
$sysRx.RegExOptions = [System.Text.RegularExpressions.RegExOptions];

$options = $sysRx.RegExOptions::None;
if ($ignoreCase) { $options = $options -bor $sysRx.RegExOptions::IgnoreCase; }
if ($multiLine) { $options = $options -bor $sysRx.RegExOptions::Multiline; }

$info = New-Object ($sysRx.Namespace+".RegexCompilationInfo") `
    ($pattern, $options, $typeName, $fullNamespace, $true);

$popDir = [System.Environment]::CurrentDirectory;
[System.Environment]::CurrentDirectory = $PWD;
$sysRx.RegEx::CompileToAssembly($info, $assemblyName);
[System.Environment]::CurrentDirectory = $popDir;

Here is an example usage:

./Compile-RegEx.ps1 "\s+" "Spaces" "CodeAssassin.RegEx" -ignoreCase

 Monday, September 10, 2007

Another DVD to iPod

I have another weekend driving holiday planned in the next few weeks and my fiancee has requested Family Guy episodes on the iPod for the trip. Now that I know how to do it, I figure I better get started because there are a lot of episodes.

This time I decided to try Vrata's suggestion of DVDFab instead of the Videora software I used last time. What follows is a very quick summary of my experience with DVDFab.

Benefits:

  • Does not require separate DVD ripping software.
  • Supports optionally embedding subtitles into the video stream.
  • GUI is very clean and easy to follow.
  • Hasn't crashed yet on Vista x64.

Disappointments:

  • Still insists on running as Administrator.
  • The Platinum version for iPod conversion costs about US$50.

In all other ways DVDFab is similar to Videora. Similar conversion times, same resulting file sizes. Support for multiple devices' resolutions and bit rates.

If you think you'll be converting quite a lot of DVDs to an iPod compatible format, DVDFab is probably worth the price and you can try the fully-functional 30-day trial yourself to be sure.

 Sunday, September 09, 2007

Refactor By Region

The nature of the software development profession is such that you need to be constantly learning new techniques and improving your style. A side-effect of always seeking to be better is that when you revisit old code it suddenly doesn't seem as good as it did when you first wrote it.

I was revisiting a large class recently and was struggling to navigate between all the methods to follow the logic. I decided to make use of #region blocks (something I rarely do) to help group related methods together and hide less relevant code from view.

By arranging groups of methods together by their purpose, a pattern started to emerge. Each region started to look like an ideal candidate to refactor the code into a new class with a single responsibility. I already had a set of unit tests in place for the existing code so I felt confident in being able to refactor and being able to verify I hadn't broken anything afterward.

What was originally one class became eight, resulting in greater maintainability, testability, and of course readability, solving my original problem. For this class the refactoring by region worked well because I had grouped code by it's behaviour and partly by it's call chain.

However, I've seen other approaches to using #region blocks. I've seen all event handlers put in their own region. I've seen collections of overloaded methods grouped by name. I've even seen properties grouped together, method/subs group together, and functions grouped together. In these arrangements not only does a refactoring pattern not automatically emerge but it becomes harder to search for one.

As I mentioned above, I'm not a big user of #regions. This is probably because I try to refactor my code before it gets big enough and because of the region anti-patterns I've wrestled with. I think I might have to give them a second chance.

 Thursday, September 06, 2007

My Manwich! PowerShell Which

One feature I've loved during my brief ventures into Linux-land is the which command, not to be confused with other tasty items. For those not familiar with the command, it enables you to determine the full path to an executable in your environment path that would be executed if only the name of the executable is entered at the command prompt.

This is useful in situations where you have different versions of the same executable on your file system (perhaps even a malicious version) or if you simply want to know where the executable resides. I have often wanted to perform a similar search on my Windows PCs but to the best of my knowledge, the standard command prompt does not provide an easy way to do this.

Gratefully, as is becoming the pattern lately, PowerShell makes this very simple. Simple to the point that I don't really need to bundle it into a function. For example, here is how I locate the SQL Server Management Studio executable that runs when I use Start, Run, "sqlwb":

($Env:Path).Split(";") | Get-ChildItem -filter sqlwb*

If you don't have SQL installed, you can switch the -filter parameter for "calc*" or similar. You can see that it isn't limited to just executable files on your path either and while it doesn't duplicate the default behaviour of the Linux which command, wrapping the one-liner into a function with some extra switches would get it very close.

 Wednesday, September 05, 2007

Rhino.Blocked

I tried using Rhino.Mocks in production test code for the first time the other day. I downloaded the latest version from Oren Eini's website, unzipped it into source control, and added a reference to my project.

I wrote a quick test utilising the interface mocking features and hit Ctrl+Alt+X to run the test. SecurityException! The message, which I've since forgotten, was complaining that the referenced assembly (ie Rhino.Mocks.dll) could not be loaded.

A quick Google provided the solution, you need to ensure you've unblocked any files you have downloaded from the Internet or a network machine. Now I'm happily mocking away.

 Tuesday, September 04, 2007

Debug.Assert and Team Build

Today I was setting up a nightly build for a new project. After creating the Team Build Type and writing the script to trigger the build on the build machine, I ran the script to ensure it would work. As usual Team Build retrieved the sources, compiled the projects and began running the tests. Half an hour passed and the tests were still running.

I knew the tests couldn't take that long so I stopped the build and opened the solution in Visual Studio to run the tests locally. One of the tests managed to create just the right combination of values in a project class to violate what should otherwise be a class invariant. This particular invariant was verified by a call to Debug.Assert inside the production code.

It has been a very long time since I have written a Debug.Assert statement with an expression that failed the assertion. Today I was reminded that it does not simply throw some kind of AssertException when the expression evaluates to False. Instead a rather hefty dialog box is presented to the user with a full stack trace of the code that failed and a choice of options to proceed.

The problem is that tests running under the service of the build server don't have a visible window station to display the dialog box and there is nothing to automatically click the buttons so the build just hangs indefinitely.

For now I'm just replacing any Debug.Assert statements I find with exception throwing or additional unit tests as appropriate for each case. It may be possible to modify the app.config file to use a different listener to redirect the Debug.Assert output to something non-blocking but that is going to require some investigation.

 Monday, September 03, 2007

PowerCleaning, Part 2

Each day I live with wanting to make full use of excess storage space to keep a record of every file that has ever existed in all it's versions and the conflicting need to have a neat and tidy file system without any junk sitting around.

Recently, the desire to have a ClickOnce deployment folder with only the latest version in it has won. With only the most recently published version within, the folder is a much more manageable size for distributing to other deployment servers.

However, it can be tedious to ensure only the appropriate files are deleted when cleaning a deployment folder manually, and automating the process is infinitely better. I have put together a basic PowerShell function to handle the situation.

function Clean-ClickOnceApplication ([string] $Path = (Get-Location), [switch] $WhatIf = $false)
{
    ($current, $oldList) = Get-ChildItem -path (Join-Path $Path "*") -include "*.application" `
        | ? {$_.Name -match "_\d+_\d+_\d+_\d+\." } | sort LastWriteTime -desc;
    foreach ($old in $oldList) {
        $oldFolder = $old.Name.Substring(0, $old.Name.Length - $old.Extension.Length);
        Remove-Item (Join-Path $Path $oldFolder) -recurse -WhatIf:$WhatIf;
        Remove-Item $old -WhatIf:$WhatIf;
    }
}

Simply pass the ClickOnce deployment folder as the -Path parameter (or omit to assume the current folder) and optionally enable the -WhatIf switch to test which files and folders would have been deleted.

The script currently uses the time stamps of the .application files rather than the version numbers but unless someone has been messing with the digitally-signed files this shouldn't be a problem. If it really needs to be handled by version number, I have some ideas for handling that.

 Monday, August 27, 2007

Convert DVD to iPod

I have just spend two weeks on holiday and before I left I wanted to transfer some of my DVDs to my iPod Video to keep my fiancee and I entertained whilst traveling to our destination. Unfortunately I didn't really think about this soon enough and didn't have time to get it organised. Now that I'm back home though I've been able to look into it further.

The biggest problem I found with converting DVDs to an iPod suitable format is that the movie studios don't seem to want to allow it. This results in either a complicated list of steps involving multiple tricky applications or purchasing an all-on-one solution from a questionable overseas company. Luckily though, Hanselman recently updated his Tools List for Windows, which included a link to Videora Converters, a suite of video transcoding programs available for free. If Hanselman recommends it and I don't have to provide payment details, it's worth a try.

I downloaded their iPod converter and started following their slightly outdated guide for converting DVDs. I grabbed my Futurama box set and put the first disc in. The first stage involves using the getting-harder-to-find DVD Decrypter to rip and decrypt the appropriate video and audio stream without all the extra DVD menu junk. Thankfully I'm familiar with DVD Decrypter and even more so that it works fine as non-admin in Vista 64-bit.

The second stage involves installing and running the Videora software, selecting the file that DVD Decrypter produced and waiting for it to convert the MPEG-2 data to Apple's particular MPEG-4 format. This took less than 10 minutes on my Core 2 Duo to convert a 22-minute animated episode into a 137MB MP4 file. When complete, the video played well in the PC QuickTime player and after using iTunes to transfer it to my iPod it played great there too.

Sadly, while the Videora software is free and written in .NET too, it suffers from two major problems. Firstly, it is so filled with advertising in the program itself that it's hard to see where to start and I began to wonder if I'd just voluntarily installed malware. Secondly, the software is designed to run as an Administrator, trying to write configuration data to Program Files, and under Vista, even as an admin, it crashes hard on exit.

Ultimately, if you want some favourite DVD movies or episodes on your iPod, this is probably the easiest way.

 Wednesday, August 22, 2007

Faster User Switching

I run several Vista machines each with several user accounts. Sometimes the different accounts are for different people, sometimes they are for different purposes with different permissions (ie development vs testing). Windows XP introduced a featured called "Fast User Switching" that allowed you to log into another user account without logging off the current session and Vista improves on this by allowing it in domain environments too.

But it's not enough. I want it to be faster. At the moment, in Vista, it takes far too many key presses or mouse clicks to switch to another user. This is the quickest method I've discovered so far:

Ctrl+Alt+Del, Alt+W, select account, enter password, Enter.

However, I recently discovered a much faster approach. I bought a new laptop with Vista and a built-in Vista-driver-compatible fingerprint reader. Now I've associated different fingers with different accounts and at any time I can just slide the appropriate finger over the reader and *bam*, I'm logged into another account.

Still, if anyone knows, a better keyboard shortcut would be great for the PCs without fingerprint readers.