Saturday, November 11, 2006

A Comedy of Errors

This is one long, tragic story.

I own a Pavillion m7480n multimedia PC, which I purchased around April of 2006. I purchased it at CompUSA, in an emergency, because I was in the middle of a serious project deadline and my personal computer at home died in a violent, flaming blaze of glory. I needed a computer fast. One that was fast, with a lot of memory and disk space. I had a limited budget, but wanted one that was going to last, and suited my needs as a software developer and a gaming enthusiast. The Pavillion multimedia PC seemed like a good choice at the time, and it has been, up until quite recently.

I keep a clean machine, in an effort to get the best performance out of it. I removed all the additional games and Norton Internet Security once the OS was installed. Believe me, these HP machines come with a lot of junk. Norton Internet Security has got to be one of the biggest pieces of junk in the galaxy. We're talking about a piece of software that reduces a 3GHz processor equipped with 2GB of RAM to a crawl. Combine that with the fact that the machine ships with about 30 games, two personal finance packages (both MS Money and Quicken), a trial version of Microsoft Office, tons of media software, extra theming software, and all kinds of excess crap to give you the oohs and ahhs, and you've turned what should be a screaming machine into a clay tablet and a stylus. And let's not forget that HP ships with its own updating software, which is highly intrusive.

They also don't provide you with recovery discs, or original Windows discs. You have to create your own recovery CDs off of a hidden partition on the hard disk, and even that doesn't create genuine Windows CDs for you. If you perform a recovery from those CDs, you get the whole shebang or nothing. Which means removing the whole assortment of useless and system-degrading software all over again. Out of the box, the machine has well in excess of 40 processes running, and the mouse often stutters as you drag it across the screen.

Needless to say, I do a lot of cleanup when I first get a machine. I remove the software I don't use. Especially tons of little kiddie games. Personal financial software goes, internet service offers, Office trials, Quicken & Money go, and the dreaded Norton Internet Insecurity. Once it's cleaned, I shut down non-essential services, and defragment the hard disk, making sure it's clean. I keep a clean, fast machine. By the time I'm done, I'll have, on average, somewhere around 27 to 30 processes running, very low memory consumption, and a blazingly fast machine. Which is what one would expect. I sit behind a hardware firewall, and keep the machine clean of viruses, adware, and spyware as well. Regular maintenance on the machine keeps it in tip-top shape.

Everything was going fine, and the machine was a gem for about 6 months. One morning, I was browsing the Internet, reading the news, as is my normal habit, and everything was peachy. When I came home that night, I moved the mouse, and suddenly the machine froze.

The behavior was peculiar. You could move the mouse, and the selection rectangle would appear, but it would not respond to mouse clicks. In addition, it wouldn't respond to keyboard input. I rebooted the machine, and everything came up just fine. But after about 30 minutes of use, it happened again. Reboot and it happened again, after about 10 minutes. Reboot and it happened again, about 5 minutes later. You get the picture.

Eventually, it got to where the machine would lock within minutes or seconds of full system startup. Once Windows was loaded, the system would freeze. I could bring up the Task Manager and see the processes list; you could watch as the CPU cycles for each process and their memory consumption remained active right up until the keyboard and mouse "went dead." The CPU consumption and memory usage just froze.

Now, obviously the USB port hadn't died--the mouse was still working. Swapped it out to prove it. And the CPU was working just fine. The system works beautifully in safe mode, with a USB keyboard and mouse. I even swapped in a PS/2 mouse and keyboard, and the same issue occurred. Something else was happening.

I was fairly stumped. Being a software professional with 20 years experience, that was fairly hard to admit. So, in an act of total humility, I packaged it up and lugged it to CompUSA, where I had purchased the machine. I had paid for the warranty, so I figured I might as well let someone else wrack their brains over it. It was probably something simple, anyway, and I was just overlooking it. So I explained what I had done, what I knew, and gave them my number, telling them to call me if they needed any further information.

What a mistake that was. After telling me it would take three days, they held the machine for three weeks. I never got a call. I had to call them and ask them about the status and whereabouts of my machine. At one point, a week into the ordeal, they said it was in process, and should be ready in a day or so. Obviously, that didn't turn out to be true. When they finally called to tell me it was ready, the technician told me that my problem was that I was running too many processes.

You can imagine my immediate reaction. I told him, not so subtly, that he was blowing air out of his ass. There was no possible way that that was the problem. It was running the same number of processes that it had been running since I had originally configured the machine 6 months earlier. No new applications had been installed. That could not possibly have been the problem. He argued with me about it for twenty minutes. Finally, I told him that he should just pack up the machine and I would take it to the Geek Squad for a second opinion, and I'd get my money back from CompUSA since they had not resolved the issue. He said, "Well you can take it there, but I used to work there, and they'll tell you the same thing."

Personally, I was relieved he didn't work there anymore. It bolstered my confidence in getting a more intelligent response from Geek Squad.

Unfortunately, I never got to take it to Geek Squad. The situation got worse when I arrived at CompUSA. I contacted the store manager and explained the situation to him, and told him why I wanted my money back. Naturally, the store manager has no technical savvy whatsoever. So he brought in the manager of the technical repairs department. And he grabbed the kid that had "repaired" my machine. They asked the kid to explain what he had done.

This kid had the audacity to speak to me like I was an idiot. He began his diatribe by trying to explain to me the difference between hardware and software--badly. He spoke slowly, softly, like I was a four year old, and he was trying to explain why you don't touch a hot burner on the stove. I stopped him immediately and disabused him of the notion that I had no technical knowledge.

I explained to him that I know the difference between a hardware interrupt and a software interrupt. Then I asked him a series of questions. Did you scan the hard disk for errors? Is it properly defragmented? Did you perform memory diagnostics? Are all the interrupts working properly? Did you check for hardware conflicts? Did you perform a virus scan? Is it free of spyware and adware? Did you ensure that all the device drivers were up to date? To each question, he nodded slowly and answered, "Yeah."

Then, he explained to me that the machine worked fine in safe mode and that they concluded that it was not a hardware problem. That was fine. I could accept that. But then, to my utter horror, he said that they had performed a system restore from the hidden recovery partition on my machine.

"You did a what?"

He went on a rapid speech then about why they did it and that restoring the system seemed to have solved the problem. He claimed it was a nondestructive restore. I was flabbergasted. I told them to bring the machine out, set it up, and show me. They did so, and when it booted, you could see all the original software reinstalled, and the applications that I used for work were no longer installed. I asked them to bring up task manager, and count the number of running processes. It was, of course higher than the number that had previously been running. More importantly, I noticed that the additional user accounts that had been on the machine were gone, and so was their data!

"You lost my data."

They insisted it was still there. I was irate at this point. I wanted to know why no one had called me to ask any questions. They'd had my number for the full three weeks, and no one had called me to ask any questions before they'd taken this catastrophic step. The kid stammered for a second before I looked at him and said, "Look, I'm in software development, and I have to be in constant contact with my customer to do my job. How can you do your job without calling the customer? You had my number for three weeks. Why didn't you call me before you made this kind of a decision?" He couldn't answer the question.

Then I asked him if he had bothered to check the graphics driver before he did the restore. I reminded him that in safe mode, Windows uses a default, safe 640x480 graphics driver--not the 3rd party driver that you may have installed. He sidestepped the question, but I was so ticked off now that I grilled him. "Did you check the graphics driver or not?" He sidestepped three or four times before I made him answer it. "Just answer the question. Did you check that before you wiped my machine or not? It's a yes or no question." He finally admitted that he did not check the graphics driver to see if it was causing the problem. "So there it is. You didn't do your job. You erased my hard disk, lost my data, and didn't solve this problem. I can guarantee you that it will reoccur. I want my money back."

The store manager disagreed, believing that they had solved the problem. He settled for half the fee back. I counted my losses at that point, because I was ready to do violence. I packed up the machine, and left.

(I immediately went down the street to Best Buy and picked up a new laptop for $1300 bucks. I've sworn off of CompUSA. Their arrogance, lack of thoroughness, and the way they handled that whole ordeal has led me to boycott them. I've always had bad experiences with them in the past, but this was simply the last straw. It's sad that they happen to be the only place you can buy Apple computers in my neck of the woods.)

Two days later, the problem came back.

I restored the system again, hoping it might solve the problem. I wasn't sure at this point if they'd done a full restore or not. This time, I wanted to be sure. Within days, it reoccurred.

So there I was, sitting at home with a machine that didn't work. In the meanwhile, I borrowed a laptop from the office so I could continue to work from home, and used it to research the issue. I couldn't find anyone on the Internet reporting a similar issue. Frustrated, I described the problem to the CIO and IT tech at work. Their first guess had been a bad USB controller; that was obviously not the problem. All the USB devices worked fine in safe mode. So they invited me to bring in the machine and let them take a look at it.

At first, the were boggled by the problem itself. They'd never seen anything like it. I described the problem to them again to refresh their memory, then explained how they could get the task manager up and watch the processes freeze. Sure enough, there it was. The mouse cursor would move, and the selection rectangle would sometimes appear, but you couldn't click on anything, and the system didn't respond to the keyboard. The only thing you could do was reset it via the power button. (And I abhor shutting the machine down that way.)

They looked at it for a day and a half. Finally, using MSCONFIG and process of elimination, they were able to pin down the culprit: Windows Update. Essentially, they disabled every service and startup program, and then rebooted the machine. Then they brought each one online, and waited to see if the problem would reappear. It only reappeared if you turned on Windows Update. And it reappeared every time you turned on Windows Update.

Well, that fairly sucked.

I need Windows Update. I have to be able to get critical security updates for the system. So I had to do something. We created the recovery CDs, and embarked on a plan. We were going to fully reinstall the system, and see if it worked.

On the way home, I go another brainy idea. What if I replaced Windows XP Media Center Edition with Windows XP Professional? After all, I don't need all that extra multimedia stuff. I would remove the multimedia components from the box, and install XP Pro on it. Then I'd have a clean install, without all the crap that ships on the recovery CDs. And it should work. My theory is that the problem is with something that's shipping on those Recovery CDs, or something that HP Update is pushing onto the machines, and breaking Windows Update. Ultimately, Windows Update has to work. 

So I stopped by Staples, forked over $300 bucks for a Windows XP box, and went home to set up the machine.

To my chagrin, the software won't accept the product keys when I run Setup from inside the existing Windows session. Bummer. I call Microsoft, they get me a new CD key, and those don't work either. I call Microsoft back. Apparently, I have to boot from the CD to install, because even if you specify full installation, Windows will treat it as an upgrade if you launch Setup from within an existing Windows session, and you cannot upgrade Media Center Edition to Professional Edition. (Apparently, that would be a downgrade, which is why the keys are invalid.)

So I try to run Setup from the disk, booting the machine from the CD. Lo and behold--XP needs SATA RAID drivers. I don't have them, and HP won't provide them for use with Windows XP Pro. Isn't that nice? Further, when you install 3rd party RAID drivers in Windows XP, it expects to get them from a floppy drive. I don't know if Microsoft has noticed lately, but computers these days don't have floppy disk drives. My mom's machine has one, but that's only because it's a number of years old, and we slapped it in because we had one on hand and wanted to close an open slot on the front of her machine that was letting dust into the chassis. (And no, I didn't have a bay cover on hand.) The Microsoft tech support guy on the line had the nerve to tell me to buy one.

"You're telling me I have to buy additonal hardware to install your operating system?"

"Well, no."

"That's good. Because this machine doesn't have anywhere to put one. Now how the heck am I supposed to install a RAID driver on this machine?"

So Microsoft decides that the answer is to bring in HP in a 3-way conference call. So they put me on perpetual hold, and finally connect HP. Just as HP is answering the line, my wireless provider (you know, the most reliable wireless network in the country) drops the call. I chalk it up, and wait for Microsoft to call me back. They took my number. They'll do that. Right?

Foolish me.

So I call them back. I get someone else. Back to square one. They reach the same conclusion. Back on perpetual hold. Get the HP individual on the line this time. I actually hear voices! Yay, a possible resolution is coming. Verizon drops the call again. Now I'm getting ticked. It's midnight, and that's twice. I wait again. No return call from Microsoft.

I call them back. I give the case number again. This time I explain in detail that the customer support individual could do a lot of good if he would have called me back. Now I'm upset, and have to spend another half-hour going through the whole thing again, even though it's supposedly all logged in the case file. And you never get the same tech support guy. Never. Ever. But I make it clear that I know it's not his fault, because he's not the guy who didn't call me back. So he goes through the same process, dial up HP, and starts the conference. Again, call dropped. Three times. And does he call me back? Hell no.

Note to Microsoft: If you want to provide better customer service, listen to your customers. Wireless companies drop calls. Call the customer back if they suddenly disappear from the line. We didn't wait all that time in line, trying to get our problem resolved, and then just hang up on you. Especially if we've been really polite and helpful. Call us back. What you guys did to me last night, three times in a row, was shameful, and only irritated me to the point where I wanted to curse you up and down. Your customer service department needs a serious upbraiding for that type of behavior. You guys are supposed to pride yourself on excellent customer care. How long does it take to call the customer back and find out if the call was dropped? Now you have to deal with bad PR. And I'm not doing this because I'm ticked. I'm just saying it was something you could have done better, and should be doing better. Making us call back into the queue wastes our time, makes us angry and short-tempered, and makes for a really tough experience for your customer support folks. I know that the phone support folks I talk to really don't like to deal with irate customers. Being proactive about things as simple as this can save you time and money, since it will reduce the stress for both the customer and your phone support personnel. Isn't that worth it?

At this point, I'm fed up with dropped calls. I swap out one of the DVD drives with a 300GB Maxtor drive I've got sitting around, and boot off of the Windows CD. Works like a charm. Setup installs Windows onto the machine and it's screaming. Only problem: I have a couple of devices that Windows can't identify:

  • Ethernet Controller
  • PCI Controller
  • RAID Controller
  • Unknown Device

So I log onto the HP Support site and explain my problem in a chat site. The tech support person there is very helpful and provides me the links to the drivers I need. Apparently,these devices are integrated into the motherboard, and upgrading the motherboard drivers will automatically install the appropriate drivers. So I download the drivers on my laptop, burn a CD, upload them onto the PC, and then install them.

No dice.

The devices are still not identified. I've figured out that the unknown device must be my sound card, since there's no sound on the machine, and there's no audio device in the Sounds control panel.

I contact HP support again. I explain the problem to them, letting them know that the motherboard device drivers didn't work. Suddenly I'm getting a different story. Now, according to HP, you can't install XP Pro on a Pavillion m7480n. Or, rather, you're not allowed to. What you're getting, when you buy this machine, is an OEM version of Windows XP Media Center Edition--it's been modified for their machine. They won't support any other version of Windows on it, and that's why you can't find the CDs for it or the individual device drivers. They want to ship me a new set of recovery CDs. I reiterate that re-running the Recovery CDs will not solve my original problem. Windows Update will continue to make the system freeze. They say, repeatedly, that they are certain that it will fix the problem. I am laughing inside, and bashing my head against the desk.

How thick-headed can you be? I have described the problem to you repeatedly. I have told you the steps that were taken. Reinstalling from the recovery CDs does not resolve the issue. The system is now quite stable under XP Pro, except for the simple fact that I can't hit the Internet. And now you want me to return the machine to its previous unstable state? How can you ask me to do that?

So, here I am. I have a $300 copy of Windows XP Pro, and I can't connect to the Internet. Other than that, the machine is screaming. I can't get Windows to identify the Ethernet controller or the RAID controller, so this big-ass hard disk in the machine is just useless. And I've got no sound.

So what have I learned?

  1. Never buy a prebuilt machine again. I'll build my own in the future.
  2. Never buy from CompUSA again.
  3. Never, ever trust your computer to the technical support staff at a computer store. They will make potentially catastrophic decisions about your computer without consulting you, and once those decisions are made, you have little or no recourse. I lost the notes for a novel I've been working on for twenty years. Thank God for backups. But I did lose the last six months of revisions. (Shame on me.)
  4. Don't use a cell-phone to conduct technical support calls. Use a land-line, or use a chat service. Chat services are better, because some of them will send you the transcript via email.
  5. Don't expect technical support staff to call you back, even though they take your number. That's largely just lip-service to give you a warm and fuzzy feeling.

And what am I doing now?

The only thing I can reasonably do. I'm actually going to try reinstalling, one more time, from those damned recovery CDs. Eventually, something has to give. Even if it means forking over more money for an extended warranty and shipping the whole kit and kaboodle back to HP for service.

It just amazes me that I have to go through all this crap in the first place. It's been a serious comedy of errors. 

Addendum: Reapplying the OS from the recovery CDs worked. I'm writing this blog update from the freshly repaired machine. But I made it a point to not apply any of the HP updates, since I suspect that one of their automatic updates is what hosed the machine. I'll only install critical updates from Microsoft in the future. So far, it's running nice and fast.

But I'm still waiting nervously for something to go wrong. Call me a skeptic.

Getting a Grip on Structured Exception Handling: Part 2

In Part 1 of this series, we took a look at how errors are handled in Visual Basic using On Error. We saw that it has some architectural problems, it doesn't promote developer discipline, it tends to result in code that's difficult to read and performs less than optimally, and that despite all of this it works.

I can't stress enough that if your code base is working, you should leave it alone. Learning a new technology will likely get you all fired up to use it. Don't whet your appetite on a functioning and stable code base. If you're that excited, create a test project and learn it there. In the name of all that's holy, don't break perfectly good systems just to test your knowledge.

In Part 2 of this series, we'll learn about structured exception handling (SEH): what it is, how it works, and why it's good for you. There are compelling reasons that object-oriented languages use it instead of passing around error numbers and returning error codes from functions.

What is Structured Exception Handling?


Structured exception handling is, in essence, a fancy term for "error handling." An exception is an error. Specifically, it's any condition that prevents your code from doing its work. For example, you might attempt to open a file and discover that it doesn't exist, or that you don't permissions to do so. These conditions would result in exceptions, since they would prevent your application from working.

Some conditions, however, are recoverable. In an attempt to delete a file, you may discover that the file doesn't exist. Here, the file's absence is fine; the user was going to delete it anyway, so you can ignore the exception, and move along.

So far, everything sounds familiar, and it should. It's what you've been doing all along. Only the names have been changed to protect the innocent.

However, structured exception handling differs from using On Error in that it's object-oriented. It uses objects to encapsulate error information. It establishes a clean, predictable behavior for your application when an exception occurs, and allows you to nest error handlers within the same method (something that was rather difficult to do with On Error). It also provides a mechanism for guaranteeing that certain code executes before your method loses control when an exception occurs, allowing you to clean up your resources, resulting in fewer orphaned database connections, file streams, and system resources.

Exceptions aren't raised, they are thrown, and exception handlers catch them. Hence, your exception handler tries to do something, and if your code throws an exception at you, you catch the exception and attempt to handle it. That's the big scary picture of SEH.

The keywords used in SEH are Try, Catch, Finally, End Try, and Throw. In a nutshell, you wrap your code in a Try...End Try block. You catch any exceptions in one or more intervening Catch blocks, and you place your cleanup code in a Finally block. For example:

Protected Overridable Function ReadFile(ByVal sourceFile As String) As String

Dim reader As StreamReader
Dim buffer As String

Try
reader
= New StreamReader(sourceFile)
buffer
= reader.ReadToEnd()
Catch ex As FileNotFoundException
Debug.WriteLine(
"File not found:" & sourceFile)
Throw
Catch ex As PermissionDeniedException
Debug.WriteLine(
"Permission denied:" & sourceFile)
Throw
Catch ex As Exception
Debug.WriteLine(ex.ToString())
Throw
Finally
Disposer.DisposeOf(reader)
End Try

Return buffer

End Function


When an exception occurs within the Try block, the run time scans the list of Catch blocks from top to bottom, searching for a Catch block that accepts an exception that most closely matches the type of the currently thrown exception. If it finds one, the runtime transfers control of the application to that block. If one can't be found, control is passed to the Finally block, if one exists. Your Finally block gets the opportunity to clean up resources prior to the routine losing control.

If the runtime found a Catch block that handled the exception, the Catch block can do any number of things with it. It may quietly consume it, and then continue processing. Or it, may rethrow the exception. In that event, control of the application is passed to the Finally block, as if no handling Catch block were found. The Catch block may throw a new exception. In that event, the Finally block is called, and then control is passed back up the call stack until a suitable exception handler is found.

In the event that no suitable exception handlers are found, a message is either displayed or recorded in the Windows event log (depending on the nature of the application), and the application terminates.


Try Blocks

Place any code that could throw an exception inside the Try block (that's the portion between the Try keyword and the first Catch statement). The .NET Framework documents the exceptions that it will throw, so you should have a very good idea of what exceptions you should be ready to catch, and what statements need to be placed inside the Try block.

Catch Blocks

Exceptions are handled in the Catch block. The catch block always takes exactly one parameter, and it must be an exception. You may define multiple Catch blocks; when doing so, always put the more specific exception types at the top, and the least specific type (System.Exception) at the bottom. (The reason for doing this may not be readily apparent: if you put System.Exception any where else, anything below it will be ignored, because all exceptions are derived from System.Exception. The runtime evaluates exception types from top to bottom; once it finds a match, it will stop looking any further. So remember to put the generic exception at the bottom, as a catch-all. Better yet, if you don't know what to do with it, omit it altogether, and let the caller handle it.)

In general, you do not want to place any statements inside the Catch block if those statements can throw exceptions themselves. If they can, wrap those statements in exception handlers and handle them accordingly.

You are not required to provide any Catch blocks at all. In that event, you must provide a Finally block. This situation is desirable when you want the code in the Finally block to execute even if an exception occurs, but you don't want to handle any of the exceptions that you'll encounter in your method.

Finally Blocks

The last block you may include in a Try...End Try block is the Finally block. You may only include one Finally block. Its contents are executed after your Catch block code has executed, and before control is transferred out of the exception handler. It's your last chance to do anything before your method loses control.

Typically, the code in the Finally block rolls back transactions, closes open files, and cleans up disposable resources. As with a Catch block, it shouldn't invoke methods that can throw exceptions unless it wraps those statements in exception handlers and handles the exceptions.

Throw

To throw an exception, you use the Throw keyword, like this:


Throw New ArgumentNullException("value")


Throw always takes an exception object as its parameter. That's it. As we'll see in a later article in this series, you can create your own custom exceptions, and you can attach additional properties to them (because they're objects) to convey as much information as you need in order to describe the problem.

Structured Exception Handling Example


In the code sample below, we are doing meaningful work and committing a transaction in the Try block, rolling back a transaction in the Catch block, and disposing of resources in the Finally block.

Private Sub InsertEmployee( _
ByVal name As String, _
ByVal employeeID As String, _
ByVal connection As SqlConnection)

If name Is Nothing Then
Throw New ArgumentNullException("name")
ElseIf employeeID Is Nothing Then
Throw New ArgumentNullException("employeeID")
ElseIf name = String.Empty Then
Throw New ArgumentException("name cannot be empty")
ElseIf employeeID = String.Empty Then
Throw New ArgumentException("employeeID cannot be empty")
ElseIf connection Is Nothing Then
Throw New ArgumentNullException("connection")
ElseIf (connection.State And ConnectionState.Open) <> ConnectionState.Open Then
Throw New ArgumentException("Connection is closed.)
End If

Const SqlTemplate As String = _
"INSERT INTO Employee (Name, EmployeeID) VALUES ('{0}', '{1}')"

Dim sql As String = String.Format(SqlTemplate, name, employeeID)
Dim command As SqlCommand
Dim transaction As SqlTransaction

Try
transaction
= connection.BeginTransaction()
command = connection.CreateCommand(sql)
command.CommandType = CommandType.Text
command.Transaction = transaction
command.ExecuteNonQuery()
transaction.Commit()
Catch ex As SqlException()
transaction.RollBack()
Throw
Finally
command.Dispose()
transaction.Dispose()
End Try

End Sub

Moving On


In this article, we've seen the basics of how to handle thrown exceptions, and how to throw them ourselves. Moving forward, we'll cover nesting exception handlers, creating our own exceptions, and we'll dive into when it's appropriate to throw them, and when it's not.

Getting a Grip on Structured Exception Handling: Part 1

Many VB 6 developers who have made the switch to .NET continue to use VB 6's On Error error handling model. Some continue to do so because structured exception handling using Try...Catch and Throw represents a fairly daunting learning curve. Others do so because the On Error model is still supported for backwards compatibility and the old adage still applies: "If it ain't broke, don't fix it."

But there are very good and compelling reasons to learn structured exception handling. Modern programming languages have been using it for years, and Visual Basic has only recently caught on. It's a good thing, believe me. In this post, and the one that will follow, I'm going to try to explain why you want to embrace it, and how it can improve your code. Hopefully, I'll do so in a way that's clear, concise and doesn't confuse you.

If It Ain't Broke, Don't Fix It. No, Really

First and foremost, you shouldn't do a massive overhaul of your existing code just to replace the error handlers. And if you think someone's not considering that, think again. If you are, knock it off.
Refactoring your entire code base just to replace the exception handlers isn't a good idea. It's tantamount to tearing apart your car just to replace all the screws. If your software is working with the existing error handling engine, leave it alone, especially if you're not familiar with structured exception handling. You do not want to break perfectly good code because you wanted to implement a language feature that you don't fully understand. Get a grip on structured exception handling first, then use it in your next project.

As an aside, structured exception handling and On Error can co-exist in the same project. They are not mutually exclusive. Having said that, I don't normally recommend it. Mixing exception handling models tends to create code that confuses maintenance programmers; it creates a mental context switch when they're reviewing your code. Some routines use one model, some use another. This slows down maintenance of the system, and that's rarely a good thing. As you can imagine, I will not be providing advice on how to mix these models in a project.

A Review of On Error

Visual Basic's On Error error handling mechanism, which is still supported in .NET for backwards-compatibility, works in a relatively straightforward manner:
Public Sub Main()

On Error GoTo ErrorHandler

Dim x As Integer

x = 5 / 0 ' Force a divide by zero

HelloWorld:
MsgBox "Hello, World!"
Exit Sub

ErrorHandler:
If Err.Number
= 11 Then
' Yep, we did this on purpose. Ignore it and continue.
Resume HelloWorld
Else
MsgBox Err.Description
End If

End Sub

In some cases, you didn't really want to jump to an error handler. Instead, you wanted to ignore the error, because you could safely ignore it. In those cases, you used On Error Resume Next, and handled the error on the line immediately below the offending line.

Visual Basic provides the Err object, which exposes the Erl, Number, Source, and Description properties. These properties are intended to provide enough information for you to find out what happened, where it happened and what component was responsible for it.

While the On Error model was straightforward, it was looked upon with fairly universal disdain for one major reason: it involved the use of the dreaded GoTo keyword. While I won't dredge up a pointless debate about its merits, I will point out that it had one particular failing: it tended to create very complex procedures when a function needed to handle many different kinds of errors. Simply put, there was a lot of jumping around.

In addition, determining the nature of the error could sometimes be a risky business. In an ideal scenario, you could trust the error number. According to the documentation, the first 1,050 error numbers were reserved for use by Microsoft. An application or library vendor was expected to start their unique error numbers with 1051 and supply a descriptive error message along with the number. This rule wasn't always respected, however.

Vendors were also expected to provide the source of the error. But this meant that you had to query at least two properties in order to determine the true nature of an error, because two different vendors might expose the same error number. But it gets worse. Some vendors chose to use the same number for multiple errors, and distinguish between specific errors in the message. Consequently, in order to be truly certain, you had to check all three properties.

If Err.Number = x And Err.Source = y And Err.Description = z Then
' Handle the error
End If

 

For many developers, this seemed like overkill, so they chose to parse the contents of the description, since the likelihood of two vendors providing exactly the same message was pretty remote. This trade-off is never a good idea, however. It compromises certainty, and it brings inefficient string operations to the table. I've worked with an immeasurable amount of VB and ASP code in which this decision was made. While the code worked, it was hard to read and therefore difficult to maintain; and the lack of information about the source made it difficult to determine who was raising the error that you were trying to handle.

Someone once said that the difference between a professional developer and a hack was the way in which they dealt with errors: A professional developer takes them very seriously, and a hack plays foot-loose and fancy-free with them. I'm not sure who said that, but it struck a serious chord with me, and it changed the way I write software. When it comes to handling errors in your code, you want to be absolutely certain that you're handling the right error, at the right time, in the right way, and if you don't know what to do with an error, you leave it alone so that the caller can handle it.

Because of the way that the On Error architecture works, it is sometimes difficult to do this. You have to create lots of "jump-points" so that your error handlers return control to the correct location. If you're querying the Err object like most developers, you're doing a lot of string parsing, which can be quite tedious and creates code that is hard-to-read and performs poorly. After a while, you may start letting things go, and eventually only pay attention to the big, obvious errors. You may even get to the point where you dread diving into the error handling code because it's harder to read than the application logic.

Think you've got it bad? What about the next poor developer who's got to maintain that code after you've moved on to bigger and better things?

Moving On


So here's what we've got:


  • On Error is a simple, straightforward error handling system that works (usually).
  • There's no reason to modify existing code bases that work without an utterly compelling reason to do so.
  • On Error does pose maintenance issues, in that it is difficult to read, and tends to discourage developer discipline.
  • Error number collisions are quite frequent, meaning that developers have to rely on the Err.Source and Err.Description properties to determine the nature of the error, negatively impacting application performance and maintainability.
  • On Error is based on a trust system: it trusts the vendor or developer to correctly populate the Err object's properties, which often doesn't happen as it should.

In the next article in this series, we'll look at structured exception handling (SEH) as the alternative to On Error. We'll look at how it addresses these issues, and provides a superior means of dealing with errors in your applications.

Linux: The Silver Bullet of Operating Systems

I've been reading a lot of reviews of IE 7 lately, and many of them allow users to post responses. There seems to be a fairly common theme among those who don't like it: with zealous fervor, they tell you that you can solve all your problems by simply switching to Linux.
My god, people, wake up.
Are you honestly suggesting that the average home user switch to Linux or any Unix-based operating system (excluding OS X) simply because Internet Explorer doesn't meet your exacting standards? Get real.
First off, the average home user isn't going to have the technical skills to be able to use any of the xNix operating systems (again, excluding OS X). These operating systems tend to be complex, having been built up over 40 years and counting. Their vast feature set is daunting, even to advanced IT professionals. You cannot expect the average home user to turn in an operating system like Windows, which shields them from the complexities inherent in an operating system and expect them to switch gears without introducing a whole new set of problems. The frustration from learning a new operating system alone would likely drive them bonkers.
Second, you have to take application selection into consideration. Are your favorite applications even available in that operating system? Can you get Microsoft Office for it? PageMaker? Adobe Acrobat? Decent video or music editing software? And of the software that is available for it, can the average home user go to Best Buy, Circuit City, or CompUSA and purchase it in a nice shrink-wrapped box (complete with documentation, mind you), or does he or she have to go to some obscure website that he or she may or may not trust and download it (possibly with a complimentary virus)? And if they download it, will it work and can they get product support for it?
Third, how about device support? Can I plug all of my peripherals in it and have them just work? Or will I have to go on a mad search for device drivers again, like I had to back in the days before Windows XP? Sure, if I'm an IT professional, that's a snap. But if I'm the Average Home User (TM), that's more than I'm usually able to deal with. It's probably going to tick me off royally, and I'm going to wonder why in the heck I bothered with this operating system in the first place. I'll be wondering, "Where's my plug-n-play support?"
Fourth, how good is the font support? Does it support true WYSIWYG? Or does the type look completely different between the screen and the printer? The same could be asked of the colors. That's going to be really important for the average home user who's working with videos and photos, printing calendars, Christmas cards, and invitations, you know.
Fifth, if I need to do my office work on it, will I be able to do so? If so, how difficult will it be to do so? Will I have to be a rocket scientist? And if it breaks, who do I call? I mean, if I was able to call my brother or my kid before, because he was a Windows guru at his company, will he still be able to do that? I switch operating systems, and now no one knows anything about it. I'm now an island in the middle of a Windows ocean. I've effectively isolated myself from everyone. How long do I have to wait before I can get meaningful support in an emergency?

Now. Ask yourself a really serious question and think hard before you spit out a really ludicrous answer. Are you really going to tell someone to risk losing all that support just because the browser isn't that cool, or because there are a few security loopholes?

Sure, we live in a Microsoft world. Are all their products perfect? Heck no. I work with Microsoft products every day, and I can testify to the fact that they aren't. I also own a Mac computer at home, and I happen to be a big fan of OS X. I've also had the occasion to use Linux. Each of them has their pros and cons. But I'm here to tell you that there is no silver bullet. Statements like those being made in these IE 7 reviews are asinine, short-sighted, and utterly ridiculous. Linux is not the solution to everyone's problems because it isn't the ideal operating system for the average home user.

Average home users need an operating system that's easy to use, has a large selection of over-the-counter applications, is secure but not intrusive, supports all of their peripherals, and has great product support. In my opinion, the one that comes closest to that is Mac OS X. However, it's pricey nature makes it cost-prohibitive for the average home user; and that's one of the reasons we live in a Microsoft-dominated world.

Sure, Linux is cheap. But the over-the-counter application support just isn't there, and the GUIs, in my opinion, still look like their in their infancy. And they're still targeted at tech-heads. When all the Linux variants start targeting the average home user, and the over-the-counter application support gets there, and they get really good device driver support, things might change. But there's no way on earth I'd ever recommend it to my family or any of my non-technical friends. They'd go out of their minds trying to figure it out.

So, in the end, I'll continue to read these kinds of posts from Linux advocates and shake my head. In my personal opinion, statements like these demonstrate a complete lack of comprehension when it comes to the average home user's computing needs.

Raising Base Class Events From a Derived Class in .NET

I always forget how to do simple things. This blog is likely to contain lots of little snippets like this. Lots of them will be stuff that big brained techheads find fairly obvious. Well, that's just tuff. You sit in your big comfy chairs and scoff. I'm not here to feed your egos. I'm here to post stuff about things that I find hard to locate, stuff that made me smack my forehad when I found it because it was so obvious. The frustration and time wasted searching for it makes it worth posting here.

So, with that out of the way, here we go. And I'm not even going to add any fluff to it. Just a simple code sample.

Public Class BaseClass

Public Event
MyEvent(ByVal sender As Object, ByVal e As EventArgs)

Protected Sub
OnMyEvent()
RaiseEvent MyEvent(Me, EventArgs.Empty)
End Sub

End Class

Public Class DerivedClass
Inherits BaseClass

Public Sub MyTask()
MyBase.OnMyEvent() ' Raises the event
End Sub

End Class

The Evil Wiles of the Chain Letter

My mother recently got a chain letter in her email, and was telling me how she hates it when people send them to her. Certain friends and family members are notorious for forwarding them us, and it really peeves us off, because they're a complete waste of our time. We simply delete them these days. Unfortunately, in many cases, you have to open the email before you can determine whether or not its a chain letter. And by the time you've opened it, you feel utterly compelled to finish reading it. It's like a hypnotic effect.

Once you've opened a chain letter, it's contents are like road kill: you have to look. You can't help it. You don't want to look, but you have to. Your brain screams, "It's not right! Turn the other way! Close your eyes!" But your eyes stare fixedly at it while your mouth drops open in horror and bile rises at the back of your throat. When you've finished reading it, you're left with that feeling that you just got ripped off, like you paid to see Saving Private Ryan and instead got something on the order of Killer Beach Bimbos from Outer Space.

Chain letters have even moved away from the email format and evolved into MySpace comments. (I haven't checked out sites like TagWorld or any of MySpace's competitors, so I can't really comment on that.) Fortunately, MySpace comments are easily ignored. You can just customize the page to not display them.

Now, chain letters pose a number of problems, as I see it:

  1. They waste the recipient's time. No one that I know likes to waste time reading them. I'd guess that those who do are also those who like to forward them, thereby propagating them and contributing to the misery of those who don't want to get them in the first place.

  2. They clog up mailboxes. Most users have a finite amount of space in their mailboxes. When your mail box runs out of space on the mail server, the mail server refuses to accept mail for you until you start clearing out your maibox. If your mailbox is full of spam, that tends to tick you off. No one wants to waste a limited amount of space on email that serves no purpose and that they'll just delete anyway. This is especially problematic if the chain letter includes images, audio, or video attachments.

  3. They tend to contain deeply nested attachments. Because they are forwarded so many times, and due to the way that many email programs forward emails, the message is forwarded as an attachment: its text is not included in the body. This forces the user to open the attachment in order to read the message, which is often a risky proposition these days, since many casual users either don't use virus software or don't keep it up to date. However, every time the message is forwarded, the message is sent as an attachment, with a new message wrapper. So the new recipients have one more layer to click through in order to get to the actual message. By the time it's been sent ten or twenty times, which happens quite frequently, the real message is so deeply embedded in nested attachments that it's a chore to reach it. This is a highly annoying aspect of these messages, and it wastes lots of time.

    The fact that the messages are included as message headers also bloats the size of the email itself. Instead of simply including the text of the body, and using a single mail header, the email is sent as a file attachment, with the complete email header plus the complete email header for the new email. Now if I send it again, I get to create an email that contains a new email header, plus a new file attachment that contains the email header and text for the email I received plus the original attachment. The file's just gotten much larger. When my recipient forwards the file, it will get larger again. This causes the chain letter's overall size when sent across the Internet to grow every time it is forwarded.

  4. They are propagated by exploiting human weakness. Chain letters aren't sent out by a mindless computer sitting in an air conditioned network operations center. Nor are they sent out by some hacker sitting in a dark room in his mom's basement somewhere. They are sent out by people who get them from their friends, and think they're cute, or touching, or emotionally stirring, and forward them on to their friends. They forward them on to the people that they think are most interested in them. And so, unlike spam, which dies when it hits a user's machine and (hopefully) goes to his or her recycle bin or junk email folder, the user keeps the chain letter alive by sharing it with his or her friends and coworkers. Further, the user sends it to multiple individuals, and those individuals do the same. So the chain letter is spread like a biological virus.

  5. They are nothing more than personal, selective spam. Don't believe me? Dictionary.com defines spam as "unsolicited e-mail, often of a commercial nature, sent indiscriminately to multiple mailing lists, individuals, or newsgroups; junk e-mail." Now here, by "indiscriminately" we mean that the sender of a chain letter thinks that everyone wants to get the same chain letter that he or she did. "If I like it, certainly all of my friends and coworkers want to get it." So what does that mean, exactly?

    It means that the sender is distributing spam, knowingly and willingly, albeit naïvely. People have been sent to jail and fined for that because it's illegal in certain states. Why is it illegal? Because it annoys users, and wastes bandwidth and the recipients' resources. The biggest reason, however, is that it annoys users. This article shows that the vast majority of people want spam banned simply because it annoys them.

    By definition, spam is not required to be commercial in nature. That means that all those chain letters you get that promise you happiness, the good will of angels, good luck, blessings, prosperity, the love of your life within three days, your wish coming true, the prevention of bad luck, and on and on, are all spam. Most of us just haven't viewed chain letters in that light before.

And those are just the issues I can think of right off the top of my head.

While I'm at it, it isn't just novice users that get sucked into these emails. With address spoofing you can receive an email that looks perfectly legitimate, and you can start clicking on those attachments because it seems perfectly reasonable to do so. I've done it. My mom has done it. My coworkers have done it. You've typically already opened four or five layers before you realize what you've done and think to yourself, "What am I doing? This is obviously junk!"

So please, please, think before you send that next chain letter out. Those promises they make at the bottom of them are untrue. The threats they make are untrue. You control your own destiny, and no email is going to dictate it for you. Do you think that the destiny of the people that two whom you forward the email is going to be changed because you sent it to them?

You aren't helping anyone by forwarding these things. Point of fact, you're likely annoying more people than you're helping.

Do everyone a favor. When you receive a chain letter, delete it immediately. Then contact the person who sent it to you and politely ask them not to send them to you anymore.

Creating an IsDate() Function in JavaScript

One of those things that always seems to be missing from the Date object in Javascript is the ability to determine if a string contains a valid date expression. I find that sorely lacking myself. Coupled with the fact that I needed it badly for a major project I'm working on, this became a really pressing concern of mine. Hence, a new blog entry.

A quick search on Google turned up a few different ideas. But my favorite one was this, which I found on http://www.thescripts.com/:


There are a lot of good answers on this thread but personally I think
javascript itself is a better date validator.

function isDate(sDate) {
var scratch = new Date(sDate);
if (scratch.toString() == "NaN" scratch.toString() == "Invalid Date") {
alert("Not a Date");
return false;
} else {
return true;


}
}

Javascript's date parser is actually very robust and powerful and able
to hande 1/20/2004 as well as January 20 2004. IE returns NaN (not a
number) when it can't figure out a date, Mozilla (gekko engine) returns
Invalid Date.

I like this one because it's simple, short, and to-the-point. It's unfortunate that the poster was anonymous, because I'd like to give him/her credit for it. At the least, I can provide a link back to the thread. It's response number ten in the thread, from user pcx99..

The Power of Unit Testing, and Microsoft's Big Snafu

Automated unit testing has got to be one of the most powerful ways to produce rock-solid code. That's not to say that code-reviews and full testing by a dedicated quality assurance team don't have value, but automated unit testing has the virtue of being fast, easy-to-execute, preemptive, and (assuming the tests were properly written) extremely accurate.


The idea is simple. Before you write any code, you write a test that breaks. Then, you massage the code until the test passes. This makes you focus on the problem domain, and keeps you from getting side-tracked by fancy fluffy stuff that you might add and never actually use.


Now here's where the power comes from: the automated tests are run every time the software is built. These tests don't tell you if the software is syntactically unsound (that's the compiler's job); instead, they tell you whether or not the software is doing what it's supposed to do, and it's telling you this before the testers get their hands on it. The fact that it happens every time the software is built should give you a tremendous feeling of confidence: you'll know immediately if anyone introduced any code changes that broke your business logic, so you can be proactive about correcting them, instead of waiting for the testers to find it and rub your nose in it.


However, manually creating your own testing framework, and religiously executing the unit tests themselves with every build can be daunting. That's why projects like JUnit and NUnit are wildly popular: they simplify the process of creating and executing automated unit tests. The idea is so deeply entrenched in our development methodology these days that it has a name: Test-Driven Development (TDD). Kent Beck wrote what is arguably the definitive introductory work on the idea, the aptly named Test-Driven Development. (You can find that book on Amazon here. But if you don't want to read the whole book—even though it's a really good idea—you can take the crash course here.)


So now that I've made it clear that I'm a huge proponent of test-driven development, and of automated unit testing specifically, it's time we turned our steely gaze to Microsoft.


As many well know, Microsoft created its own unit testing framework. From what I've seen, it's pretty exhaustive. (Of course, like any Microsoft version of an existing "standard," their framework deviates pretty substantially from the NUnit and JUnit frameworks. So you can't just plop your existing test frameworks into Visual Studio 2005 and run them. No, you have to convert them to Microsoft's way of thinking.


How very Gatesian.


But that's not my main peeve. My main peeve is this: you can only access the unit testing capabilities of Visual Studio 2005 if you own Team System. Furthermore, the express editions of Visual Studio do not provide add-in support, so that tools like NUnit can't be snapped into the IDE, except as external tools.


Here's what I don't understand: Microsoft is all about security these days. Part of security is writing secure, stable software. Why on earth would you deny developers access to a technology that could help them to write stable, secure software? Furthermore, if you're releasing a free version of your IDE to the masses, why would you think it's a good idea not to support add-ins, so that new developers can't take advantage of things like NUnit?


And please don't tell me that you just expect everyone to upgrade to Team System. If you want them to do that, price your products reasonably. The average CIO will just about have a heart attack when he sees the estimate to upgrade from 2003 to Team Suite. I wouldn't be surprised if most companies can't afford it.


We can sit here and talk about how everything comes down to long-term cost versus short-term costs until we're blue in the face. Hey, I've read Steve McConnell, I know the facts. But the plain truth is, when you have someone sitting in the finance department who doesn't really get it, and who doesn't care to get it, because he's got a "bigger picture" than you do, your upgrade costs are going to get nixed in a heartbeat. You'll get the Professional Edition, or something close to it, because it's at least reasonably priced and does just about everything you need. I'm willing to bet that more companies are run that way than aren't, and a lot of poor developers are working for them.


So, as I see it, what Microsoft has done is shoot itself in the foot over unit testing. They're not fostering the growth of a corps of developers who write good, stable, well-tested code. They've placed the cost of unit testing using Microsoft's preferred framework so far out of reach that most companies will never embrace it. Those that do embrace automated unit testing will look for open-source third party products like NUnit. They'll find ways to make it work. If Microsoft was trying to squeeze that competition out, they certainly went about it the wrong way.


I would certainly like to sit down and talk to the brainiac that decided that unit testing wasn't going to be made available except for the elite few who could afford Team Suite. I'd also like to find the guy who decided that Add-In support was a no-go for the Express Editions, and confiscate his Geek Mafia membership card.


These two decisions together have pretty much guaranteed that most new users will find it too hard to learn how to unit test from the beginning, because Microsoft went out of its way to make it difficult.


When Microsoft had the opportunity to put the tools into the hands of new developers (many of whom couldn't afford pricey development tools) to make them better, more effective programmers, they opted instead to take those tools away, to keep them for the rich, and take away their ability to use 3rd party components in the IDE as well.


"Do it my way, at my price, or not at all."


Shame on you, Microsoft. Shame on you.