Wednesday, December 27, 2006

Why idiots shouldn't code business-critical apps in VB

Ok, so it may sound like a harsh comment, but there have been many times when I've found rather dodgy Visual Basic code in business critical applications. Unfortunately, the ease of programming with VB means that users do not have to have any knowledge of good programming practice, or even a good knowledge of VB to be able to create the next wizz bang application. Show these to your boss, and mention the fact that it took you less than a day to develop and you'll get a promotion in no time!

So why on earth do we have programmers?

To those who don't know me too well, you might think that I'm bagging Visual Basic as a language, but this is quite the opposite. The language is brilliant for RAD (Rapid Application Development) due mainly to its readability and (relatively) concise code. I use VB pretty much every day in my current workplace, and I have a library of VB code that does some pretty nifty stuff.

With Microsoft including a full Visual Basic for Applications development environment in their Microsoft Office suite, they have made it easy for users to play with. This is a good thing. However, users have a natural tendency to assume a great knowledge of programming simply because they know what a macro is.

This is even true for other programmers with little or no VB experience. All too often, companies use C/C++ developers to develop VB applications. This is fine if the developers have a genuine interest in VB and want to learn it as a language, but all to often they try to apply C/C++ concepts where they shouldn't or needn't be applied.

I put to you this block of sample VBA code that I found today in a Microsoft Access application at work. Its task is simple: to display a File Open dialog, prompting the user to select a file to open. In this case, the user wasn't a complete idiot, as it appears that they had some familiarity with the Windows API, although this could well have been an Experts Exchange copy-and-paste.

Exhibit A:

Option Compare Database 'Use database order for string comparisons
Option Explicit
Option Base 0

Declare Function upg_lstrcpy Lib "Kernel32" Alias "lstrcpy" _
(ByVal lpDestString As Any, ByVal lpSourceString As Any) As Long

lStructSize As Long
hwndOwner As Integer
hInstance As Integer
lpstrFilter As Long
lpstrCustomFilter As Long
nMaxCustFilter As Long
NFilterIndex As Long
lpstrFile As Long
nMaxFile As Long
lpstrFileTitle As Long
nMaxFileTitle As Long
lpstrInitialDir As Long
lpstrTitle As Long
Flags As Long
nFileOffset As Integer
nFileExtension As Integer
lpstrDefExt As Long
lCustData As Long
lpfnHook As Long
lpTemplateName As Long
End Type

Declare Function upg_GetOpenFileName Lib "COMMDLG32.DLL" _
Alias "GetOpenFileName" (OPENFILENAME As upg_OPENFILENAME) As Integer

Global Const upg_OFN_READONLY1 = &H1
Global Const upg_OFN_OVERWRITEPROMPT1 = &H2
Global Const upg_OFN_HIDEREADONLY1 = &H4
Global Const upg_OFN_NOCHANGEDIR1 = &H8
Global Const upg_OFN_SHOWHELP1 = &H10
Global Const upg_OFN_ENABLEHOOK1 = &H20
Global Const upg_OFN_ENABLETEMPLATE1 = &H40
Global Const upg_OFN_NOVALIDATE1 = &H100
Global Const upg_OFN_ALLOWMULTISELECT1 = &H200
Global Const upg_OFN_EXTENSIONDIFFERENT1 = &H400
Global Const upg_OFN_PATHMUSTEXIST1 = &H800
Global Const upg_OFN_FILEMUSTEXIST1 = &H1000
Global Const upg_OFN_CREATEPROMPT1 = &H2000
Global Const upg_OFN_SHAREAWARE1 = &H4000
Global Const upg_OFN_NOREADONLYRETURN1 = &H8000
Global Const upg_OFN_NOTESTFILECREATE1 = &H10000
Global Const upg_OFN_SHAREFALLTHROUGH1 = 2
Global Const upg_OFN_SHARENOWARN1 = 1
Global Const upg_OFN_SHAREWARN1 = 0




Function upg_GetDBFileName$(stype As String, sOpenPrompt$)
Dim szFilter As String
Dim Retval As Variant
Dim wFlags As Integer
Dim varFileName As Variant
Dim szDirectory As String
Dim sPrompt$

' Specify that the chosen file must already exist if stype <>NEW

If stype = "new" Then
wFlags = 0
sPrompt = "New Database"
sPrompt = sOpenPrompt
End If

szDirectory = "C:\"

'* Define the filter string and allocate space in the "c" string
'* Duplicate this line with changes as necessary for more file templates.
szFilter = szFilter & "Access Database (*.mdb;*.mdd)" & Chr$(0) & "*.mdb;*.mdd" & Chr$(0)

' Now actually call to get the file name.
varFileName = upg_OpenCommDlg(szDirectory, szFilter, wFlags, "MDD", sPrompt)
If Not IsNull(varFileName) Then
' strip the trailing nulls
varFileName = Left(varFileName, InStr(1, varFileName, Chr(0)) - 1)
varFileName = ""
End If
upg_GetDBFileName = varFileName
End Function

Function upg_OpenCommDlg( _
szInitialDir As String, szFilter As String, wFlags As Integer, _
szDefExt As String, szTitle As String) As Variant
Dim szFileName As String
Dim szFileTitle As String
Dim szTitleStr As String
Dim szCurDir As String
Dim wAPIResults As Integer

CRLF$ = Chr$(13) & Chr$(10)

'* Allocate string space for the returned strings.
szFileName = Chr$(0) & Space$(255) & Chr$(0)
szFileTitle = Chr$(0) & Space$(255) & Chr$(0)

'* Give the dialog a caption title.
szTitleStr = szTitle & Chr$(0)

'* Set up the defualt directory
szCurDir = szInitialDir & Chr$(0)

'* Set up the data structure before you call the GetFileNameName

' This ought to be a legal hWnd, but maybe there's no form open. So we'll
' use 0 here. Windows doesn't seem to mind.
OPENFILENAME.hwndOwner = 0
OPENFILENAME.lpstrFilter = upg_lstrcpy(szFilter, szFilter)
OPENFILENAME.lpstrFile = upg_lstrcpy(szFileName, szFileName)
OPENFILENAME.nMaxFile = Len(szFileName)
OPENFILENAME.lpstrFileTitle = upg_lstrcpy(szFileTitle, szFileTitle)
OPENFILENAME.nMaxFileTitle = Len(szFileTitle)
OPENFILENAME.lpstrTitle = upg_lstrcpy(szTitleStr, szTitleStr)
OPENFILENAME.lpstrDefExt = upg_lstrcpy(szDefExt, szDefExt)
OPENFILENAME.hInstance = 0
OPENFILENAME.lpstrCustomFilter = 0
OPENFILENAME.nMaxCustFilter = 0
OPENFILENAME.lpstrInitialDir = upg_lstrcpy(szCurDir, szCurDir)
OPENFILENAME.nFileOffset = 0
OPENFILENAME.nFileExtension = 0
OPENFILENAME.lpTemplateName = 0

'* This will pass the desired data structure to the Windows API,
'* which will in turn it uses to display the Open Dialog form.

wAPIResults = upg_GetOpenFileName(OPENFILENAME)
upg_OpenCommDlg = IIf(wAPIResults = 0, Null, szFileName)
End Function


WTFs in order:

  1. What do you plan to achieve with the lpstrcpy function?
    lpstrcpy is a kernel function to copy a string from one location to another. It happens to return a pointer to the destination string when successful. These idiots are using a string copy function to return a handle to a string. In VB. Idiots.
  2. Why are the lpstr's in upg_OPENFILENAME declared as long integers?
    Ok, so an lpstr is a long integer pointer to a string. So a C programmer would declare it as a long. But strings in VB are stored with pointers, just like they are in every other language I know. So you can declare lpstr's as Strings and save a lot of time and effort.
    The only noticable difference is that VB strings are not null terminated, while most Windows API functions expect them to be. This is easily resolved by appending the ASCII character &H0 (stored in the VB constant vbNullChar) to the end of the string before calling the API function.
  3. Who on earth still uses dollar signs, percentages and ampersands to declare the data types of BASIC variables?
    Last time I used things like "name$" and "id%" was when I was coding in GW-BASIC and QuickBASIC. Yes, in MS-DOS. Correct me if I'm wrong, but since at least version 4 of Visual Basic (ie: pre-VBA in MS Office) you have been able to use the As keyword to declare the data type of a variable. So "Dim name$" becomes "Dim name As String" and "id%" becomes "Dim id As Integer". Much more readable, and the added bonus is if you decide to change the data type of an existing variable, you don't need to find and replace all existing instances of the variable name. Sweet, huh?
  4. What's with the stype parameter to upg_GetDBFileName$?
    Why use a string to represent a parameter that can only have two values: new or old? That's what a boolean is for. Declare the parameter as "bNewFile As Boolean" or similar. Or drop the parameter completely and display "New Database" if the sOpenPrompt$ parameter is blank.
  5. What's with the variants?
    In Visual Basic, simple data types are not derived from the object data type. This is because BASIC was never designed as an object-oriented language. Visual Basic has the variant data type as hack to allow programmers to store either simple data types or an object in the same variable. For instance, a variant can contain a string, an integer or a recordset. This is similar to declaring a variable as as object in languages such as Java. Like the object type in Java, you should avoid it's use at all costs. If you know what the data type of a variable will be at development time, there is no excuse to use variants. Variants = BAD.
    In this case, variants have been used to allow the upg_OpenCommDlg function to return Null. While it may be common for C functions to return null, this is not common practice in VB. This is because the Null data type is used (particularly when dealing with databases) to represent an unknown value. In this case, if the call to the Windows API returns zero, (ie: the dialog box was cancelled or closed) then the filename is empty, but it is not unknown. The function should return "" (an empty string) or to be more correct, the VB value Empty. This would allow the function to be defined as returning only strings, not variants. (For more information on the difference between Null and Empty in VB see Eric Lippert's post on MSDN.)
    What makes this all the more entertaining is that the function upg_GetDBFileName$, (which is the only function in the application to call upg_OpenCommDlg,) checks to see if the value is Null and, if so, returns an empty string. Why not do this in upg_OpenCommDlg and save the effort of an extra IF statement?
  6. Why specify the initial directory to be C:\?
    Never assume that C:\ exists. The user may have their CD drive as C:\, or they may not have access to view C:\ due to security settings.
    And if you don't specify a folder when calling methods of the Common Dialog API, it will default to the user's home directory, which is likely to be the location of any document they are about to open anyway.
  7. Why declare CRLF$?
    This is pointless for the following reasons:
    1. vbCrLf is a Visual Basic constant that would replace it.
    2. They don't use it in this code any way.
  8. What's with the Chr$(0)'s?
    How about using vbNullChar and save yourself a function call or two. The two methods are equivalent, so this is only a minor WTF, but constants are always a good idea.
  9. What's with the null characters at the end of the null strings?
    The programmers allocate szFileName as empty space to store a string using the common method of Space(255) to return a string of 255 space characters. In this instance, they've decided to improve on that by making it 257 characters with a null at each end. Why? Who knows. The API function will see that the first character is null, and consider it to be a zero length string. Upon the function's return, the memory allocated by the Space(255) function may be overwritten if the API decides to change the value of this variable. So the end null character is pointless.
    Note that the Space function in VB is not a memory function that allocates space of 255 bytes, it is a string function that returns a string containing the specified number of ASCII space characters. The actual character is irrelevant when calling the API, as it will get overwritten anyway.
  10. What's going on with hwndOwner?
    The Common Dialog API in Windows provides standard looking File Open/File Save/Print and Color Chooser dialog boxes. Because it creates these dialog windows from outside your application, hwndOwner is used to pass a handle to a window representing the owner of the dialog box. In most applications, this will be the handle of the window containing the button or menu item that triggered the dialog box to be displayed. This allows Windows to ensure that the dialog box stays modal in that application until such time as it has been closed. This also reduces the chances of having an orphaned dialog box drifting through the Z-order as you Alt-Tab between applications.
    In most VB applications, your call to a wrapper function such as upg_OpenCommDlg would contain a parameter of type Form. Every window in VB is a Form. Every Form as a property called hWnd which returns the underlying handle used by Windows as an identifier of that window. Therefore, you can provide the Common Dialog API with an hWnd to keep the relationship between the application and the dialog box.
    In this application, as noted in the comments, the developers weren't sure if a window would currently be displayed by their application, so they default the value of hWndOwner to zero, making the dialog box an orphan. Initially, their comment sounded beliveable. This function is buried deep in the code of the application, and could theoretically be called at anytime. However, this is a GUI application. All GUI applications have windows, and at least one of those windows will always be loaded. That window may not be visible, but Windows will still know it exists, so it will have an hWnd.
    To make the matter worse, because this application is a Microsoft Access database, they could have easily used the Application.hWndApplication function to return the handle to the Microsoft Access window, irrespective of what is happening in the database itself.
  11. (See WTF 1 regarding the blatant abuse of the lpstrcpy function)

While this appears to be old code that may have been written for Microsoft Access '97, I thought it would be good to show how I would do this in recent versions of Microsoft Access:

Function PromptForDBFileName(Optional sOpenPrompt As String = "") As String
Dim fd As FileDialog
Set fd = Application.FileDialog(msoFileDialogFilePicker)

fd.Title = IIf(sOpenPrompt = "", "New Database", sOpenPrompt)
fd.Filters.Add "Access Database (*.mdb;*.mdd)", "*.mdb;*.mdd"

If fd.Show Then PromptForDBFileName = fd.SelectedItems(1)
End Function


Note that this function replaces all the code shown above.

I'm glad I've gotten that off my chest. Now to tidy this mess up...

Tuesday, September 12, 2006

Snakes on a Plane - The Review


I went with a mate to see Snakes on a Plane (official site or IMDB) over the weekend. Well, what can I say?

I'd previously heard mixed reviews about this movie, some saying it was as cheap as the title made it out be, while others saying that it was actually a jolly good flick. This movie had the ability to be a well produced, well acted, suspenseful work of art. Unfortunately, it failed on almost all counts.

Samuel L. Jackson's character is made out to be the hero of the story, when in reality Nathan Phillips ("Sean Jones"), Kenan Thompson ("Troy McDaniel"), Julianna Margulies ("Claire Miller") could each have been the hero, as they all play a more valuable role in the movie than Jackson. (According to my mate, the original scripts were titled "Snakes on a Plane" and when the producers went to rename it, Jackson refused and asked it to be left with the crummy title.)

As usual, Rachel Blanchard plays a dizzy blonde ("Mercedes Harbont", another Paris Hilton), while Flex Alexander plays a professional musician ("Three G's"). Neither of these characters have any depth, nor do they perform any useful role in the story line, apart from perhaps Mercedes' dog.

The computer graphics were obvious (the boa constrictor scene is a perfect example) and the plot lines were flawed:
- How did snakes manage to get into the cockpit at the same time that they entered the rear cabin, without any appearing in the forward cabin or first class?
- How did the boa constrictor start off in the light fitting? After the boa gets out, there is a scene that shows quite clearly that the light fitting is a sealed unit.
- Surely pheromones would just make the snakes amorous, not angry?
- Isn't it a coincidence that the lifeboat is EXACTLY the same shape as the stairwell?
- Blowing a whole in the side of the plane would not be high on my list of suggestions as a possible way to resolve the snake situation. Not only would it reck the structural integrity of the plane, but the passengers would be unable to hold themselves in, and there would be no air to breath!

Nathan Phillips ("Sean Jones"), Kenan Thompson ("Troy McDaniel"), Julianna Margulies ("Claire Miller") clearly produced the most polished performances in the whole movie and they deserve congratulations for bring some real believability to the whole thing.

While the movie has some unexpected surprises (ie: snakes jumping out from seemingly innocuous places), it is very predictable and amateurish. I don't know what the budget was, but I'd hazard a guess at $1.95 (ooo, enough for a Big Mac!). With movies costing $14 to view these days, I came out feeling very short changed...

Thursday, July 27, 2006

How to run the free version of Hamachi as a Windows Service

I recently tried out the Hamachi VPN client, but I was disappointed to see that the free version of Hamachi wouldn't run as a Windows Service, like OpenVPN.

It is possible to use an application like SrvAny, from the Windows NT Resource Kit to overcome this, but this is beyond your average user.

But within a half hour, I had created a simple little service launcher for it, using Visual Studio. This neat little app appears as a Windows service, and requires no configuration. When it starts, it checks the registry to determine where Hamachi is installed, then spawns Hamachi. When the service is stopped, it automatically kills the Hamachi process. Likewise, if the instance of Hamachi dies for whatever reason, the service will stop. This means that you can use Windows' inbuilt automatic service restart facilities to restart the service if Hamachi dies.

The downside is that the application cannot be configured, because there is no visible GUI. This isn't much of a problem as you can configure the application by running it normally and then closing the application, before you start the service.

This software is available as freeware from the link below.

Download the Hamachi Background Service launcher (10KB)

Tuesday, June 27, 2006

Asterisk & the Digium TDM400P

At last, the Digium TDM400P has received a Telepermit to allow its use in New Zealand. This means that those who are interested in all things VoIP can now try it out for real with their own PABX! For those who are unawares, Asterisk is the way to go, and if you don't want to hack around getting it installed try the Asterisk@Home distro. To source the hardware I can recommend nicegear, and for New Zealand-based support, check out their sister site the New Zealand Asterisk Users Group. We've got a PBX set up at home now, so I thought it would be useful to share the experience.

If you intend on using Asterisk with a landline, make sure you contact your telephony provider to arrange Disconnect Supervision. Basically, the Digium board relies on this extra signalling to know when an incoming landline caller hangs up the phone. If you don't do this, then you'll find that the Digium board won't release the line when remote callers hang up.
If you are a Telecom customer, you need to ring Telecom Business on 126 and ask for "Clear Forward and Answer Reversal". It is available at no cost to business customers. Note that Telecom Residential customers may not be eligible for this service. However, you can try asking for it. Don't call 123 as they won't know what the hell you're on about, call 126 instead.

When installing the hardware drivers, make sure you use the patched version of the driver, as the current Zaptel driver build is not Telepermit compliant. Check out the bottom of the nicegear home page for more information. Make sure that you use the extra command line switches too. More information is available on ASTUG.

If you don't like the multitude of configuration files, you might want to look at using Asterisk RealTime to pull the configuration out of a MySQL database or similar. If you decide to go down this track, make sure you implement RealTime before you get stuck into configuring Asterisk, as it can be painful to retrofit. Note also that RealTime does not seem to be supported by any of the free front-ends for Asterisk, such as FreePBX, so all the configuration might need to be done manually or by using custom scripts.

While not for the faint hearted, Asterisk offers the coolest functionality and utimately the best value for money. After all, you can't beat free!

Monday, May 29, 2006

Terry Tate: Office Linebacker

If you havent seen these videos you're missing out. Classic! ;-)

Saturday, May 13, 2006

Diebold Election Systems are truly holey!

I have been watching with interest, the debate in the United States about the use of electronic voting systems during government elections. I personally believe that it is possible to have an electronic voting system that is a suitable replacement for the traditional paper-based methods, but the current system is a complete pig's ear.

For some reason, Diebold Election Systems was chosen as the preferred provider of the technology for the US Elections. Previous to this, the only systems that I associated with Diebold were the photocopier credit systems used at schools, universities and libraries, to allow you to pay for photocopies using your membership card. But it turns out that they also manufacture Automated Teller Machines and other self-service electronic kiosks. If the security of their ATM's is anything like the security of the voting systems, then banks have a lot to worry about.

Diebold were pulled up in July 2003, after it was discovered that encryption of the data collected by the systems was poor at best, and that voters could create their own smart cards, enabling them to vote more than once. And this was discovered after the voting software itself was released publicly on a Diebold website!

Then in September 2004, the state of California accused Diebold of using uncertified software on the machines. This resulted in a US$2.6 million settlement.

But even better than that, is the latest debarcle. A recent Slashdot article lead me in the direction of this blog entry by Bruce Schneier, world-reknowned computer security researcher. According to a recent report by Black Box Voting, the Diebold devices run a basic build of Microsoft Windows CE. While the details are sketchy, it would appear that the Diebold devices ship with Windows Plug'n'Play support enabled...

The devices have dual PCMCIA slots underneath the unit (indicated by B in the picture). The basic idea is that any voter with a PCMCIA memory card (or any other memory card with a PCMCIA adapter), can probably update the software on the machine at their will, and remove the card without anybody knowing that the software was changed.

Or for a more sophisticated attack, voters can use a standard Philips head screwdriver to disassemble the unit, and install an MMC/SD card in the hidden, internal SD slot (located just above the CMOS battery in the picture). Again, this card could simply be a memory card, containing a custom application, or it could be an SD WiFi card, providing a way to remotely trigger an attack. An external inspection of the unit would not show any indication of this type of attack.

Or if all this sounds too easy, you can replace the entire bootloader on the machine with your own, by simply having a file with the appropriate name sitting on a removable memory card while the machine is booting up. Although there are integrity checks in the boot loader, it sounds like they are limited to checking the filename, the size of the file and possibly the file header.

My, my, so many choices! It sounds like even school kids and script kiddies could crack these things.

Thursday, May 04, 2006

Dvorak reckons MS is dead in the water...

Looks like I'm not the only one who thinks that Microsoft is having issues. John Dvorak agrees in this article at MarketWatch. He makes some very interesting points.

Saturday, April 29, 2006

Google, Microsoft, Apple, etc

Ok, so it's been a while since my last post. Ok, quite a while. Yes, I know, over a month!

Anyway, I thought I'd start back into it by discussing happenings in the IT industry. I was having an interesting chat about the state of things with a mate tonight, and these were a few of my thoughts. Now I'm no business expert, or fortune-telling psychic, but me thinks big things are happening.

For a start, Microsoft are in a lot of tepid water at the moment. For a start, the EU debarcle does not seem to be going well for them (here and here). They haven't released a desktop version of Windows for about four years and Office is now about three years old. Added to that is the fact that the distant Vista is getting cut down more and more, although they still have big hopes. And in the last few days, Microsoft shares are sinking faster than Tiger's golf balls. The only thing that does seem to have worked out for them lately is the Xbox 360.

Apple seem to have taken a Microsoft-esque approach in recent years, marketing crap products to the masses. The uptake of iPods has been huge (with even the Pope getting an iPod recently), despite reports of poor quality and Harvey Norman reporting a high return rate. Even the Motorola ROKR spin-off has been a flop. It intrigues me that a lot of people don't even seem to realise that there are other personal music players available with equal or better features for much less. So it just goes to prove once again, that you don't need to have a good product to make good money.

Google on the other hand seems to be the company of the 21st century. Through clever thought and planning, they have a company which while being a monolith is still highly respected. Their recent run-in with the DOJ allowed them to illustrate to the world what they stand for. The have also made some clever acquistions in the last couple of years, including Blogger, Keyhole Software (Google Earth)and @Last Software, makers of SketchUp. The only real hit for them recently has been the China censorship issue.

The way I see it, companies like Apple and Microsoft will soon find their out-dated habits hard to continue with. Companies like Google are demonstrating that it is possible to be BFG (ie: Roald Dahl's Big Friendly Giant, not Doom's Bio Force Gun ;-) in this modern age.

See also: this article on innovation.

Sunday, March 26, 2006

Commonwealth Games Results

Well, our medal results are almost final, with only the Mens Cycling Road Race outstanding. As always, New Zealand has performed exceedingly well in the Commonwealth Games, and we are currently 7th in the country rankings. My congratulations to the competitors for their performance, and to Melbourne for providing a great location for the games.

Tuesday, March 07, 2006

Wednesday, February 15, 2006

eMusic blows goats!

Well, I downloaded the latest version of Winamp the other day, and decided to try out the offer for 100 free MP3s from What a waste of time. I should have seen it coming. I mean, why else would any self-respecting dotcom site be willing to give away 100 MP3s?

I was expecting to see a huge variety of music from some of the major artists like Coldplay, Eminem, Prodigy, etc, but really it's a whole load of flipside tracks that no other company was willing to buy. It can be broken into three main categories:

- startup bands who don't have (read: will never get) a record deal,
- flipside tracks you'd expect on compilation albums, and
- tracks from dead people.

You may be one of those who are interested in that sort of thing, but it's not my cup of tea.

I prefer coffee...

Sunday, February 12, 2006

Another usability rant...

It continues to amaze me that there are a large number of developers, developing Microsoft Windows user interfaces, who haven't heard of the Offical User Interface Guidelines. That's right, Microsoft supply freely accessible information on how user interfaces should look and behave when they are running under Microsoft Windows. And most of this is basic stuff. Consider these points from the User-Centered Design Principles:

- The operational assumption is that the user (not the computer or software) initiates actions. The user plays an active rather than reactive role. You can automate tasks, but implement the automation in a way that allows the user to choose or control it.

- Because of their widely varying skills and preferences, users must be able to personalize aspects of the interface. The system software provides user access to many of these aspects. Your software should reflect user settings for different system properties, such as colors, fonts, or other options.

- Your software should be as interactive and responsive as possible. Avoid modes whenever possible. A mode is a state that excludes general interaction or otherwise limits the user to specific interactions. When a mode is the best or only design alternative (for example, for selecting a particular tool in a drawing program) make sure the mode is obvious, visible, the result of an explicit user choice, and easy to cancel.

And there's other basic stuff in there too, like:

- ensuring your application supports the tab key to move between text boxes (yes, Relic Entertainment this applies to you!),

- making menus and buttons are accessible using keyboard shortcuts (Alt-x, etc),

- ensuring you can cancel a process after it has started,

- when menus are used, make sure the File menu handles file operations, the Edit menu handles editing operations, and the Help menu provides access to help documentation.

And ANY user interface developer should make sure that they are familiar with Jakob Nielsen's ten usability heuristics.

It's all basic stuff!

Monday, January 30, 2006

You know you're a geek when...

- you consider shop assistants to be search engines, pre-planning queries to ensure you get the most relevant results for minimum effort.
- the last time you visited a bank was when you opened the account.
- you order your pizza over the Internet.
- you have never met your best friend face-to-face.
- you talk to your mates via Skype, VOIP and TeamSpeak.
- you own more game CDs/DVDs than you do music CDs/movie DVDs.
- your curtains are always shut.
- you drink more cola than you do water.

Wednesday, January 18, 2006

Asterisk - The free PABX!

We've spent the last few nights configuring our new Asterisk @ Home installation, and it's working great. The web-based administration console makes it a doddle to configure the large feature set offered by Asterisk.

Check it out...

Sunday, January 01, 2006

Xmas Rox!

Well, look what I got for Christmas...

Nice, huh? Yep, that's right, it's a new Thermaltake Soprano PC case. This thing has five hard drive bays, two 3.5" removable media bays, and four 5.25" removable media bays. It has a 430W power supply and seven PCI/AGP/PCI Express slots. It's a beauty!

I did my first bit of case modding too: I added a couple of 300mm blue Cold Cathode Fluorescents (CCFL) to jazz the thing up.

Jaycar sell both the tubes and the inverter boards pre-wired, so all I had to do was put a 4-pin Molex connector on the end of the power leads and mount it all. There's one CCFL behind the support that runs horizontally, underneath the power supply, and another sitting at the bottom of the case. I cut up an old reflector from a small fluorescent tube, and mounted that behind each tube to reflect the light in the right direction. A simple mod, but it looks great!