2007-12-21

PowerShell Associative Arrays and Anagrams

Jon Bentley's Programming Pearls describes the following pipeline for finding anagrams from a list of words: generate a signature for the word, then group together all words with the same signature. The signature is just a sorted list of all letters in a word. For instance, dame, edam, made and mead all have the same signature adem.

Associative Arrays

To implement an anagram-finder in PowerShell, let's use an associative array and we store the signatures in the keys and each word that has the same signature is stored in an array related to that key. Below is a concrete example of what we plan to do:

> $a = @{adem:("dame","edam","made","mead")}
> $a
Name                           Value
----                           -----
adem                           {dame, edam, made, mead}

Notes

  • The key for an associative array does not have to be quoted if it is a string without a whitespace.
  • Oddly, arrays in the value column are printed delimited by braces instead of parentheses.

Generating Word Signatures

A word signature is just a string with the letters in the original word sorted. We split a string into a char[] type, sort it, then make it into a string again:

> $sig = [string]("edam".ToCharArray() | sort-object)
> $sig
a d e m
> $sig.length
7

Note that the signature sig is a string with a space between each character. We can prettify the signature but it doesn't hurt because all the signatures will have the same format.

Find Anagrams in a Word List

Now that we can create a signature, we can find all anagrams in a word list by assigning each word's signature as a key in the associative array's value and adding the word to that key's array:

> get-content <test.txt> | foreach-object { $h = @{} } { $t = $_.clone(); $sig = [string]($t.ToCharArray() | sort-object); if (!$h.containsKey($sig)) { $h[$sig] = @() } $h[$sig] += $t } { $h }

Name                           Value
----                           -----
adem                           {dame, edam, made, mead}

Let's decompose this longish statement to understand what it is doing:

get-content <test.txt> |
Send every line in the input file into a pipeline.
foreach-object
Apply some operation on each object in the pipeline.
{ $h = @{} }
Initialize the associative array h in the begin script block.
{ process block }
Here is where words with the same signature are grouped together in the associative array.
$t = $_.clone();
Copy the input word from the current pipeline object before it is overwritten in the next statement.
$sig = [string]($t.ToCharArray() | sort-object);
Get the signature of the input word.
if (!$h.containsKey($sig)) { $h[$sig] = @() }
Create an array of anagrams if the signature does not already exist.
$h[$sig] += $t
Add the current word to the array of anagrams.
{ $h }
Output the associative array h in the end script block.

Conclusion

This article presented an imperative approach to grouping data with the same property using a loop and an associative array. You could apply the same style to any programming language and get the same result. In a future article, I will explore how to use a more streamlined approach to solve similar problems.

2007-12-19

Code Noise Ratio

Using Scott Hickey's article on reducing code noise in Groovy as a starting point, can we measure of the noisiness of a programming language and environment? Let's say that the quietest code where the developer has to add the least amount boilerplate code to implement a particular feature. Just to keep the metric simple, let's just measure the size of the source files for different implementations of the same feature and assume that the developer is trying to write sensible code. We assume that the shortest version is the quietest and calculate the ratio between the shortest version and all other versions.

Let's test this ratio on several versions of Hello World implemented using different scripting languages in previous articles. What is the noisiness of each implementation?

VersionPlatformSizeRatio
Groovy + SwingBuilderJava2400%
JythonJava2535.42%
GroovyJava27012.50%
IronPython.Net47391.25%
PowerShell.Net579141.25%

What does this table tell us about reducing code noise for small programs?

  • Script environment should pre-import common classes. Groovy + SwingBuilder and plain Groovy makes it very easy to write a small GUI program because all the Java Swing references are pre-imported. The Jython and IronPython implementations are nearly the same but the IronPython version is longer because it has to load .Net references.
  • Use class aliases. One reason the PowerShell version is very long is you can't add a class name into the current namespace (such as Python's from <library> import <class> or C# using namespace <blah>). You can define class aliases like this: $Form = [System.Windows.Forms.Form] but that only starts reducing noise when you use that class more than once.
  • Define properties in constructors. Another reason the PowerShell version is long is that only the .Net constructors for GUI controls are available through the platform interface, so to define a control, you have to write a sequence of statements starting with creating a new object followed by some SetX() methods. I wonder if PowerShell adaptors can overload constructors?

Find Longest (or Shortest) Line in a File

Quick scripts to find the longest line in a file. To find the shortest line, just invert the appropriate test.

Gawk

gawk "{ if (length > max) { max = length; m = $0 } }  END { print m }" <file>

Gawk + Sort + Tail

gawk "{ printf("""%4.0f %s\n""", length,$0) }" <file> | sort | tail -1

In Windows Cmd, three double-quotes are required to escape a double-quote, and the %4.0f format control ensures that lines up to 9999 characters long are sorted correctly.

PowerShell

get-content <file> | sort-object -property length | select-object -last 1

WC

wc -L gives the length of the longest line, but not the line itself.

2008-04-20: Consolidated all samples into this article.

2007-12-17

PowerShell String and Char[] Sort and Conversion

Introduction

I wanted to sort the characters in a string to use as a signature for that string. A string is basically an array of characters but System.String class does not have a Sort() method. Hmm, looks like we have to break the process into the following steps:

  1. Convert a string to a character array.
  2. Sort the character array.
  3. Convert the character array back into a string.

First cut

A hitch: System.String can only be converted into a char[], so we have to use an external sorter such as Sort-Object cmdlet. If we could have made an Array of char, then we could have used the Array's Sort() method. The first cut looks like this:

> $s = "mad"
> $sig = sort-object -inputObject $s.ToCharArray()
> $sig
m
a
d

Eh? Why isn't sig sorted? Either Sort-Object or PowerShell doesn't do the expected when presented with an array using the -inputObject parameter. Let's test this:

> "m","a","d" | sort-object
a
d
m
> sort-object -inputObject "m","a","d"
m
a
d

Second Cut

Because of the strange behaviour found in the previous section, we call Sort-Object in a pipeline. In addition, we reconstruct the output into a string:

> $s = "mad"
> $sig = (string)($s.ToCharArray() | sort-object)
> $sig
a d m
> $sig.length
5

What's wrong now? Why does sig have a whitespace between each character? This was getting a bit deep into PowerShell for me at the moment, so let's use an appropriate constructor in System.String such as String(char[]).

21-Dec-2007: Solution is to change OFS (Output Field Separator) to an empty string, like this: $OFS = "".

Third Cut

We try the System.String(char[]) constructor and make the sorted array output into a string:

> $s = "mad"
> $sig = new-object String(($s.ToCharArray() | sort-object))
New-Object : Exception calling ".ctor" with "3" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: startIndex"
At line:1 char:16
+ $t = new-object  <<<< String($s.ToCharArray())

What gives? The error indicates that a different constructor, String(char[], startIndex, length) should be used. It's not clear why the first constructor is not available.

Fourth Cut

Finally, taking into account all that we learnt above, we end up with the following statement which gives us the result we wanted:

> $s = "mad"
> $sig = new-object String(($s.ToCharArray() | sort-object), 0, $s.length)
> $sig
adm

Conclusion

The resulting statement to sort characters in a string is mostly noise because the purpose of the statement statement is obscured by the need to convert an object from one type to another. In an earlier article about palindromes, another way to make a string from an character array is to use the static method [string]::Join(). That is …

> $sig = [string]::Join("", ($s.ToCharArray() | sort-object))

The second method is shorter but still rather obscure because it relies on the side-effect of the empty string argument when calling the Join() method. It's a rather disappointing end to this exercise because I spent most of the time fighting instead of using PowerShell.

19-Dec-2007. PowerShell 2.0 will have a new Join operator that should make this exercise moot.

21-Dec-2007. Fourth method is to change OFS first, leading to:

> $OFS = ""
> $s = "mad"
$gt; $sig = [string]($s.ToCharArray() | sort-object)
> $sig
adm
> $sig.length
3

2007-12-15

Two Hello World Windows in Groovy

Groovy is scripting language on the Java platform. Groovy is interesting because it can be compiled into Java byte-code and makes heavy use of closures.

Groovy Hello World

In the beginning, I ported my Jython version into Groovy. It looks pretty much the same as the Jython version:

// Hello World in Groovy
f = new javax.swing.JFrame("Hello World")
f.setSize(170,70)
f.contentPane.layout = new java.awt.FlowLayout()
f.defaultCloseOperation = javax.swing.JFrame.EXIT_ON_CLOSE
f.add(new javax.swing.JLabel("Label me:"))
f.add(new javax.swing.JButton("Press me"))
f.show()

Groovy + SwingBuilder Hello World

Groovy has a helper class called SwingBuilder to make it much easier to write Swing applications. Here's one way to re-write the program:

// Hello World Window in Groovy + SwingBuilder
sb = new groovy.swing.SwingBuilder()
f = sb.frame(
  title:"Hello World"
  ,size:[170,70]
  ,defaultCloseOperation: javax.swing.JFrame.EXIT_ON_CLOSE) {
  flowLayout()
  label(text:"Label me:")
  button(text:"Press me")
}
f.show()

SwingBuilder is quite wonderful because it allows you to define a GUI with a minimum of code noise. The definition of a JFrame probably maps a keyword X to a setX() function but I haven't worked out how SwingBuilder converts words such as flowLayout(), label and button to Swing objects and functions.

PowerShell Hello World Window

Here's an attempt at a Hello World Window using PowerShell:

# PowerShell Hello World Window
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$b = new-object System.Windows.Forms.Button
$b.Text = "Press Me"

$l = new-object System.Windows.Forms.Label
$l.Text = "A label:"
$l.Size = new-object System.Drawing.Size(50,20)
$l.TextAlign = [System.Drawing.ContentAlignment]::BottomLeft

$p = new-object System.Windows.Forms.FlowLayoutPanel
$p.Controls.Add($l)
$p.Controls.Add($b)

$f = new-object System.Windows.Forms.Form
$f.Text = "Hello World"
$f.Size = new-object System.Drawing.Size(160,70)
$f.Controls.Add($p)
$f.ShowDialog()

Mini Observations

  • System.Drawing namespace and assembly is loaded with System.Windows.Forms at the start.
  • Unlike IronPython, only the actual .Net constructors for System.Windows.Forms classes are available, so you have to set properties of objects in separate statements after an object is created.

2007-12-14

IronPython and Jython Hello Windows

The Python language has been ported to two major virtual machine platforms: IronPython for Microsoft .Net and Jython for Java. To get a flavour of these implementations, here are two Hello World scripts that open a window containing a label and a button.

IronPython Hello World Window

import clr
clr.AddReference("System.Drawing")
from System.Drawing import ContentAlignment, Size
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Button, FlowLayoutPanel, Form, Label

p = FlowLayoutPanel()
p.Controls.Add(Label(Text="A label:", Size=Size(50,20), TextAlign=ContentAlignment.BottomLeft))
p.Controls.Add(Button(Text="Press Me"))
f = Form(Text="Hello World", Size=Size(160,70))
f.Controls.Add(p)
f.ShowDialog()

Jython Hello World Window

# Jython Hello Window
from java.awt import FlowLayout
from javax.swing import JButton, JFrame, JLabel

f = JFrame("Hello", defaultCloseOperation=JFrame.DISPOSE_ON_CLOSE, size=(170,70), layout=FlowLayout())
f.add(JLabel("A label:"))
f.add(JButton("Press Me"))
f.show()

Mini Observations

  • IronPython makes loading the .Net libraries explicit by using the clr module.
  • Both implementations allow setX functions in object constructor's argument list.

2007-12-12

Sax BASIC Initialize Array

If you're using Sax BASIC 6.4.x, you can only initialize an array of Variant using the Array(), not any other type. For example:

Option Explicit
Dim s1() As Variant
s1 = Array("a", "b", "c") 'Works
Dim s2() As String
s2 = Array("a", "b", "c") 'Fails with (10902) Builtin function/instruction is not implemented

2007-12-10

PowerShell Where-Object Predicates

In an earlier article about palindromes , I wrote an if-statement in the script block of the Where-Object cmdlet. While that's fine when the test is simple, the purpose of the Where-Object is obscured when the script block spans several lines, and it is not possible to re-use the code in the script block.

For example, let's find all prime numbers from a list of numbers. If you follow a top-down programming style, your code might look like this:

> 1..20 | Where-Object { Test-Prime $_ }

Now we have to supply a predicate Test-Prime that returns True if a number is prime, and False otherwise. Below is an example using Eratosthenes Sieve:

> function Test-Prime { 
  $isprime = $True
  foreach ($i in 2..([math]::Sqrt($args[0]))) {
    if ($args[0]%$i -eq 0) {
      $isprime = false
      break
    }
  }
  if ($isprime) {
    $args[1] += $args[0]
    return $True
  } else {
    return $False
  }
}

Test-Prime requires an array of existing primes, so the resulting script block is slightly different from our original plan. Oh well, can't plan everything in all the time. Let's make the modification and do a test run:

> $primes = (1,2); 1..20 | Where-Object { Test-Prime $_ $primes }
3
5
7
11
13
17
19

Not too shabby. It would be nice if Test-Prime can maintain its own list of prime numbers instead of relying on the caller to create it. I'll revisit this problem in future when I study more about PowerShell.

Convert Number Bases

Convert a number from base-10 to another base using the .Net Convert.ToString(number, target-base) static function. Test in PowerShell:

> [Convert]::ToString(15,16)
f
> [Convert]::ToString(15,2)
1111

To convert from base-n to base-10, use .Net Convert.ToInt32(String, source-base). There are also ToInt16() and ToInt64() methods. Examples using ToInt32() below:

> [Convert]::ToInt32("f",16)
15
> [Convert]::ToInt32("1111",2)
15

If you use the wrong source-base, PowerShell shows this error:

Exception calling "ToInt32" with "2" argument(s): "Could not find any recognizable digits."
At line:1 char:19
+ [Convert]::ToInt32( <<<< "f",2)

Note: Don't mix this class with DOS convert command which converts a filesystem from FAT32 to NTFS!

2007-12-09

Find Duplicate Objects in PowerShell

You can find unique lines in a file using Get-Unique but what if you want to know which lines are duplicates? Below are two variations of a one-liner that finds duplicate lines. In both cases, an object is added into an array $a if it does not already exist in that array ($a -contains …). If the object exists in the array (i.e. not unique), it is printed out. A side-effect is that the unique lines are available in $a after the statement is finished.

> $a = @(); foreach ($l in Get-Content <file>) { if ($a -contains $l) { $l } else { $a += $l } }
> Get-Content <file> | Foreach-Object { $a = @() } { if ($a -contains $_) { $_ } else { $a += $_ } }

The first version is implemented using the foreach statement. The $a array is initialized in the first statement, then the file is read into $l one line at a time in the second statement. It's actually a short two-liner.

The second version is implemented using the Foreach-Object cmdlet in a command pipeline. The Foreach-Object can have three command blocks: beginning, middle and ending. The $a array is initialized in the beginning command block ($a = @()) and the test and output executed in the middle command block (if () …). The ending command block is not used in this example. This version is more tidy because only one variable, $a has to be created; the other version requires another variable $l.

2007-12-08

Powershell Palindromes

A palindrome is a word or phrase that reads the same forwards or backwards. A well-known example is Madam, I am Adam. Another example is Weird Al Yankovic's song Bob, which is composed of rhyming palindromes. If you have a list of words in a file, here's how you can find palindromes using a one-liner in PowerShell.

> Get-Content <file> | Where-Object {[string]::Join("",$_[-1..-$_.Length]) -eq $_ }
…
aha
ala
dud
mm
mum
pap
pip
…

The one-liner translates to: for every line in an input file, display only lines which are the same forward and backward.

The complex part of this pipeline is the filter in Where-Object. In this filter, we …

  1. Convert the input string, $_, into an array of characters in reverse order: $_[-1..-$_.Length].
  2. Convert the array back into a string by using the Join() function with an empty separator: [string]::Join("",<array>).
  3. Compare the reversed and original strings using <s1> -eq <s2>.

Where-Object only outputs a line when comparison operator returns True.

Slicing PowerShell Arrays and Ranges

Introduction

You can slice (or extract elements) from an array in PowerShell in several ways. Let's start with a simple array of numbers. We start with 0, because PowerShell's array indices start from 0 and to make it easier to see the effect of each statement. We use the Write-Host cmdlet where necessary to keep the number of lines in the output to a minimum.

> $a = 0,1,2,3,4,5
> $a.length
6
> Write-Host $a
0 1 2 3 4 5

The rest of this article describes how to obtain a single item, multiple items and a reverse slice, and how to combine an index list with a range.

Get an Item

Let's retrieve an item from different indices in our array, $a:

> $a[0]
0
> $a[$a.length]
> $a[-$a.length-1]
> $a[$a.length-1] -eq $a[-1]
True
> $a[$a.length-2] -eq $a[-2]
True

The first item is in index 0 and the last index is $a.length-1. Also, $a.length-x is the same as -x. If you try to access an index value greater than $a.length-1 (e.g. 6) or less than -$a.length (e.g. -7), you will get nothing.

Get Arbitrary Set of Items

We can also retrieve multiple items and items more than once.

> Write-Host $a[0,-1,5,2,4,1]
0 5 5 2 4 1

Get Multiple Items Using a Range

We can also retrieve a range of values using the .. (dot-dot) operator.

> Write-Host $a[0..2]
0 1 2
> Write-Host $a[2..5]
2 3 4 5
> Write-Host $a[0..($a.length-1)]
0 1 2 3 4 5

We enclose the arithmetic expression in parentheses otherwise the command processor generates the error below. It seems like 0..$a.length-1 is expanded into an range and the length method is called for the last number. The error seems a bit unexpected because I would have thought that the binary minus operator had a higher precedence than the range operator. Also, the number converted into System.Object, doesn't have a length method, and PowerShell would have reported that error instead of an error about the op_Subtraction method.

> write-host $a[0..$a.length-1]
Method invocation failed because [System.Object[]] doesn't contain a method named 'op_Subtraction'.
At line:1 char:28
+ write-host $a[0..$a.length-1 <<<< ]
>> write-host 0..$a.length-1
0..0 1 2 3 4 5.length-1

Get a Reversed Slice

We can also get a reversed slice of the array by specifying the right index before the left index in the range.

> Write-Host $a[5..2]
5 4 3 2
> Write-Host $a[($a.length-1)..0]

We know that -1 represents the last item, so can we simplify the second statement?

> Write-Host $a[-1..0]
5 0

What went wrong? How does PowerShell interpret -1..0?

> -1..0
-1
0
> Write-Host $a[-1] $a[0]
5 0

The range operator expanded -1..0 to 5 and 0. Is there another way specify a reverse slice of the original array? Let's try the following line of reasoning.

  1. From first section, $a.length-x refers to the same item as -x
  2. Replace x with $a.length
  3. So $a.length-$a.length (or 0) refers to the same item as -$a.length
> Write-Host (-1..-$a.length)
-1 -2 -3 -4 -5 -6
> Write-Host $a[-1..-$a.length]
5 4 3 2 1 0

Combining Numeric and Range Indices

Since an array slice can be specified by either a list of numbers or a range, can we combine them?

> Write-Host $a[0,3,2..5]
Cannot convert "System.Object[]" to "System.Int32".
At line:1 char:22
+ Write-Host $a[0,3,2..5 <<<< ]
> 0,3,2..5
Cannot convert "System.Object[]" to "System.Int32".
At line:1 char:8
+ 0,3,2..5 <<<<
> Write-Host 0,3,(2..5)
0 3 2 3 4 5
> Write-Host $a[0,3,(2..5)]
0 3

In the first and second statements, the comma array constructor operator has higher precedence than the range operator, so PowerShell could not create the list from 2..5. In the third statement, we add parentheses to modify the precedence so that the range operator creates a list 2 3 4 5.

However, in the fourth statement, it seems like PowerShell has ignored the list generated by the range operator because only two items are displayed. You can combine a range with a list of indices using the + (Plus) operator. The syntax is a little inconsistent because you can produce an index list without this operator:

> Write-Host $a[0,3+2..5]
0 3 2 3 4 5

Furthermore, you have to use the Plus operator before and after a range:

> Write-Host $a[0,3+2..5,1]
Cannot convert "System.Object[]" to "System.Int32".
At line:1 char:22
+ write-host $n[0,3+2..5 <<<< ,1]
> write-host $n[0,3+2..5+1,4]
0 3 2 3 4 5 1 4

Conclusion

This article has demonstrated how to slice arrays in PowerShell, starting from single item slices, progressing to multiple items, using the range operator and described two approaches to obtain a reversed list of items from an array. We also found that you have to use the Plus operator to combine the comma array constructor operator and range operator to get an array slice.

Renesis SVG Plug-in

It's old news that Adobe no longer develops its SVG Viewer plug-in. The only browser that really required an SVG plug-in is MSIE; Firefox, Opera and Safari browsers support SVG. If you're still using MSIE and want to view SVG, Examotion has a pre-release Renesis SVG plug-in.

I viewed some pages containing SVG with Renesis and found Renesis can display static images and Javascript animation in an <object> tag. Renesis doesn't currently support in-line SVG (essentially SVG code in XML).

2007-12-07

Outlook 2003 Smart Quotes and Right Justified Plain Text

Outlook 2003 mail editor has some undocumented shortcut keys for formatting plain text messages (!) that I accidentally triggered.

  • Ctrl+R right justifies the current paragraph and all new text. To restore your message back to left-justified formatting, select Format / HTML then select Format / Plain Text menu items. The shortcut key and menu item to left-justify a paragraph aren't available in plain text mode.
  • Ctrl+Shift+' toggles the Smart Quote (or Curly Quote) feature, where a leading and trailing quotes are ‘ ’ and “ ”.

These shortcut keys and features aren't useful in plain text mode. On the other hand, the useful shortcut key, Shift+F3 to switch case of selected text, doesn't work.

2007-12-04

PowerShell Directories, Collection and Range Operator

What are the biggest files in a directory? Using the Select-Object Cmdlet provides this command chain:

Get-ChildItem c:\windows\*.* | Sort-Object length -descending | Select-Object -first 3

In the article, the first two commands produce a collection and Select-Object picks the first three items. It's a little tedious having long command chains, so can we make it shorter? Since we have a collection, we can access its items using the array syntax and an index:

(Get-ChildItem c:\windows\*.* | Sort-Object length -descending)[3]

Using the dot-dot range operator (..), we can generate a list of indices using [0..2] (collections start with a 0 index) and reproduce the effect of Select-Object -first 3:

(Get-ChildItem c:\windows\*.* | Sort-Object length -descending)[0..2]

What about getting last 3 items using Select-Object -last 3? It can be replaced by [-3..-1]. Note that the last item in a collection starts from -1.

(Get-ChildItem c:\windows\*.* | Sort-Object length -descending)[-3..-1]

2007-12-01

Making Paragraphs With Vim

E-books from Project Gutenberg are broken into 70-character lines. Fixed length lines can be hard to read on a mobile phone, so I wanted to let the phone's viewer break the lines and use only a blank line between paragraphs. Here's some text from Arthur Conan Doyle's A Scandal in Bohemia originally fro Project Gutenberg:

"Wedlock suits you," he remarked. "I think, Watson, that you have
put on seven and a half pounds since I saw you."

"Seven!" I answered.

"Indeed, I should have thought a little more. Just a trifle more,
I fancy, Watson. And in practice again, I observe. You did not
tell me that you intended to go into harness."

Below is how this text should become (paste the before and after text into your text editor to see the difference):

"Wedlock suits you," he remarked. "I think, Watson, that you have put on seven and a half pounds since I saw you."

"Seven!" I answered.

"Indeed, I should have thought a little more. Just a trifle more, I fancy, Watson. And in practice again, I observe. You did not tell me that you intended to go into harness."

The following command in Vim did the required conversion: %s/\(.\)\n\(.\)/\1 \2/. Basically, for every line, replace any new line character between any two characters with a whitespace.

2007-11-30

Xandros and Firefox on Radio National

It was so weird driving to work and hearing a segment about Xandros and Firefox on Radio National (MP3 recording). For a few minutes, I thought I had passed into some bizarro open-source world because the presenters hardly mentioned Microsoft Windows.

2008-06-21: Updated MP3 link.

2007-11-28

Unexpected Windows Scheduled Task Copy-Paste Bug

I never expected to find this bug. In Windows XP, if you try to copy and paste a task in the Scheduled Task folder, you may get this error message: Cannot copy <Task Name>: The source and destination file names are the same. Even the obvious workaround doesn't work: copy a task, rename the original and paste the new task. unlike Windows Explorer, Scheduled Task doesn't seem to give a copied task a name such as Copy of x.

2007-11-25

Beginning PowerShell

Introduction

Microsoft's PowerShell is a scripting environment based on the .Net framework. PowerShell is aimed at replacing Windows Cmd and Windows Scripting Host (WSH) for administrative tasks. What interests me is that I can use .Net objects interactively and in a script, without having to write and compile a program. As I explore PowerShell, I'll write about features that I find interesting and useful.

In this first article, I describe really basic use of the PowerShell console. I assume you downloaded and installed Powershell.

Help!

Powershell installation provides a lot of on-line help. You obtain information using the get-help cmdlet (essentially a built-in command). There's so much information that the trick is to figure out the appropriate help topic:

> help # or 'get-help'
< 299 lines … >
> help about_Arithmetic_Operators
TOPIC
    Arithmetic operators

SHORT DESCRIPTION
    Operators that can be used in the Windows PowerShell to perform
    mathematical operations
…

You can also download the on-line help as a CHM file (search for "Windows PowerShell Graphical Help File").

Interactive Mode

You can enter commands for processing immediately:

> 'abc' + 'def' # String concatenation
abcdef
> 5+6 # Addition
11
> 5*6 # Multiplication
30
> 30%7 # Remainder
2

The '#' marks the start of a comment. All text from '#' to the end of the line is a comment.

Using .Net Classes and Objects

How long is a string (said with a straight face)? Since we are using .Net String objects, each string should have a Length property:

> 'abc'.Length
3
> 'def'.Length
3
> ('abc' + 'def').Length
6

You can find the methods of an object using get-member.

> 'abc' | get-member


   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
…
get_Length       Method                System.Int32 get_Length()
…

What's 2 to the power of 8? The .Net System.Math class defines a set of common mathematical functions. Again, you can use the get-member cmdlet to find out what functions are available in any class, but this time, with the -static option because the class, not individual objects, define these functions.

> [math] | get-member -static


   TypeName: System.Math

Name            MemberType Definition
----            ---------- ----------
…
Pow             Method     static System.Double Pow(Double x, Double y)
…
> [math]::Pow(2,8)
256

The System.Math is pre-loaded by PowerShell and can be accessed as [System.Math], [Math] or [math]. Square brackets refer to .Net classes and some aliases have been defined for these classes.

How do you know whether an object or a class has the required function? No easy answer: trial-and-error, and experience with other class libraries.

Tab Expansion and Aliases

It's pretty tedious typing a full cmdlet string, so there are two shortcuts available: tab expansion and aliases.

If you enter get-m<TAB>, you should see Get-Member. For some reason, you have to enter the name of cmdlet up to the '-' (dash) character before tab expansion works.

The get-member cmdlet also aliased as gm. What aliases are available? Use the get-alias cmdlet, which is also aliased as gal, for a list of available aliases.

> Get-Alias

CommandType     Name                                                Definition
-----------     ----                                                ----------
Alias           gal                                                 Get-Alias
…
Alias           gm                                                  Get-Member
…

Conclusion

At this point, you can run some simple commands using PowerShell and know how to find some on-line information.

2007-11-13

Uninstall Product Desktop Shortcut

During the development and test phase of a project, I have to install and uninstall daily program builds for testing, so I'm pretty interested in reducing the amount of time and effort required to uninstall programs. Here's how my uninstallation process evolved:

In the beginning, use Start / Control Panel / Add or Remove Programs.

Spend less time by starting Windows' Add or Remove Programs dialog using the Run dialog by typing Windows+R appwiz.cpl.

Then you might notice that the Windows XP Add or Remove Programs dialog takes up to 30 seconds to start and there's no quick way to find the desired program. It's annoying that the list of programs doesn't scroll when you hit the PageDown and PageUp keys or when you type the first few letters of a program name. (The Vista equivalent, Programs and Features, doesn't have these limitations.)

Finally, using Windows Installer, msiexec, you could uninstall any program if you have the ProductCode. Just create a desktop shortcut with the following string in the Target field:

C:\WINDOWS\system32\msiexec.exe /uninstall <ProductCode>

P.S. You can find the ProductCode of your program using this script.

2007-11-12

Weird NLS_LANG JDBC Connection Error

A colleague got a new computer and when he tried to connect to an Oracle server with a JDBC client, he got this error:

ORA-12705: Cannot access NLS data files or invalid environment specified

After comparing our Java environments, he found that he had user.country = BZ (BZ is the top level domain for Belize). He changed his Regional and Language Options to English (Australia) and then he had no problems connecting to the Oracle server.

I think when JDBC client tries to connect to the Oracle server, the client specifies its locale and when the Oracle server fails to find the required locale files, the server throws the ORA-12705 error message.

2007-11-11

Event 1530, User Profile Service

After having to debug a couple of Windows profile problems recently, I started to keep an eye for Warning or Error events in the Windows Event Viewer. The latest warning was Event 1530, User Profile Service, where there was some conflict between processes writing to the registry as I was logging out and had the following details:

 2 user registry handles leaked from ...:
Process 932 (\Device\HarddiskVolume2\Windows\System32\svchost.exe) has opened key ...
Process 3472 (\Device\HarddiskVolume2\Windows\System32\IFXSPMGT.exe) has opened key ...

Process 3472 is IFXSPMGT.exe (Infineon Security Platform Software) but what was process 932? One solution was to run tasklist and save the list of process numbers in the current session. Here's the command to run:

tasklist /svc /fo table /fi "imagename eq svchost.exe" > C:\Temp\Processes.txt

Very roughly, we are looking for all services started by svchost.exe.

I ran the tasklist command in the previous session and found that process 932 was WinDefend (Windows Defender, Microsoft's anti-spyware program).

14-Nov-2007. For Windows pre-Vista, if you receive Event ID 1517, Microsoft provides User Profile Hive Update (UPHClean.exe) utility to detect and free the registry. There isn't a version of UPHClean.exe for Vista yet.

2007-11-08

Very Slow FTP Download Fixed

When I download files from our FTP server using my notebook on the company LAN, the download speed was 10 to 20 kbits/sec, which is very slow compared to using a local desktop, where the download speed was closer to 150 kbits/sec. There was no problem downloading files from external Internet sites. The IT guy and I puzzled over this problem off and on; it was annoying but not critical. I tried different FTP clients but there was no difference. Then the IT guy suggested turning off Deterministic Network Enhancer (DNE) (open Local Area Connection Properties and clear the DNE's checkbox). When I restarted my FTP client, the download speed improved to the same level as the desktop.

It turns out that DNE was installed with the VPN software on my notebook. I have to remember to re-enable the DNE when I use the VPN. Still beats me why it only affected FTP downloads from our local server.

14-Nov-2007. It's not DNE. When I reset the network adapter after I start the FTP client, the download speed improved. IT Guy thinks my notebook may require an updated network adapter driver or BIOS.

Updated my Asus BIOS from 214 to 218 and the networking problem seems to be fixed.

15-Nov-2007. It's not the BIOS either. Updated my Realtek RTL8160/8111 network adaptor driver from 5.666.301.2007 to 5.680.1023.2007 and I'll see what happens the next time I restart my notebook.

16-Nov-2007. Success! My notebook download speed went past 130 kbits/sec on the first go.

2007-11-06

Firefox more popular than MSIE6

I can't quite believe my eyes. W3Schools Browser Statistics page shows that in September 2007, there more hits from developers with Firefox than MSIE6 or MSIE7 (35.4% vs. 34.9% vs. 20.8%). In CY2007, MSIE7's take-up rate started high but has since slowed to the same rate as Firefox, indicating that all early adopters have upgraded from MSIE6 to MSIE7. As the year progressed, MSIE6's share fell, and developers migrated to MSIE7 and Firefox in equal proportions. MSIE7 is built into, or auto upgraded in, new Windows distributions, so the fact that developers go out of their way to install Firefox must indicate a higher perceived value in Firefox over MSIE7.

Its also nice for Firefox to segue from a niche technophile browser into mainstream awareness. For instance, in a recent game show on telly, Are you smarter than a 5th grader?, both Firefox and MSIE are mentioned in the same breath:

Q: What type of software are Firefox and Internet Explorer? (That's roughly the question.)

A: Web browsers.

2007-10-28

GIMP and Inkscape Stylus Tracking Problem

If you use a Wacom Graphire tablet and drawing tools GIMP 2.4 or Inkscape 0.45.1 on Windows Vista, you may find the cursor unresponsive; for instance, it would halt and jump to a new location after a some seconds of use. The problem seems to be caused by the underlying GTK library not processing all the events from the tablet device. Drawing tools that use different libraries, such as Paint.Net don't have this problem.

A work-around is to avoid using devices via the WinTab interface. Just add the following option to the Windows shortcut for the GIMP and Inkscape: --no-wintab.

There's some drawbacks of this work-around:

  • You can't use the pressure sensor on the Wacom stylus to change the drawing cursor in the GIMP.
  • You can't use the eraser on the rear of the Wacom stylus as another drawing tool.

2008-05-06: Another bug to track is Wacom Bamboo Doesn't Function with GTK apps in Win32.

2007-10-26

GIMP 2.4 Released

GIMP 2.4 has been released. For occasional users with Windows Vista, the most noticeable change is that it only takes seconds to load after the first time; in version 2.2.x, it used to take about half a minute. However, support for Wacom tablet is not good; the pointer still freezes after some seconds. Maybe the next version of GTK would improve the situation?

2007-10-11

Windows Command Selected Text

If I inadvertently click in a Windows cmd window when a batch job is running, the job halts because the window blocks all output while it waits for me to make a selection. The fix, of course, is to just hit the return key and clear the selection. Now I notice that if I start highlighting a selection, the text in the title bar is prepended with Select.

2007-08-17

Windows Games Mysteriously Halting

My son found a problem with his computer. Sometimes, the game he was playing would halt after it started. Then he said that if he defragmented the hard disk, the game would start running again. I didn't think too much about it until the problem occurred yesterday evening. When I examined his computer, I realised that it had run out of disk space. The game didn't crash; it was waiting for some spare disk space to write its files. When he defragmented the hard disk, some space was freed up, so the game could continue running. I freed up about a third of the hard disk and there's no problems running games again.

2007-08-16

Firefox Custom CSS Styles For Sites

The Stylish add-on for Firefox is really, really neat. Create your own custom CSS stylesheets or install pre-defined ones in userstyles.org. Stylish will apply the style sheet based on a URL pattern.

I've installed …

  • Bright Focus because it's hard to see which button or link has the focus by default. Read the users' comments on the page to find out how to customize the focus colour or style.
  • Gmail - display keyboard shortcuts.
  • 18-Aug-07: Slim down Firefox GUI so that I have slightly more space for web pages. Customized this style so that the Edit and History menus remain displayed.

See Also

2007-08-15

Synaptics Touchpad Sketch Utility

Tried drawing with the Synaptics touchpad; after all, there should be a way to map your finger's position to an absolute location in a window or on the screen. Found a little Synaptics Sketch utility for doodling, but it was pretty useless because it doesn't show your finger's position before you make a mark and you can only save your image in a proprietary "SKE" format. Oh, well.

2007-08-13

More Asus Synaptics Touchpad Settings

Set up the following Tap Zones (in the four corners) on my touchpad:

  • Bottom Left Action: Browse forward one web page.
  • Bottom Right Action: Context menu.
  • Top Left Action: Browse backward one web page.
  • Top Right Action: Context menu.

16-Aug-07: Found a better configuration. Changed Bottom Right Action to Middle Click. In Firefox, this opens a link under the pointer in a new tab.

2007-08-11

GIMP Transparent Backgrounds

Steps for making a transparent GIF layer in GIMP.

  1. Open your GIF image file.
  2. Open the Channels tab. You should see a single Indexed channel.
  3. Add Alpha Channel (Layer / Transparency / Add Alpha Channel). You should see a new Alpha channel added in the Channels tab.
  4. Make a selection by selecting a colour (Select / By Color then click on the area with the required colour).
  5. Optionally, enlarge the selection (Select / Grow, then enter the number of pixels in the Grow Selection dialog). See note below.
  6. Clear the selection (Edit / Clear).
  7. Save your GIF image file.

When making a transparent GIF with GIMP, sometimes the borders of the image can be a little ragged, especially next to a curve with antialiasing. One solution is to "grow" the selection by one pixel (Select / Grow) before clearing all the pixels for the transparent layer.

2007-08-05

ATI Catalyst Control Center Rotate Display

Added some notes on how to use the ATI Catalyst Control Center to turn computer display 90° clockwise and anti-clockwise. Sort-of useful for reading on-line documents, such as webcomics, in portrait format.

9-Aug-07: This feature isn't available on another Asus laptop with Windows XP. Maybe it's just a Vista feature?

2007-07-29

New Web Hosting Service

Transferred to a new hosting service. I kept running out of disk space on the old service and the new one has more space and is even cheaper. This is essentially a test post.

Notes to myself:

  • It takes some minutes before nameservers propagate the updated host address for a domain name. Check with the ISP's name server to see if the domain name has been redirected.
  • To test the new host, remember to flush the browser's cache.
  • Ack! Forgot to copy the PHP header files! Remember to run sync software and test on a second computer next time!

2007-07-18

Remove Word Drawing Canvas

When making a Microsoft Word Picture in Microsoft Word 2003, the drawing editor first adds a drawing canvas. Apparently, the canvas allows the user to keep shapes together when printing, so that the picture isn't split between two pages. While it might be useful for shapes inserted into a document, there's isn't much point having a canvas in a Word Picture object, because that's displayed in one page. To stop Word from automatically adding a drawing canvas, select Tools / Options / General and uncheck Automatically create drawing canvas when inserting Autoshapes.

2007-07-14

Vista Tablet Input Panel Revisited

Found some better ways to use Vista's Tablet Input Panel (TIP):
  • Activate TIP by wriggling the tablet's pen above the tablet's surface (in other words, don't press on the pen's tip). To turn on this feature, select checkbox Enable start Input Panel gesture in Pen and Input Devices / Start Input Panel Gesture Settings control panel. Aside: the instructions are in that dialog but it took a while to sink in that I had to avoid pressing on the pen's tip while moving the pen.
  • When using Flicks to navigate, set the sensitivity close to Relaxed otherwise you have to move the pen very fast for Vista to recognize a flick event.
  • Use Press and hold to generate a mouse-style Right-click event so that you can display a context menu. This action is useful for browsing (e.g. open URL in a another tab) but any selection (such as highlighted text) is de-selected.

2007-07-08

Vista Tablet Input Panel

Vista comes with a handwriting recognition system for tablet PCs, so if you have a tablet device, such as a Wacom graphics tablet, you can enter text using a pen interface. If you are used to pen interfaces on handheld devices, then the Vista version is - um - different. You can only write in a special dialog called the Tablet Input Panel (TIP), not anywhere on the screen and you have to insert the text into your text field or document window. The TIP takes some getting used to. It always appears on top of all windows if you float the window. If you dock it at the top or the bottom of the screen, it fills too much of the screen and you can't shrink it.

After some experimentation and practice here's some tips that may help you use it:

  • Use an extra-fine point (ink thickness) so that you can more easily read your own writing. The default point size just makes a smudge if your handwriting is small.
  • TIP can better guess your words if you have clear spacing between words and if you write on the horizontal guide with obvious descenders {e.g. g and y) and ascenders (e.g. d and h). After a while, you don't even have to cross your t's or dot your i's or even write every letter for the system to guess the right word.
  • After writing in TIP you can just tap your input field to enter your text instead of having to press the Insert button.
  • To cross out words, you should carefully draw a horizontal line through the middle of the entire word. If your stroke is drawn too quickly, TIP thinks that you are writing another letter and tries to guess what it is.
  • TIP's Writing Pad allows you to correct the guess for a word even if you are already writing another word. For example, if you started writing "XML lag are..." you can go back to cross the "t" and add an "s" in the second word to change it from "lag" to "tags".
  • If you want to enter special codes such as XML tags, use the Character Pad instead of the Writing Pad, and enter each character separately.

Some improvements to TIP (if any developers are reading this):

  • There should be a second horizontal guideline, like preschool writing exercise books to help TIP distinguish between capital and lowercase letters that have the same strokes, such as "x" and "X", and symbols such as "<" and "(".
  • It should be possible to make the TIP dialog smaller and semi-transparent so that you can see more of a document or input field. With all the eye-candy available in Vista, it's annoying that the user can't better configure this dialog.

This entry was mostly written using TIP and edited using the keyboard.

2007-07-02

Excel Fill Series

A consultant showed me a neat Excel feature today: filling rows or columns using a rule.

You might be familiar with the auto-fill feature where you enter part of a series, e.g. 1, 2, 3, into a sequence of cells, select the cells, then use the mouse pointer to grab a corner of the selection to extend the series. But if you want to extend dates monthly, e.g. starting with 1-Jan-07, 1-Feb-07, 1-Mar-07, you would end up with a repeating series of the same dates.

Excel has a function that lets you choose different rules for filling a series of cells. To create a monthly series in a row, enter a start date in a cell, select a group of cells to fill, then choose the Edit / Fill / Series to display the Series dialog. In this dialog, select the Rows radio button, then the Date radio button, then the Month radio button, then press the OK button.

If you want to create a quarterly series, repeat the steps above, but this time enter 3 in the Step value field. Now you will get a series such 1-Jan-07, 1-Apr-07, 1-Jul-07, 1-Oct-07, 1-Jan-08, 1-Apr-08, 1-Jul-08.

2007-07-01

Oracle Import and Export LOBs

After playing with PL/SQL, LOBs and writing files on Oracle, I wrote an article explaining how to import and export LOBs.

2007-06-24

Vista Enable Microphone Recording

I wanted to make a recording but there were no recording devices available in Vista's Sound control panel applet. Then I found out that I had to select Show Disabled Devices menu item in the Recording tab sheet's context menu to see the microphone before I could enable it. Grrr! Prior to this problem, I'd never seen a context menu in a tab sheet. Also, it seems silly to have the option to hide audio devices; most users would only have a small number of playback and recording devices in the first place.

2007-06-17

More PHP Web Boilerplate

Added a more PHP Web boilerplate text to save more typing. The page structure is now simplified to …

<?php
include "php/xhtml_mimetype.php";
$doc_title = "It Mostly Works";
include "php/header.php";
?>

…

<?php include "php/trailer.php" ?>

2007-06-16

Apache Blank Error Dialog on Vista

Whenever I restart my Vista computer after installing Apache 2.2, I see a error dialog with no message or icon. Repairing the installation didn't fix the problem.

After the usual ten minutes of head-scratching and searching, it turns out that the Apache installer creates a task in the Windows Startup folder called Apache Monitor, which refers to ApacheMonitor.exe. This is Windows taskbar applet which, I guess, allows you to monitor the status Apache services. Since I only run Apache on my computer for software development, I don't particularly care about running this applet and I removed it from the Startup folder. When I restarted my computer again, the blank error dialog is no longer displayed.

Investigating further, I found that this program doesn't work in Vista.

2007-06-12

XHTML using PHP

Introduction

Started using PHP for my website to generate static pages and save myself a lot of cutting-and-pasting each time I made a new page; could have used Apache's Server-Side Include feature but I intend to write some interactive pages in the future. Here's the changes I've made so far:

XHTML / PHP File Structure

The structure of my XHTML / PHP files follows:

<?php include "php/xhtml_mimetype.php"; ?>
<html … >

<head>
<title>Title Text</title>
<?php include "php/head.php"; ?>
</head>

<body>
<?php include "php/header.php" ?>

…

<?php include "php/trailer.php"; ?>

</body>

</html>

While HTML tags are static, I left them in the XHTML file so that a text editor can match the opening and closing tags.

xhtml_mimetype.php

The xhtml_mimetype.php file generates the HTTP header and writes the XML declaration and DOCTYPE. See Serving XHTML with the correct mime type using PHP by Neil Crosby on how to generate headers for browsers that can and cannot support XHTML (such as MSIE6 and MSIE7).

head.php

head.php generates META and LINK tags.

header.php and trailer.php

header.php and trailer.php contain static boilerplate XHTML text and a function to write the last modified date.

.htaccess

Added PermanentRedirect directive for pages that are now generated using PHP. The rule is currently applied on a file-by-file basis until I have converted all my files from HTML or XHTML to PHP.

2007-05-20

Review Mastering Oracle SQL 2nd Edition

Mastering Oracle SQL 2nd Edition by Sanjay Mishra and Alan Beaulieu

The goal of the authors is to explain how to write good readable SQL queries in Oracle 10g. The book starts with how to construct SELECT statements to group, filter and format result sets for dates, reports and data analysis. Then it proceeds to cover Oracle-specific queries and functions for hierarchies (data in tree structures), object-oriented types, XML documents, regular expressions and models (spreadsheet-like objects). Where relevant, there are notes about the differences between SQL for Oracle 10, Oracle 9 and the ANSI standard.

As expected from the title, the chapters using declarative programming (i.e. SQL queries) for relational data, hierarchical data and reports are the most comprehensive. Chapters on interfacing Oracle SQL with other technologies such as scripting (Oracle's PL/SQL), object-oriented types, XML and regular expressions, or on optimization, are brief but sufficient to get you started, especially if you have a existing background in those technologies.

This is the 2nd edition, so it's not surprising that the scope of the book is well-defined and that the writing is easy to read and polished. The example data and queries are just complex enough to demonstrate the issues without obscuring the main points. Minor annoyance about Chapter 15, "SQL Best Practices", which does not explain how to use the query analyzer and bind variables.

I was already familiar with basic Oracle SQL but didn't really understand the language; this book blew away many of the fuzzy concepts in my mind and provided me the framework to tackle more complex problems.

2007-05-19

Auslogics Disk Defragmenter

Diskeeper Lite doesn't work out-of-the-box in Vista; it complains about not having access to Terminal Services. After installing and uninstalling Diskeeper Lite, Vista thinks that there's no longer a defragmenter registered! Ay caramba! Another problem to fix later.

Vista's built-in defragmenter defrag.exe is mute (no status, no progress bar) so I have no idea when I can shut down my computer after I've started a defragmenting job. I found another free disk defragmenter program Auslogics Disk Defrag.

2007-05-15

WinCVS Tcl Shell

Just noticed that I can enter Tcl commands in the WinCVS Output pane (see View / Output menu item). For instance …

info commands
tell socket subst open eof pwd glob list exec pid dir time eval lrange fblocked
lsearch gets case lappend proc break cvsentries variable llength …
info nameofexecutable
C:/Program Files/GNU/WinCvs 2.0/wincvs.exe
info tclversion
8.2

Useful commands for me, right now, are ls and pwd, so that I can open files or change the working directory in cmd shell.

2007-05-06

Vista First Impressions Again

After getting over the difficulty of transferring all my files from my old PC to the new one, here's my first impressions of Vista compared to XP:

  • Definitely lots of eye candy. The semi-transparent borders are neat. Shadow around each window softens their edges.
  • Text display is very clear. Can't tell if it's because of the new computer or improvements in font display. Where's the option to tweak font appearances?
  • Windows Sidebar takes up too much desktop real estate. Until I can find a useful gadget, I turned it off.
  • System administration tasks are better integrated with Control Panel. The related tasks in each window are helpful.
  • Internet Explorer 7.0 now has tabbed browsing, but Windows Explorer doesn't. Trying to opening a folder in MSIE7 just opens yet-another Explorer window. Isn't it obvious that Explorer should have tabbed views to improve file and folder management? Stick with 2xExplorer!
  • Tablet PC input device is standard. No idea how to enable it for the touchpad or a tablet device.
  • What, Powershell not installed by default?
  • Windows Media Player is less annoying than previous versions that wanted to go on-line all the time.

2007-05-05

Windows Vista First Impressions

Started playing with Windows Vista Home Premium (that's a mouthful). First thing I noticed was the eye candy. Desktop is very pretty. Second thing I noticed was how slow network transfers were. So slow that nothing happened when I tried to transfer files from my old computer to my new one.

Applied the following tweaks from Top 10 Vista Speed Tweaks.

  1. Disabled Indexing. I choose Google Desktop for indexing my computer.
  2. Turned of Remote Differential Compression.
  3. Turned off Windows Defender.
  4. Disk Defragmentation is not scheduled.
  5. Didn't try the USB boost option.
  6. Didn't try the Hibernation option.
  7. Left System Restore feature running. It seems like good insurance.
  8. Disabled User Account Control (UAC). Those Apple Get a Mac - Security ads were spot-on. UAC is such a pain in the neck if you want to do anything remotely resembling system administration.
  9. Disabled some unused services.

I also did the following:

  • Turned off Remote Assistance.
  • Removed Symantec Norton. This program also installed its own network adapters. Speed improved from less than 1 MB/sec to 2.3 MB/sec, which is about 50% the speed of my wireless router. Good enough for transferring my files from one computer to another.
  • Turned off the Firewall. My router has a hardware firewall and I prefer Sygate software firewall.

2007-05-02

Disable Compressed Folders

Here's how to disable Windows Explorer's compressed folder view. Basically, start cmd shell, then enter regsvr32 /u zipflr.dll. It's annoying (conceptual overloading, anyone?) when only ZIP files, rather than all compressed files are listed with normal folders in the right pane.

2007-04-29

JDBC to SQL Server Express

Connecting JDBC-based tools such as SQL Developer or DbVisualizer to SQL Server Express required the following steps:

  • Obtain JDBC Driver
  • TCP/IP for SQL Server Express
  • Authentication Method

Obtain JDBC Driver

Using SQL Developer, when you get the following exception …

Unable to find driver: net.sourceforge.jtds.jdbc.Driver

… download the jTDS JDBC driver and install it in your JRE's ext folder. The latest version of the driver is 1.2. Of course, there are other JDBC drivers for SQL Server you can use.

TCP/IP for SQL Server Express

By default, TCP/IP for SQL Server Express is disabled, so JDBC cannot connect to it and you may get the following exception …

Network error IOException: Connection refused: connect

Enable TCP/IP

To enable TCP/IP, start SQL Server Configuration Manager.

  1. Expand SQL Server 2005 Network Configuration node.
  2. In the right pane, select Protocols for SQLEXPRESS. The right pane should now show Protocols and Status columns.
  3. Select Enable from the TCP/IP context menu.

Find or Configure TCP/IP Port

After enabling TCP/IP, you have to find out which port number to use. SQL Server Express allocates a port dynamically each time it is started, so to find or configure the port number, continue using SQL Server Configuration Manager

  1. Select Properties from the TCP/IP context menu. The TCP/IP Properties dialog should open.
  2. Select the IP Addresses tab.
  3. In the IPAll node …
    1. The TCP Dynamic Ports field shows the currently used port number. If you set that field to blank, then SQL Server Express should not automatically choose another port when it restarts.
    2. Set the desired port number in the TCP Port field.
    3. Press OK to apply your settings and close the dialog.

Test TCP/IP

If you change the TCP/IP port, you have to restart SQL Server Express before it can use the new port number. To test that your port number is used, start a cmd window and type: netstat -an. For instance, if you used port 1433, you should see this line in the list of ports used:

TCP    0.0.0.0:1433           0.0.0.0:0              LISTENING

Authentication Method

By default, SQL Server Express uses Windows Authentication Mode to authenticate connections. If you get this exception …

Login failed for user '<User name>'. The user is not associated with a trusted SQL Server connection.

… then you may have to enable SQL Server Authentication Mode and create or enable a user.

  1. Start Microsoft SQL Server Management Studio Express (SSMSE) and connect to your database server.
  2. In Object Explorer pane, select Properties from your database's context menu. The Server Properties dialog should open.
  3. Select Security page.
  4. Select SQL Server and Windows Authentication Mode check box.
  5. Press OK button to close the dialog.
  6. In Object Explorer pane, expand Security / Logins node.
  7. Select existing user sa. Note that there is a red downward arrow next to that user's image.
  8. View sa's properties. The Login Properties dialog should open.
  9. Select Status page.
  10. Ensure that the Login: Enabled radio button is selected.
  11. Select General page.
  12. Enter a password for this user.
  13. Press OK button to close the dialog.
  14. If you refresh the Object Explorer pane, note that user sa no longer has a red downward arrow.

Finally …

After all these steps, you should be able to connect to your SQL Server Express database using JDBC.

2007-04-28

SQLDeveloper Paste Multiple Rows

If you're using SQLDeveloper, you can copy and paste multiple rows into a table in one operation, which makes it easy to load small amounts of data into a database for testing.

  1. Create your tabular data in Excel.
  2. Copy your data into the clipboard.
  3. In SQLDeveloper, open the Data tab of the required table.
  4. Add as many rows as required by repeatedly pressing the Insert Row button.
  5. Paste your data into SQLDeveloper.

If you look in the SQLDeveloper console page, you can see a sequence of INSERT statements followed by a COMMIT statement.

It seems that you can only insert a row if your database server supports transactions, so this trick works for Oracle but not for MySQL.

2007-04-25

JUnit 4

Quick note. JUnit 4.1 is very different from JUnit 3.8. See …

JUnit 4.1 is supported by Eclipse 3.2. Good stuff.

2007-04-22

Windows Cmd Variables and For-loop Command

Strange Syntax for Variable Definition

My quickie script to export Oracle tables defined the for command variable using two percent symbols, %%i, instead of the expected %i% in Windows command shell. Even more strange is that only one % is required when the for command is written interactively in the command shell.

Cmd Variables

To demonstrate and understand how variables are defined in cmd, try the following script in a batch file (i.e. save the commands in a file and run it) and interactively (i.e. type in each command). Note that homedrive is an pre-defined environment variable while i is not defined.

echo %homedrive
echo %i
echo %homedrive%
echo %i%
echo %%homedrive%
echo %%i%
echo %%homedrive%%
echo %%i%%

Here's the results of using a batch script:

> echo homedrive
homedrive
> echo i
i
> echo C:
C:
> echo
ECHO is on.
> echo %homedrive
%homedrive
> echo %i
%i
> echo %homedrive%
%homedrive%
> echo %i%
%i%

Here's what happens when you enter these commands one at a time:

> echo %homedrive
%homedrive
> echo %i
%i
> echo %homedrive%
C:
> echo %i%
%i%
> echo %%homedrive%
%C:
> echo %%i%
%%i%
> echo %%homedrive%%
%C:%
> echo %%i%%
%%i%%

When cmd reads a script in batch mode, it always consumes the leading % for each string as it looks for variables or has to escape a % (see %%i). If a variable is found, that string is always replaced, even if it is not defined (see echo %i%).

On the other hand, when cmd processes a command interactively, it only consumes the %'s in a string when that string is delimited by % (compare echo %homedrive and echo %homedrive%) and if that string maps to a variable name (compare echo %homedrive% and echo %i%). Also, % by itself is treated as a literal "%".

For Command

Back to the for command from the start of this article. In a batch script, a variable, %i, for the for command has to be entered as %%i so that cmd will replace %% with just %. In interactive mode, you only need %i because cmd does not regard this string as a variable. So here's the batch script version of a for command …

for %%i in (a, b, c) do echo %%i

… while here's the interactive version of a for command …

for %i in (a, b, c) do echo %i

The for command defines a variable only if a string starts with %. If you use %i%, then cmd replaces it with a value if i is defined (as expected) and you may encounter some other unexpected error depending on the value of i. However, if i is not defined, for command exits with this message:

%i% was unexpected at this time.

Another unexpected limitation of the for command is that a variable can be only one character long. If you try a variable name that is two or more characters long, you will get this …

for %xy in (a, b, c) do echo %xy
%xy was unexpected at this time.

Windows Cmd Oracle Export

A quickie: I had to dump all the data from an Oracle database and e-mail them to a colleague. Not being familiar with backup and restore for Oracle, I decided to just run exp for each of the tables. To get a list of tables in a schema, I executed this query in DbVisualizer, SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = '<schema>', and saved the output to a file, table.txt. Then in Windows cmd, I wrote this script into a file and ran it:

for /f %%i in (table.txt) do exp <connection string> TABLES=%%i FILE=%%i

2007-04-17

Pasting MS-Word Heading Numbers

When I cut a heading and its heading number from a Microsoft Word document and paste them into an Outlook 2003 HTML message or Wordpad, the heading numbers are always reset. For example, 4.1.1.1 My Heading is pasted as 1.1.1.1 My Heading. When you copy text into the clipboard, it can be copied using multiple formats (text, RTF, metafile, etc.). In this case, the plain text version has the correct heading number but the RTF version does not. Note that you can check the contents of the clipboard using clipbrd.exe.

Following on, I found that Outlook 2003 only allows the user to choose the paste format (Edit / Paste Special menu item) when the message is RTF, not HTML or plain text.

2007-04-14

Asus Touchpad Advanced Settings

When I try to set the touchpad's advanced properties on my work-provided Asus notebook, I get the following error:

An exception occurred while trying to run "C:\WINDOWS\system32\shell32.dll,Control_RunDLL "C:\WINDOWS\system32\main.cpl",Mouse"

This problem still happened after I had installed TouchPad_XP_060220.zip from Asus, which installed the 8.2.0.0 driver. Deep breath … Downloaded and installed the latest Synaptics driver, XP_2K.8.3.4 and now I can configure the touchpad's advanced properties.

It's a bit of a puzzle why the Asus drivers stopped working. I suspect that some of the Windows updates conflict with the Asus drivers. It's a bit hard to narrow down which update, since I don't always fiddle with the touchpad's advanced settings. Microsoft, Synaptics and Asus don't seem to have any information about why this problem occurs.

2007-04-10

Windows Hardlinks with Fsutil

Microsoft Internet Explorer 7 (MSIE7) doesn't open .xhtml files; instead it uses Firefox (probably because Firefox is my default browser). MSIE7 behaves that way regardless of whether I use the Open With context menu item in Explorer, open the file using its file browser or drag-and-drop that file into its window. On the other hand, Opera browser will open an .xhtml file. MSIE7's behaviour is a bit annoying because I go out of my way to test my web pages on different browsers.

I worked around this MSIE7 annoyance using NTFS hardlinks (similar to Unix hardlinks). When you create a file, a hardlink (or name) referring to this file is also created. You can create other hardlinks (or names) that refer to the same file using fsutil utility. A file is only deleted when the last hardlink is deleted.

To create a new hardlink called Name2 for an existing file called Name1 using fsutil, open a command shell and enter: fsutil hardlinks create Name2 Name1.

Windows XP Speech Recognition

I had a play with the Windows XP Speech Recognition service over Easter. Enabling and using this service was less obvious than I expected, so I wrote a short article explaining what I did.

Windows MAC Addresses

There's at least three ways to get MAC addresses for Windows computers:

  • Open Network Connections applet from the Control Panel, Device Manager or run ncpa.cpl, then look at the properties of each network adaptor. This method is rather hit-and-miss and depends on the information provided by the drivers. My Intel PRO/Wireless driver shows the MAC address in the Advanced property sheet but the Realtek NIC driver doesn't show the MAC address at all.
  • In the command shell, enter ipconfig /all. ipconfig provides a lot of networking information and you have to look for the Physical Address field.
  • In the command shell, enter getmac /v. getmac provides just information about network adaptors.

The last method, getmac -v, provides just the required information. If you use the /fo csv option, you can even get the output formatted for a spreadsheet or database.

Why is it useful to know MAC addresses? If you're setting up a wireless router, you can restrict the computers that can use the router by specifying a list of allowed MAC addresses. If your company uses a software license manager such as FlexLM, you may need to provide the MAC address of your users' computers to your software vendor to obtain node-locked licences.

2007-04-04

Drag-and-drop and Alt-Tab

Unexpected but nice feature in Windows. You can grab and start dragging an object with your pointer, then use Alt-Tab to bring the required application window to the foreground where you can drop the object. Previously, I thought both the originating and destination windows had to be visible at the same time on the desktop for a drag-and-drop action. Useful if you usually maximize your work windows on your desktop. For example, if you have your Excel window maximized, then realise you have to insert a TXT data file, you can either locate the file using the file browser dialog or drag-and-drop from Explorer into Excel.

2007-04-01

OpenOffice 2.2 Database Connection

OpenOffice.org has released version 2.2 of their office suite. Seems to be just a bug-fix release. Having installed the latest version, I thought I'd connect the Base product to my test MySQL database. It should have been painless but I kept getting a JDBC driver could not be loaded error. After some head-scratching, it turns out that the MySQL JDBC driver had to be copied into the Java Runtime (JRE) ext folder. Here's how I set up a connection between OpenOffice and MySQL.

  1. Download and extract mysql-connector-java-5.0.4-bin.jar.
  2. Copy mysql-connector-java-5.0.4-bin.jar to C:\Program Files\Java\jre1.5.0_11\lib\ext\ folder.
  3. Start OpenOffice and select Tools / Options menu item, then select Java from the tree pane.
  4. In Java Options pane, select the appropriate JRE and select the OK button.
  5. In OpenOffice, select File / New / Database menu item.
  6. In Select Database step, select Connect to an existing database radio button, select MySQL from the drop down list, then press the Next button.
  7. In Set up MySQL connection step, select Connect using JDBC (Java Database Connectivity) radio button, then press the Next button.
  8. In Set up JDBC connection step, press Test class button. If your connector is copied in the JRE's ext, then the JDBC Driver Test dialog should report The JDBC driver was loaded successfully. If the test fails, it will report The JDBC driver could not be loaded. Close the test dialog and continue configuring your database connection.
  9. Enter the Name of the database (e.g. test), Server URL (e.g. localhost) then press the Next button.
  10. In Set up user authentication step, enter User name (e.g. root) then press Test Connection button. If the connection was unsuccessful, the Connection Test dialog should report Access denied for user 'X' to database 'Y'. Close the test dialog and press the Next button.
  11. In Save and proceed step, use the default options then press the Finish button. OpenOffice will prompt you to save the database connection as an .odb file.

Notes: OpenOffice doesn't find the MySQL driver class after setting the CLASSPATH in the Options dialog. Another bug is that I can't remove a wrong JAR entry from the Options dialog.

Virtual CDs with MagicDisc

Tired of inserting a key disk whenever you want to play a game? Worried that your kids might destroy your CD drive? Or just annoyed with Windows' Autoplay spinning up your CD drive each time you insert a new CD*. One solution: install a virtual CD drive. There's plenty available; I chose MagicDisc because the instructions were straightforward.

*Of course, you can also disable Autoplay. Here's one way:

  1. Run gpedit.msc.
  2. In Group Policy dialog, select Computer Configuration / Administrative Templates / System / Turn off Autoplay.
  3. In Turn off Autoplay dialog, select Enabled radio button.

2007-03-24

Synaptics Touchpad Options

After getting Synaptics virtual scrolling for Firefox, I fiddled with a few more settings. I slowed Coasting down to a minimum so that I can scan web pages and documents by swiping my finger down once the vertical scroll region. For drawing diagrams, I enabled Constrained Motion (the pointer would only move horizontally or vertically when I hold down the Left-Shift key) and Slow Motion (slow pointer to a minimum when I hold down the Left-Control key). These two two options are useful for drawing connection lines with elbows. Finally, I enabled Tap again and hold to drag and Locking Drags (try not to think too hard about the Priscilla, Queen of the Desert) so that I only need one finger to drag and drop objects. This gesture was the hardest to learn. I had to tap then press down on the touch pad for about a second before the Synaptics driver recognises the start of a drag event. If there is too long a delay between the tap and the press, the driver would generate two single click events. Another reason it's hard to learn is that Windows doesn't change the mouse pointer for a drag action until after the mouse pointer starts moving. Finally, if you're making a text selection, the selection action doesn't end until you tap the touchpad while the mouse pointer is within the selection area.

Firefox Find Link Shortcut

It's only when I started reading news articles again that I found Firefox's find as you type link keyboard shortcut (' or apostrophe) more useful than expected. News articles often span several pages and their links have a numeric label ("2" or "3") or "Next" label. If you use plain Find shortcut, your cursor would jump to the first, usually inappropriate, occurrence of that character in the page while find as you type link would move the cursor only to the required link.

2007-03-18

Firefox Adblock and Flashblock

Advertising on web sites is fair enough; media providers want some return on their effort. Sometimes the ads are amusing and they add sparkle to pages. But it becomes seriously, seriously, annoying when a video clip starts playing when a page is loaded. Adblock and Flashblock to the rescue!

Of the two extensions, Flashblock is the simpler to use. It just replaces an embedded Flash movie with a still image. You can still click on that image to play the movie. Adblock doesn't do anything when it's installed. You have to mark individual elements to block or specify a regular expression filter to identify these elements. In theory, Adblock can block all advertisements but have to configure or download your filters while Flashblock doesn't block all Flash movies (sometimes the references are tricked up), so it makes sense to combine the two extensions for control and convenience.

Asus Touchpad Firefox Scrolling

For whatever reason, I've never been able to use my Asus' touchpad to scroll pages in Firefox; when I try virtual scrolling in Firefox, only the mouse pointer changes to a scrollbar. It hadn't particularly annoyed me until I decided to use my touchpad more. The most recent driver from the Asus web site didn't support Firefox but the generic 8.3.4 driver from Synaptics worked.

I didn't realise until I enabled virtual scrolling for Firefox that the feature worked like turning the mouse wheel. For instance, if I hold down the Control key while scrolling vertically, the page's font size changes.

Firefox MAF / MHTML

Last year, I found the MAF extension which allowed us to send an HTML manual to a client. The official MAF extension hasn't been updated for Firefox 2.0 but there's a patch available.

Intel PRO/Wireless 2200BG Constant Authentication

My wireless adaptor software now always prompts me for a password to connect to my router, even though I've already provided a valid password in my profile. Turns out that I had enabled the Cisco Compatible Extensions, probably when I was trying different options to get my wireless adaptor working. It's not obvious that this setting is enabled in the Intel PRO/Wireless client program. Try this:

  1. Start the Intel PRO/Wireless client.
  2. Examine your wireless profile. The client doesn't display the Cisco options if you the Personal Security radio button selected.
  3. Select the Enterprise Security radio button. Now the Enable 802.1x Authentication Type field and Cisco Options ... button appear.
  4. Press the Cisco Options ... button to display the Cisco Compatibility Extensions Options dialog.
  5. Uncheck the Enable Cisco Compatibility Extensions check box.

It would have been more obvious to the user if the extra field and button were always visible but only enabled when Enterprise Security was selected.

Later .... The problem persists, so it's nothing to do with the Cisco Compatibility Extensions. I let Windows manage the wireless adaptor and (ping!) it works. Tried Standby and Restart options for Windows and there's no problems connecting to the wireless router. Could this finally be the end of the wireless annoyances?

2007-03-17

Firefox Link Alert

Link Alert is a little extension that changes the mouse pointer to a different image depending on the link destination. For example, if the link refers to a PDF file, the Adobe PDF document image is shown, and if the link refers to some Javascript code, a little script image is shown. Link Alert doesn't change the pointer for links to other web pages. The extension doesn't seem particularly useful initially but as time wore on, I find that I'm no longer annoyed when I click on a link and some plug-in is started because now I have some advance notice. An application of the principle of least astonishment?

2007-03-12

Floating Floor Room 1

Started laying floating floors in the dining room. We'd removed the skirting boards last weekend because we wanted new ones anyway. The floor boards had click lock joins which didn't need glue and seemed less messy than the usual ones with straight tongue and grove. Fitting the floor boards together required a certain knack of holding the boards together at an angle and pushing them together until the joins met. I crushed some of the joins initially by whacking them with a piece of wood and hammer before getting the knack. Made a mistake of leaving the moulding around the doors and having to hack some of the floor boards around them, which left a ragged edge on the floor boards near the doors. We'll redo those boards next to the door next weekend. Another mistake was not extending the boards halfway through the door frame, so the expansion joint is in the room rather than in the door. Finally, have to be remember which way to cut the board at the end of a column; the boards only join in a one orientation and I wasted one board.

The kids love the new floor. No more splinters from the old floor or bits of filler sticking to the their soles. They skated on their socks all day and played crab ball in the evening.

2007-03-11

Intel PRO/Wireless 2200BG versus Asus HControl.exe

I thought I had solved my wireless networking problems when I decided to let Windows manage my wireless network adaptor. When my Asus notebook computer starts, it establishes a connection. However, when I put Windows on Standby and then Resume my session, Windows still has the same connection problem.

Since there are no newer drivers from Asus, I tried the latest drivers from Intel. They're available from either the PRO/Wireless 2200BG support page or use the Intel support site and enter pro/wireless 2200bg in the search field. Download the PROSet/Wireless Network Connection Software (WIRELESS_TIC_131935_V10.5.2.0_XP32.EXE) which contains the drivers and Windows GUI. After installing it, my wireless adaptor driver was upgraded to version 9.0.4.27. Now I can use my wireless connection after Standby and Resume.

As per course, I rebooted my laptop. Now the Asus HControl.exe could not start. On Asus laptops, users can use keyboard shortcuts to turn off and on features such as the DVD player, display output, speaker volume and (you guessed it) the wireless adaptor. I've fixed this problem previously for another Asus notebook simply by reinstalling the not-so-obvious ATK0100 ACPI Utility. This time, no luck with using the support software for my M6R model. Then I found another version but for the Asus A3 model. Installed it and it works!

2007-03-10

Firefox InfoLister Extension

Firefox InfoLister extension can print your current Firefox version, add-ins and plug-ins. For example, here's my current configuration. It also adds new command about:info which displays the same information. Useful for tracking different Firefox installations and reporting bugs.

22-Oct-2007: Fixed URL as per Ant's comment.

2007-03-04

Firefox and Windows Text-to-speech

Firefox has a Speak It extension which can read aloud web pages or highlighted text. One application is read aloud the news in the background, say from an RSS feed in one tab, and to continue browsing the web in another tab. However, with the limited options available in Speak It, it's too hard to do this in Firefox at the moment.

This extension works in Windows XP, which comes with a default voice for text-to-speech synthesis, known as Microsoft Sam. If you're tired of Sam, you can get two additional voices from Microsoft, Mike and Mary, from the Speech SDK 5.1 page. The SDK also includes a Sample TTS Voice but I can't get it to say more than, "Blah blah blah," in Speak It.

The hassle is that you have to download the 68 Mbyte SpeechSDK51.exe. That SDK page suggests downloading Sp5TTIntXP.exe but this file contains an MSM file that you can't install; developers include MSM files into a software distribution using a development tool such as Visual Studio. Once you've installed the SDK, you can select a different default voice for your computer in the Speech control panel applet.

2007-03-03

Firefox Restart Extension

If you're testing (or simply having fun with) different Firefox add-ins, then it's pretty useful to simply restart Firefox without losing all your tabs. The Restart Firefox extension adds a File / Restart menu item.

2007-02-24

Windows Network Places Rubbish

I was thinking of using SyncBack to synchronize files from an FTP site to my local drive. Problem is that you can't specify an FTP location as the source, only Network Addresses (or UNC paths, the ones that start with a double backslash). What if I create a Network Place? No go, because Explorer treats Network Places differently from Network Addresses. I think FTP Network Places is only a bookmark for MSIE, no different from having a "ftp://blah.com" URI. You can't map a drive to a Network Place without using a program such as NetDrive or WebDrive. However, NetDrive requires a Novell licence. Stone the crows!