Define Your Lowest Common Denominator

I think of the lowest common denominator as the minimum supported operated system for my apps.

I spent many years supporting apps on Windows 95/98, Windows ME and Windows 2000, but recently I have set my lowest common denominator to be Windows XP (even more specifically for some apps, Windows XP Service Pack 3).

Why? Tons of code wrapped in IF – ENDIF statements, checking for the major version number of the OS: If Windows 98, do this, else if Windows 2000, do this, else if Windows XP, do that, else if Windows Vista, do something else.

Frankly, it just seems a waste of time to continue adding to that code. I’d much rather take out the parts I don’t need anymore. Plus, I gain the benefit of ease in my design decisions.

Windows XP is an eleven year-old operating system. Supporting anything earlier than XP just doesn’t make much sense to me (though depending on your own situation, your mileage may vary).

About two years ago, I had a new customer complaining that an app I wrote just wasn’t working right on his Windows 2000 computer. After a few emails (during which he continually shared with me that he is a professional software developer and he is running Windows 2000 Professional), I asked him which Service Pack he was running (I had successfully tested the app on Windows 2000 Service Pack 4, and assumed anyone still using Windows 2000 would be running SP4).

He wrote back saying he was on Service Pack 2, because in his opinion it is rock solid and he has his development environment set up just right for SP2 and he doesn’t want to mess that up.

He refused to even consider updating to Service Pack 4.

So, I refunded his purchase and said, “When you’re ready to join us in the 21st century, we’ll be here. Just email me when you get a new computer with a newer version of Windows, and I’ll give you a free copy of the software.”

As far as I know, he still hasn’t joined us in the 21st century.

In an earlier post, I mentioned that a lot of my apps these days use the IE web browser control. Lately, I’ve found myself changing the lowest common denominator for IE as well. At this point, I require at least IE8.

Do you have a lowest common denominator for your apps?

 

So, What Is An Ugly App Anyway?

Here’s one:

I call 'em like I see 'em. This is an ugly, u-g-l-y app.

I call ’em like I see ’em. This is an ugly, u-g-l-y app.

The form above is an absolute mess. The colors are bad, needlessly distracting your attention from the task at-hand. The controls are misaligned, and the tab order is not ordered at all (you’ll have to trust me on this one – I moved some controls around after saving the form, and forgot to reset the tab order – have you ever done that?).

What’s with the navigational overload at the bottom of the form? Sure, tooltips help the user know what the buttons do, but come on!

A standard Windows icon in the upper-left corner tells me that no icon was assigned to the form, and (though you can’t tell from the screenshot) the maximize button and close button are both disabled.

How do you close the form? With the close button in the navigation area, of course!

Any guesses as to which button is the close button?

Even worse, I copied the style for the form shown above from an actual online tutorial for building Visual FoxPro applications!

Here’s another ugly form from another ugly app. This one is from one of my apps. I figure if I’m going to point out other ugly apps, I should be fair and point out my own as well:

My Ugly App

I thought it looked great. Would you believe for some users it’s ugly?

Not nearly as bad as the form in the earlier image, is it?

  • It’s got an icon.
  • It can’t be resized, minimized or maximized, but that’s OK – after all, at least the minimize and maximize buttons have been removed, right?
  • The tab order is set properly, so no problem there.
  • The “Read more about Quick Add” uses the Windows color for a link, and when you click the link it opens the help file to the correct entry.
  • Once some search terms and a search name have been entered, the Search button becomes enabled. This prevents me from prompting the user with a MESSAGEBOX(), alerting her to the need to enter a phrase, etc.

Overall, I thought the form had a nice “Windows 7-feel” to it. Professional, polished, functional. Sweet!

But, within three days I heard from a customer, complaining about how ugly it was (you may need to click on the image to see it full-size):

"It's blurry," said the customer to the frustrated developer.

The form running with DPI of 150% (144 dpi), using Windows’ DPI Virtualization

See the title bar? The user has told Windows to set the dpi to 150% (or 144 dpi), possibly because she has a super-large monitor and just wants the text to appear larger. Or, maybe she has sight problems and needs the text to be larger.

Windows did its job and made the title bar and close button larger, but the rest of the form is a little blurry. This is because Windows 7 used DPI Virtualization to draw the form. DPI Virtualization makes the form bigger, but can cause the objects on the form to “blur” if your app is not DPI-Aware. I’ll discuss DPI-awareness later in this series.

Still, there’s something really ugly about this form. Have you noticed it yet? Let’s take a look at the next screenshot:

High Contrast Theme

Ouch! Same form, but using Windows High Contrast (Black) theme

Windows XP, Vista, 7 and 8 all include the high contrast themes, for accessibility reasons.

If you do not design for all possible Windows themes, you may will eventually get a support call from someone who uses a high contrast theme. I mentioned in an earlier post that beauty is in the eye of the beholder – when we add accessibility into the equation we move beyond beauty right into functionality.

It is much easier to design for high contrast themes and higher DPI settings upfront than it is to redesign later.

What’s ugly to me may be beautiful to someone else. Or, what’s beautiful to me may be ugly (or unusable) to another.

“Beauty, like supreme dominion, is but supported by opinion.” – Benjamin Franklin, Poor Richard’s Almanack (1741)

 

Lose THE FoxPro

There are several ways to “Lose THE FoxPro” (or, remove all references to Visual FoxPro) in our apps:

  1. Get rid of the Microsoft Visual FoxPro caption on your app title bar
    If you use a Top-Level form as the main window for your application, this is easy: simply set the form Caption property to the name of your app. Some people who use the VFP _SCREEN as the main window set the value for _SCREEN.Caption to the app name somewhere in their startup code, but this can cause the main window to load with “Microsoft Visual FoxPro” as the caption before the caption is updated to your apps name. For this reason, I recommend adding the following line to the app CONFIG.FPW file and including it with the build (replace TheNameOfTheApp with the actual app name): TITLE = TheNameOfTheApp
  2. Make sure your icons aren’t the “Fox Head” or the “Windows” icon
    This is easy as well. You can attach an icon to your Project via the Project Info dialog in the Visual FoxPro IDE. This icon will be used as the display icon for your EXE in Windows Explorer, and also as the icon for the apps main form (unless you choose a different icon for the form). While you’re at it, be sure to assign an icon to the icon property of every form in the app. This prevents all of your secondary forms from having the “Windows” icon. Not good with creating icons? I recommend Axialis IconWorkshop, which just happens to be on sale until December 31st.
  3. Trap unhandled errors and report them as an application error, not a “Visual FoxPro” error
    Error handling is more important than ever if you want to make sure your users do not see a message about “Visual FoxPro”. I’ve been using a modified version of the error-handler form Craig Boyd posted on his blog about four years ago. Craig also created some screencasts about error-handling. All of the links are at the end of this post.
  4. Name the app according to its purpose, not the tool it was written in
    Hey, I love Visual FoxPro as much as anyone, but I’m not naming my apps with the words “visual“, “fox“, “foxy” or “pro” in the name. I’ve seen enough “Visual Foxy Accounting Professional 2005” and “Foxy FeedReader Pro” apps to last me a lifetime.
  5. If you must display a MESSAGEBOX(), make sure to include a title
    Otherwise, the message comes from “Microsoft Visual FoxPro”, instead of from “The App You Paid Me Good Money To Write For You”.
  6. Include an ON SHUTDOWN command
    If you don’t, your users will likely see a lovely “Cannot quit Visual FoxPro” message when they try to exit the app. This is an easy fix for a common problem (as common as, “Why does my form flash, and then go away when my app starts?”).

Feel free to jump in via the Comments section. Do you agree that we should “Lose THE FoxPro”? Disagree? I’d love to hear your thoughts!

Craig Boyd – Professional Error Handling for VFP Applications
Craig Boyd – Learning VFP 104: Error Handling (A three-part video series)

 

SET PROCEDURE TO MyDll

I use the Microsoft Web Browser Control as the primary UI for an app. This means I need a ton of support files like cascading stylesheets, javascript files, images, etc.

Typically, I create a subfolder in the Windows “Temp” (SYS(2023)) folder for the HTML files, then subfolders for each of the support file types (CSS, JS, IMG). I don’t like to install the support files with the app, because there’s always someone out there that wants to modify the files or just poke around to see how some things get done.

In the past, I simply included the support files in the EXE and did a series of STRTOFILE()’s to copy the files to the temporary location. But as the app (and the number of support files it uses) grows, it really bloats the EXE. So I created a DLL which contained all the included files, with methods to do the file copying.

During startup, the EXE instantiates the OLEPUBLIC classes in the DLL and fires the methods to copy the files. Works great, and it keeps the size of the EXE a bit smaller.

Yep, works great. Except when the DLL doesn’t get properly registered during the install, and the instantiation fails. I get error reports from time to time where the DLL hasn’t been registered. Though I’ve been unable to recreate it here, it seems the common denominator is a particular anti-virus software solution – I’m looking at you, Kaspersky.

So, I’m stuck with needing to manually register the DLL if it hasn’t been registered. I suppose I could use Reg-Free COM, manifest files, elevation, etc. to register the DLL programmatically, but I worry about going through all of that only to find that it still won’t register for the same reason it didn’t during installation (I’m still looking at you, Kaspersky).

I stumbled on an idea earlier this week and thought I’d give it a try. The idea was to SET PROCEDURE TO MyDll. Why not create a DLL, then, instead of going through registering it, just SET PROCEDURE to it and call the methods?

Duh. Can’t do it. It’s an object method, and I haven’t created the object.

So I did a little refactoring on the DLL. It contains one OLEPUBLIC class, which is nothing more than a class definition – no properties, no methods, no nothing. Then I split the methods out to simple FUNCTIONs.

Recompiled the DLL, then got rid of the registration info that was added to the Windows Registry. Then I tried the SET PROCEDURE TO.

The pseudocode looks like this:

IF MyDll.dll Exists then
   SET PROCEDURE TO MyDll.dll
   MyFunction()
   RELEASE PROCEDURE MyDll.dll
ENDIF

And, it worked! No registration, just a simple check to see if the file exists. If it does, run the method. This assumes, of course, that the method exists (since there’s no PEMSTATUS I can check – remember, no object or class exists).

Now for the disclaimer (before the comments start rolling in):

  1. Yes, I am well aware that SET PROCEDURE TO was not designed to be used this way.
  2. Yes, I know the best way to “fix” the problem is to determine what the problem actually is and find a “real” solution.
  3. Yes, I know this wouldn’t be considered “best practice”.

On the other hand, though, my purpose for this is pretty specific, and was completed very quickly. Plus, with a little bit of coding on the app’s update procedure, I can now update the DLL anytime I need to, without having to install a whole new update. Best of all, it appears that the problem has been solved.  I still try to instantiate the object first, but if it fails I fallback to the new “unregistered” DLL. Not a single error report since the last update.

So far, so good…