14

03/12

Windows Tip: You Need a COM Shell to Use Environment Variables

21:47 by rleahy. Filed under: Technology

I was attempting to write a script at work today that would alter the default user’s registry, so that each new user created on the machine (by logging in for the first time) would have a RunOnce that would fire the first time they logged in.

The first challenge was finding out where the registry hive that each new user’s HKCU is based off is stored.  That was accomplished easily enough: It’s in C:\Users\Default\NTUser.dat.  You can mount unloaded registry hives using reg.exe load and unload them (after you’re finished modifying them) with reg.exe unload.

The next challenge was that I wanted to copy a file to this new user’s %appdata% directory, but using xcopy just wasn’t working.

The not-so-obvious problem was that when RunOnce runs something, it isn’t running within a proper COM interpreter shell, so things you’d expect to work — like expansion of “%appdata%” into something more meaningful — don’t.

The solution is to give the command a COM shell by invoking it with cmd.exe /c <command>.

Another useful tip: If you want a command window to persist after the command finishes — for debugging, for example — use cmd.exe /k <command>.

22

11/10

Making PowerShell Play Nice With .NET

09:32 by rleahy. Filed under: Technology

Before we get started, let me just say that I love PowerShell.  Any hope that Linux had of wooing me with Python/Perl/BaSH evaporated when Microsoft released PowerShell, because it can do everything you’d want a shell to do and more.

Now that that’s out of the way, let me say that sometimes—even though it’s built right on top of .NET—getting PowerShell to play nice with the Framework, and/or the things you wrote in C#, is kind of a pain in the ass.

Not a complicated, convoluted pain in the ass, like anything that has anything remotely to do with Linux, but more the “what the hell?” irritation you get when something has bad HCI.

The first pain in the ass is calling methods from the Framework.  PowerShell has this very nice and feature-complete set of cmdlets and language features that manage to wrap a lot of the things you might otherwise need the Framework for, but sometimes you just need to join two file paths together, and you’re too lazy to use string concatenation (especially when the format of the two paths you’re joining isn’t predictable, getting regex involved and…).

The solution: Your gut instinct was probably to try something like System.IO.Path.Combine(), but then PowerShell yelled at you.  Turns out that PowerShell’s designers opted for some weird bastardization of C#’s Namespace Alias Qualifier syntax.  What you have to do is type the leading part of the class/method (i.e. the namespace and class, and perhaps sub-class(es)) in square brackets, append that with two colons, and then put the name of the method.  So the example from above becomes [System.IO.Path]::Combine(), which works.

With some classes—Regex, for example—you mysteriously don’t need to include the namespace, so it’s [Regex]::Match().

Also keep in mind that while C# may be case sensitive, PowerShell is not, so you don’t need to be a capitalization Nazi even when calling the .NET Framework classes/methods directly from PowerShell.

The second pain in the ass is classes with custom indexers.  In C# this is wonderful—array-like indexing into classes wherein such indexing makes sense—but it doesn’t work quite so fluidly for PowerShell (for whatever reason), and trying to use this syntax will cause red text to appear.

A little poking around in the documentation for a few classes that have these indexers reveals that the custom indexers are actually syntactic sugar for calls to a property called Item, which takes arguments.

You can perform this type of call in PowerShell:

$myindexeditem.Item($itemindex)

for objects with more than one argument to their indexer, just send more than one argument to Item.

That’s all for now, get out there and write even sweeter PowerShell scripts, armed with this new knowledge.