April 2006 - Posts

I normally attempt to keep my personal life out of my blog, but this time, I couldn't resist.

I am a theater geek. I love live theater. I go a lot. In a weekend in NYC, I can easily see four (sometimes five, if creative) plays on Broadway. I've done this many times. Can't tell you why I like live theater so much, but I just do.

Currently in Toronto for VSLive. In planning for this event, I noticed that a World Premiere production of a new musical named (for the obvious reasons) Lord the Rings would be playing during my visit. I knew that friend and co-speaker Paul D. Sheriff would also be in town for the conference (and he's read the eponymous books several times, most recently in the past few years), like theater as well, and might enjoy the experience.

So, last night we ventured out on a "man-date". Delicious dinner at N'awlins, near the theater. We should have stayed.

Turns out the show is 210 minutes long. That's 3 1/2 hours. That's very, very long. Two intermissions. Had I bothered to look at the tickets, I would have noticed that the show didn't start at the industry standard 8PM, but instead, at the "we have a really really long show here so let's start early" time of 7:30. We arrived at 7:29, to discover that the show started in one minute. Barely made it.

But what splendors we were privy to once we made it into the theater. Short people warbling offkey. Terrible, really really terrible acting. A script that at best was amateurish, at worst completely incomprehensible. Music all over the map, from chant to pop bellowing to Irish jig. Did I mention the acting? They carted in a professional "Broadway" actor (Brent Carver) to play Gandalf. Paul and I agreed: it seemed that he was so ready for someone, anyone, to relieve him from this torture that he wasn't even trying to act. He was, in fact, by far the worst actor in the show, and that's saying something. I expected, several times, to see him walk to the edge of the stage and shout "SAVE ME. PLEASE!" Contracts are contracts.

Let's be clear: The sets and the stage are amazing. They put ALL their money into these technical issues. Clearly very little went into the actors, the script, or the music. Several times Paul and I were both awed by the visuals.

And long. So long. Fifteen minutes into a twenty minute loud pop ballad about Lothlorien (in which the word "Lothlorien" was just about the only lyric), by a gold-faced singer with what appeared to be the business end of a sprouting potato on her head, I found myself rolling my eyes, and nearly shouting "OK, let's move this along."

Listen. You don't have to agree, but I find theater productions in which actors are completely interchangeable (that is, it was not only impossible to tell Pippin from Merry, as actors, and the direction is all about traffic management, not acting) to be skippable. "Beauty and the Beast" for example, fits into this "high school musical" category. LOTR takes this to extremes. Except for the gentleman who portrayed Gollum/Smeagle, any actor could have been any character. They might have switched parts during the show, and I wouldn't have known. The only distinguishing factor, as far as I could tell, was height.

Finally, the show is COMPLETELY INCOMPREHENSIBLE. I can't tell you how many times I found myself turning to Paul and whispering "WTF?" (spelled out, of course). The best (or worst) was at the end, when Frodo is trying to cast the Ring back into its source. Rather than bothering to stage a fight between Gollum and Frodo, or give the impression that Frodo was struggling, or anything at all, you saw Frodo standing above an area with red lighting, Gollum does something (not quite sure what), the lights black out, and it's over. Frodo and Sam are now lying on the stage, apparently tired. From something. Perhaps from surviving the previous 200 minutes of running around the stage. (And man, do they run around the stage a lot. In figure 8s, in circles, in straight lines. I was exhausted just watching.)

I'd go on, except that it doesn't serve a purpose. I've said enough. But amazingly, we both stayed awake (the first moment of the third act scared the heck out of us--took a few minutes off my life) and also amazingly, agreed that we enjoyed it. But it was a guilty pleasure. It's clearly just terrible. Bad acting, bad music, bad script, bad direction (think traffic cop). I pity anyone seeing this who hadn't read the books, or at least seen the movies. Are there such people who would pay over $100 to see this dreck?

If you must go, go warned. It's really bad. But fun bad.

Posted by KenG | 1 comment(s)

As long as I'm at it, links to recent articles I've written for MSDN Magazine:

Advanced Basics: Set Word Document Properties Programmatically

At the beginning of another lovely day of writing courseware in mad pursuit of unrealistic deadlines, I received a frantic call from a business partner. He was at the end of a long consulting project and had several hundred Microsoft® Word documents, all of which required their document properties to be set identically, except the Title property of the document, which was to be based on the document file name, minus the .doc extension. He didn't have time to set all the properties manually. Could I write a little utility to do the work for him? (Read the rest...)

Advanced Basics: What's My IP Address?

If you're like me, you regularly do tech-support for family, friends, and neighbors. You can't go to a party without hearing the familiar refrain: "I've just got a quick question." It's always something—their Internet connections get dropped, they've got a virus, they can't install some piece of hardware, or some file has gone missing.

Maybe you support your brother or your grandmother. In my case, I spend a good deal of time helping out my father, who is astoundingly agile with his computer considering he didn't pick it up until he was 65. When he has trouble, it would be convenient if I could use Remote Desktop Connection to connect into his computer and see what's going on. Of course, connecting to his computer would require that I know his IP address. That would be simple if he wasn't using Network Address Translation (NAT) behind a firewall, and if he didn't get a dynamic IP address from his ISP. I could have him run a command prompt, get his IP address, and read it to me, but that address would be a useless internal address. I could have him browse to one of the many sites that reports the external (outward-facing) IP address, such as www.WhatIsMyIP.com, and have him read me the address. But attempts to perform this exercise have been frustrating, resulting in misplaced digits, missing dots, and incorrect addresses.

It would be best if he could click a button and have his IP address e-mailed to me. That's the goal of this column. (Of course, one could also use Remote Assistance to make the process easier, but that wouldn't make for a very interesting column, now would it)? Follow along as I create a Windows®-based application that retrieves the IP address of a remote machine and e-mails it to the appropriate mailbox. Along the way, you'll learn a few handy bits of information about ASP.NET Server variables. If you're already using ASP.NET, you should be comfortable with this information, but I'll bet anyone moving from Visual Basic® 6.0 to Visual Basic .NET isn't familiar with the information that's available to a Web site. (Read the rest...)

Advanced Basics: Revisiting Operator Overloading

On the conference circuit recently, I was speaking about some of my favorite new features in the Microsoft® .NET Framework 2.0, using the content of three recent columns in this series as fodder. In my talk I sped through generics, operator overloading, and the BackgroundWorker component, all in the space of an hour (for the columns, see Advanced Basics: Being Generic Ain't So Bad, Advanced Basics: Calling All Operators, and Advanced Basics: Doing Async the Easy Way). This accomplishment requires speed talking, but over the years I've mastered that skill. Of course, a downloadable"fast conference speaker" codec would help listeners keep up, but alas, that may never happen.

After the session was over, one attendee came up to chat. He suggested that my example for operator overloading was a bit too academic. It showed how to create a class named Vector, which contained a one-dimensional array of integers. In that example, you can overload the +, =, <>, and CType operators in order to manipulate the contents of the vector instances. He then suggested an example that is also a bit esoteric, but it presented a perfect opportunity to revisit operator overloading: using the RGBA and HSV standards for handling colors. (Read the rest...)

Advanced Basics: Doing Async the Easy Way

If you've been following Ted Pattison's excellent series of Basic Instincts columns on multithreading and asynchronous behavior, you should by now be an expert on handling the issues involved in working with multiple threads in Windows®-based apps. Recently, a friend, who evidently hadn't read Ted's columns, sent me a Windows-based application that performed a very lengthy file search operation that locked the user interface while executing. Users couldn't interact with, move, or resize the form while the main activity was running, so they weren't happy. He wanted to know what he could do to make the app more responsive. The answer, of course, is to perform the work in a background thread.

I recommended Ted's series of columns to my friend, but couldn't resist fixing things up on my own in the meantime. The application and how you can safely perform a background operation with as little code as possible—both in Visual Basic® .NET 2003 and in Visual Basic 2005—is this month's topic. (Read the rest...)

Posted by KenG | with no comments
Filed under:

At a recent user group meeting in Princeton, NJ (thanks, INETA), an attendee looked up my blog while I was talking, and noted that I was a terrible blogger. It's true.

My excuse was that I write a lot of words each month for various publications, and courseware, and there aren't many words left for a blog.

The attendee's suggestion was (the obvious) that I at least post links to articles as they happen. I suppose I can do that...

I'll try. In the interest of getting started, here are links to the last four columns for CoDe Magazine:

Try and Ye Shall Succeed: In which the author tries to live out a life-long dream (finding otherwise unused and unknown space in a new house) and explores the simple, but useful, TryParse method.

Moving and Downsizing: In which the author describes the trials that finally (OK, it was only 9 months) drives him from condo living, and describes a technique for shrinking huge SQL Express databases used by ASP.NET

My Not-So-Evil Twin: In which the author exposes the horror of cloning (well, really, a neophyte with the same name, posing public newsgroup questions the author would otherwise not ask in public) and somehow manages to twist this into a discussion of cloning using serialization. It's a stretch.

What Does That Beep Mean?: In which the author argues with tax collectors (never a good idea) over the meaning of the dreaded error noise in Windows, and exposes techniques for playing sounds using the .NET Framework 2.0.

 

 

Posted by KenG | 2 comment(s)

I grew up in a world in which you could only set reference types to Nothing. Attempting to set an integer to Nothing would cause a compile-time error. Not so, in Visual Basic 2005 (and maybe 2003 -- I never tried it). I never tried it 'cause, well, I assumed it would fail. On the other hand, when you set an integer to Nothing like this:

Dim i as Integer = Nothing
Debug.WriteLine(i.ToString())

what's actually happening is that you're assigning 0 to i. Not only does running the code here produce 0 in its output, but if you look at the IL for the code, in ILDASM, what you find is this:

  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  ldloca.s   i
  IL_0005:  call       instance string [mscorlib]System.Int32::ToString()
  IL_000a:  call       void [System]System.Diagnostics.Debug::WriteLine(string)

The first line of code creates a literal value 0, which gets assigned into the integer. I changed the code so that it explicitly assigned 0 to the integer, and the IL was exactly the same.

It turns out that assigning Nothing to a value type simply assigns the type's default value. I thank Herfried K. Wagner for this tip, and Jaoa Cardoso for the reference to the corresponding documentation, which states this fact explicitly. I had certainly missed it. Perhaps you had too.

Posted by KenG | with no comments
Filed under:

I travel a lot, and I need to know what the weather's going to be. I love having up-to-date weather information, and I want it to be unobstrusive, easy-to-use, and accurate.

I've been using Weather Watcher from Singer's Creations, and can't possibly say enough good things about this little app. It's ad-free, does exactly what I want, and is free. Mike Singer is happy to take contributions for his work, and it's well worth it.

Bottom line: If you want weather information at your fingertips, but not in your way, try out Weather Watcher.

Posted by KenG | with no comments

At the suggestion of my pal Russ Nemhauser, I recently found myself investigating predicates in relation to the Array and List classes in the .NET Framework 2.0. Along the way, I dug a little deeper into sorting arrays, and found a new feature which can save you a bunch of coding.

Imagine that you've got an array of objects, and you want to sort if by one of the properties of the objects. For example, imagine that you've written the following code:

[VB]
Dim di As New DirectoryInfo("C:\")
Dim files() As FileInfo = di.GetFiles("*.*")

[C#]
DirectoryInfo di = new DirectoryInfo(@"C:\");
FileInfo[] files = di.GetFiles("*.*");

Calling the Array.Sort method for this array gets you an InvalidOperationException, because the Array class has no idea on which property you want it to sort. In previous versions of the .NET Framework, you could solve the problem by creating a class that implements System.Collections.IComparer. In this class, the Compare procedure handles the logic that determines how to sort the information, like this code, which sorts the data based on the FullName property:

[VB]
Public Class CompareNames
  Implements IComparer

  Public Function Compare( _
   ByVal x As Object, ByVal y As Object) _
   As Integer _
   Implements System.Collections.IComparer.Compare

    Dim file1 As FileInfo = CType(x, FileInfo)
    Dim file2 As FileInfo = CType(y, FileInfo)

    Return file1.FullName.CompareTo(file2.FullName)
  End Function
End Class

[C#]
public class CompareNames :
  System.Collections.IComparer
{
  public int Compare(object x, object y)
  {
    FileInfo file1 = (FileInfo)x;
    FileInfo file2 = (FileInfo)y;

    return file1.FullName.CompareTo(file2.FullName);
  }
}

To perform the sort, you can write code like this:

[VB]
Dim comparer1 As New CompareNames()
Array.Sort(files, comparer1)

[C#]
CompareNames comparer1 = new CompareNames();
Array.Sort(files, comparer1);

If you then wanted to sort by CreationTime, for example, you would need to create another class, providing a different comparer. What a pain! Not only must you create a new class for each sort order, you must perform the same conversions each time, because the IComparer.Compare method passes Object references, not strongly typed values.

In the .NET Framework 2.0, you have a number of options using generics, but the simplest is to take advantage of the Array.Sort overload that's defined like this:

[VB]
Public Shared Sub Sort(Of T)(array As T(), comparison As Comparison(Of T))

[C#]
Public static void Sort<T>(T[] array, Comparison<T> comparison)

In this overload, you supply a generic array of a particular type, along with a procedure of the appropriate delegate type. The delegate type looks like this:

[VB]
Public Delegate Function Comparison(Of T) (x As T, y As T) As Integer

[C#]
public delegate int Comparison<T> (T x, T y)

All you need to do is create a procedure, like this one, that compares two objects of the same type:

[VB]
Private Function CompareFileNames( _
 ByVal f1 As FileInfo, ByVal f2 As FileInfo) As Integer
  Return f1.FullName.CompareTo(f2.FullName)
End Function

[C#]
public int CompareFileNames(FileInfo f1, FileInfo f2)
{
  return f1.FullName.CompareTo(f2.FullName);
}

It's a lot simpler than creating a new class for each sort, and performing conversions for two objects each time. Given the CompareFileNames procedure, you could perform the sort using code like this:

[VB]
Array.Sort(files, AddressOf CompareFileNames)

[C#]
Array.Sort(files, CompareFileNames);

If you then wanted to sort by CreationTime, you could supply this procedure:

[VB]
Private Function CompareDates( _
 ByVal f1 As FileInfo, ByVal f2 As FileInfo) As Integer
  Return f1.CreationTime.CompareTo(f2.CreationTime)
End Function

[C#]
public int CompareDates(FileInfo fi, FileInfo f2)
{
  return f1.CreationTime.CompareTo(f2.CreationTime);
}

You could sort by CreationDate using this code:

[VB]
Array.Sort(files, AddressOf CompareDates)

[C#]
Array.Sort(files, CompareDates);

I love it. This technique takes the sting out of sorting data structures in multiple ways.

Posted by KenG | 2 comment(s)