Weird Terminating Error Behavior

I’m writing a dirty hack PowerShell provider so that I can have a reasonable base of knowledge before approaching something more work-oriented.  One component of the path the provider accepts must be an integer, so I decided to throw a terminating error if the path does not follow this rule. I also decided I’d do this so I could examine how terminating errors are handled. Something that mystifies me is the terminating error isn’t behaving as I understand it.  Here’s the code I use to throw the error:

var record = new ErrorRecord(new ArgumentException("path"), "InvalidItemIndex",
ErrorCategory.InvalidArgument, null);
var details = new ErrorDetails("The second element of a text file path must be an integer.");
record.ErrorDetails = details;
ThrowTerminatingError(record);

I expect that I’d see the information in the ErrorDetails property in some way, based on how the documentation is written; instead it seems like this information gets lost somewhere:

PS C:\Documents and Settings\myUsername\My Documents> get-item text:\Section1\item1
Get-Item : The second component of the path must be an integer.
At line:1 char:9
+ get-item  <<<< text:\Section1\item1
PS C:\Documents and Settings\myUsername\My Documents> $error[0].ErrorDetails -eq$null
True
PS C:\Documents and Settings\myUsername\My Documents>

Maybe it’s fixed in PowerShell v.2.0, or maybe I’m doing something wrong, but this is definitely not what I expected.

Writing Objects from PowerShell Scripts

I’m going to continue writing about PowerShell because it’s fun to talk about and fun to use.

Today I decided to try and analyze some data in text files using a PowerShell script. The files were simulations of a betting strategy in a coin-toss scenario. The first line represented the final balance and every line thereafter represented the balance after a bet was completed. I was interested in the average balance over time and the average minimum balance for all files. The script itself was pretty uninteresting until I got to the output:

$files = get-childitem *.txt$sum = 0
$count = 0$minSum = 0

foreach ($file in$files)
{
Write-Verbose "Processing file: $file"$sum += [long](get-content $file -totalCount 1)$count += 1

$values = get-content$file
$minimum = 0 foreach ($line in $values) {$value = [int]$line$minimum = [Math]::Min($value,$minimum)
}

$minSum +=$minimum
}

$average =$sum / $count$averageMinimum = $minSum /$count

Now is when it gets fun. I originally just wrote the output using write-output:

write-output "Average: $average" write-output "MinAverage:$minAverage"

This didn’t seem very PowerShell-like. I wanted to output an object that could be used by other scripts (for the heck of it at least.) I knew how to do this from a cmdlet, but had to search to find how to do it from a script. There seems to be two ways to make objects or object-like things to write.

Associative Arrays

Perl hackers love their hashes, and with good reason: this little data structure is very powerful. In the .NET world, we call them "dictionaries" for some reason. PowerShell associative arrays are declared with this syntax:

$array = @{name = value; name2 = value2} If I wrote one of these using write-object, I got a nice list format with headers and could access the individual values via property syntax. Joy! Still, this isn’t really what I was looking for because I was really fishing for a way to output a PSObject. PSObjects You can create a PSObject using New-Object, but at first I couldn’t figure out how to let this object know it should have the properties I wanted. This is something you can use Add-Member for. You have to specify the type of member you are adding, and I believe the valid member types are listed in the PSMemberTypes enumeration. In this case, I needed NoteProperty since there’s no base object: $output = New-Object PSObject
$output | add-member NoteProperty -name Count -value$count
$output | add-member NoteProperty -name Average -value$average
$output | add-member NoteProperty -name AverageMinimum -value$averageMinimum
Set-Variable -name home -value $myDocs -force Apparently, you can also use flags enumerations. Setting$home in PowerShell

Lately I’ve been getting acquainted with Windows PowerShell, which is something Windows has needed for a long time.  Finally, there’s a real shell for Windows that supports scripting in a format other than horrific batch files.

At work, for whatever reason the powers that be have set our %HOMEDRIVE% and %HOMEPATH% environment variables to a networked drive.  This would be a good idea, but the problem is this drive has a pretty low quota applied and I managed to fill it up within a week or so.  Basically, people use it as a convenient public share directory.  I mention this because changing the environment variables in this way has caused me much grief.  cmd.exe defaults to %HOMEDRIVE%\%HOMEPATH% when it starts, so every time I open a command prompt I end up in this stupid useless directory.  PowerShell is not immune to this either.

I noticed that my PowerShell always started here and that the $home variable seemed set to it as well. I set up a profile to change my location to the correct place, but had trouble setting the value of the$home variable since it is read-only (or constant, according to the error message).  Luckily, there’s a way to tell PowerShell, "I know what I’m doing." and the Set-Variable cmdlet takes a -force parameter.  So in the end, my script ended up looking something like this:

Set-Location "C:\Documents and Settings\username\My Documents"
Set-Variable -name home -value "C:\Documents and Settings\username\My Documents" -force

I could probably make this more OS-agnostic by using the .NET Environment class, but that’s a project for another day.

Why doesn’t Subversion add respect svn:ignore?

I use Subversion for source control on my personal projects, and I’ve got to say it’s so easy to set up and use there’s no excuse for not using source control.  There’s one thing that really bothers me though.

When setting up a .NET project, I have a few steps I have to follow due to the small clashes with the default project templates I have.  I have to rearrange a few files after VS creates the project, then I get to adding files.  One of the first things I do is set up svn:ignore on each directory; I want it to ignore *.suo, *.user, bin, obj, and occasionally a few other files/directories.  Every time I do this, I double-check with svn status to make sure that nothing I don’t want is showing up, then I feel like it’s safe to do svn add directory.  I mean, I told svn I’d like to ignore those files, right? so it should ignore them, right?  Wrong.

So, for every project, I have to either add every file then use "svn delete –force" to remove just the files that I told it to ignore in the first place, or I have to manually add every file and directory in my project, being careful not to add anything that I’m ignoring.  This is frustrating, to say the least.  At first, I thought it was because the directory wasn’t yet under source control or the svn:ignore setting hadn’t been committed, but I’ve tried committing all directories and property changes first to no avail.

The part of it that’s most aggravating is the fact that "svn add" has a –no-ignore argument; what on Earth is it there for if the command already disregards svn:ignore?  I hope I’m just missing something stupid; I’m going dig around the internet and see what I can find.

*update* According to a blog I found, it has to do with the OS doing wildcard expansion, not Subversion.  That actually makes a lot of sense, because the OS has no idea about svn:ignore.  Apparently the default add behavior is to act as if –noignore is specified (which seems dumb to me), so when you pass it a list of files it just adds all of them.  The solution?  Set up the ignore traits on the directory, then use "svn add –force ."  I have no idea why the –force is required.  It looks like this got added to the SVN book very recently.

I had need of returning a bindable collection from one of my classes, but didn’t want to allow the caller to modify the collection.  This generally requires a straightforward pattern in .NET: make a SomethingCollection class that derives from Collection<T> and a ReadOnlySomethingCollection class that derives from ReadOnlyCollection<T>, then only return the read-only version.  Most examples don’t bother with declaring the derived types, but I feel like it makes everything look better and the framework classes seem to favor declaring their own collection types as well.

I was having one heck of a time getting everything to work. I tried the most basic implementation:

public class ReadOnlySomethingCollection : ReadOnlyObservableCollection
{
}

This failed miserably with a fairly archaic error message:

‘full typename’ does not contain a constructor that takes ‘0’ arguments

I struggled with this for a while. The error message was clearly correct; if you walk the inheritance tree there’s no base class that defines a parameterless constructor. Still, defining a constructor that matched the signature of the 1-parameter constructor didn’t seem to be solving the problem. I decided it’d be best to step outside my project and make a simple example project in preparation for asking forums and newsgroups what was going on. It turns out switching languages to VB .NET was a good idea, too, because its error message was much more helpful once I had the constructor in place:

First statement of this ‘Sub New’ must be a call to ‘MyBase.New’ or ‘MyClass.New’ becasue base class ‘…’ does not have an accessible ‘Sub New’ that can be called with no arguments.

I saw this, thought about it for a minute, then had a forehead-slap moment. When a class derives from another, it’s almost as if you have two objects glued together. The base class has to be initialized before the derived class can be initialized, because the derived class’s constructor must be able to assume its base members are in a good state. So, in my case, the compiler was trying to initialize a ReadOnlyObservableCollection<T>, but since there was no parameterless constructor it couldn’t. The solution was to do exactly what VB said: make sure I call some base-class constructor as my first action. I finally got it to work in C#:

public class ReadOnlySomethingCollection : ReadOnlyObservableCollection
{
}