Thursday, May 31, 2007

Windows GetSaveFileName Frustration

GetSaveFileName is a Windows API call that displays a standard save file dialog. It has an option to specify a default file name extension that will be added if the user doesn't type one. (lpstrDefExt in the OPENFILENAME struct)

For some reason, this wasn't working. I wasted a bunch of time checking all my code and searching for known problems on the internet with no success.

Then I tried it again and suddenly it worked! But I hadn't changed anything!

Aha! I had changed something - I had typed a different file name. The problem is that if the name you type (with no extension) exists, then it doesn't add the extension. (e.g. I was typing "test" and I had an existing file called "test", with no extension) I guess it assumes that you are "picking" the existing file, even though I was typing it, not picking it from a list.

MSDN contains a huge amount of documentation, but all it takes is one little missing detail and you're in trouble.

Monday, May 28, 2007

LINA portable applications

It's not released yet, but LINA looks like a really interesting project that enables you to run the same binaries on Linux, Windows, and Mac with native look and feel.

Sunday, May 13, 2007

Inside the Machine

I've been reading Inside the Machine by Jon Stokes - An Illustrated Introduction to Microprocessors and Computer Architecture. It looks at Intel, IBM Power PC, and Motorola chips (i.e the ones used in PC's and Mac's), including the latest Core 2 Duo chips.

I'm not much of a hardware person these days, but it was pretty interesting to read about the techniques used to push performance. Apart from bigger caches, I had wondered how modern CPU's used the 100's of millions of transistors they now contain. (A Core 2 Duo has 291 million transistors as compared to the original Pentium's 3 million.)

Considering the subject, the book is well written and easy to read. If you're at all interested in this area, I'd recommend it.

Google Sketchup

Google Sketchup is a free 3D design tool (there's also a Pro version that costs money). I haven't got a particular use for it, but I thought I'd check it out. Here is the results of my first 10 minutes with it:
You can put your Sketchup models onto Google Earth.

Thursday, May 03, 2007

Threading Horrors

Just when I was starting to think that it might not be too bad to make Suneido multi-threaded...

Quick overview of how processes exit on Windows XP

I like this part:
Note that I just refer to it as the way processes exit on Windows XP rather than saying that it is how process exit is designed. As one of my colleagues put it, "Using the word design to describe this is like using the term swimming pool to refer to a puddle in your garden."
I'm not familiar enough with Linux to know how it compares, but even if it is much "cleaner" I suspect it still has it's own "gotchas".

Fields, Controls, & Context

In Suneido, a field has an associated control. For example, if the field is "category" the control might be a pull down list of categories.

For the most part this works well. The problem is that in certain contexts you want a different control or to modify the behavior of the control slightly.

For example, when you are entering new categories it doesn't make any sense to have a control that only lets you choose from existing ones. Actually, you probably want the opposite - to validate that what you enter is not an existing category (i.e. a duplicate).

These are the two main contexts - creating and using.

Once you use a category, possibly as a foreign key, you don't normally want to delete it. But business changes and you may not want to use a category any longer, in which case you don't want it showing up in lists to choose from. We would usually do this by adding an "active/inactive" flag to the categories.

However, when you go to view or modify old data, you don't want it to become invalid because it uses a category that was later marked "inactive".

One solution is to record the date when the category became inactive and so it can be valid for data prior to that date, but invalid after that date. But this means either you make the user enter the date when it became inactive (extra work for the user) or use the current date when they mark it as invalid (but that may not be the right date). The other problem is that if the record where it's used either doesn't have a date, or has several dates, then it becomes hard to apply this. (We use this solution in certain cases where it "makes sense" to users e.g. a termination date on an employee.)

The solution we normally use is to make the inactive categories valid in existing data, but only allow active categories on new data. If there is a list to choose from, it would only show the active categories, on the assumption that you're picking for new data.

This splits the "using" context into "using on existing data" and "using on new data".

But when we come to reports (or queries) there is another problem. If you are printing a report on old data, you want to be able to select currently inactive categories. But at the same time, if you pull down a list of categories on the report options you don't really want to see every category that has ever existed. Most of the time you're printing current data and you only want to choose from the active categories.

Our normal "compromise" on reports is that lists to choose from only show active categories, but the validation will allow you to type in an inactive category.

Another alternative might be to add an option (e.g. a checkbox) to the pull down lists that lets you show/hide inactive categories.

If you use an "inactive as of" date, you still have this problem. You can't use the transaction dates because you're printing a range of dates and different categories will be active for different transactions.

So we now have four contexts:
  • creating - no list to choose from, duplicates are invalid
  • using on "old" data - inactive values are valid for existing data
  • using on "new" data - only active values are valid
  • using in report selections - allow entering inactive values
"creating" requires a completely different control. The various forms of "using" can be the same control (with a list to choose from active categories), but with different options that affect what is "valid".

Currently, Suneido associates a field with a control by the field name. We normally make the "default" control one that handles the "using on old/new data" context. For creating and reports we rename the field to get different controls.

A better approach might be to make the context more explicit. You could allow associating multiple controls with a field name, based on the context. Or the control could be aware of its context and adjust its behavior accordingly. (We partially do this to handle the "using on old data" versus "using on new data" contexts.)

I'm curious how other user interface systems handle these issues. It wouldn't be hard to get a copy of e.g. QuickBooks and see how they address these issues (assuming they do). It's not something that I have seen written up in any of the user interface guides that I've read.