There are really two sides to making your application robust against future change.
- Writing the application to maximize current and future compatibility.
- Writing the code to maximize future maintainability and minimize necessary code and more importantly architectural changes.
64 Bit
Don't make coding assumptions that won't work for 64-bit. This has two flavors, the first being the coding techniques used to be able to compile code for both 64 bit and 32 bit. Interactions with APIs, COM, and dealing with pointers are all potential hazard areas.
The second major aspect of 64 bit compatibility is making sure no assumptions are being made that interfere with the conversion layer between 32 bit and 64 bit Windows called WOW64 (which follows the same patterns as WOW32, the layer that allows 16 bit applications to run on the 32 bit Windows OS).
So what do you need to do for that? Besides getting into the appropriate beta programs and testing your applications on 64 bit versions of Windows and fixing any problems you find, make sure you're using all the APIs in the right way. If you're sticking a pointer in a long because you expect that you'll be able to get it back later as a long, you may run into big problems if that isn't expected by the layer that translates the 32 bit addresses into 64 bit ones and back.
Version-Based Behavior Changes
If you would like to take advantage of new functionality in the operating system without increasing your WINVER (which should be set to the minimum version of Windows that you support), you can create a thunking layer where you have a set of function pointers that you fill in when running on the newer version by using LoadLibraryEx to load the library that holds the function and calling GetProcAddress to get the pointer to it.
The advantage is that you're not statically linked to the library (that's also the disadvantage), and you can modify runtime behavior based on whether the function point is NULL (the value it should be initialized to, of course).
The other way you can modify runtime behavior based on OS version is by looking at the values returned by GetVersionEx. When would you do which? As new service packs are released for released versions of the operating system, sometimes functions or functionality are backported. If your logic depends on the function existing, go with the LoadLibrary/GetProcAddress as your method for determining which logic to use. If you're based specifically on the version of the OS (much more rare, but possible), base the logic on the GetVersionEx.
Maximum Compiler Warning Level
Compiler warnings give you the information you need to know about for possible compatibility problems in the future in addition to the issues you have now. I'm a big believer in setting the maximum compiler warning level, making warnings errors, and turning on any other compiler options I can find that can help me avoid or find problems earlier. This would be the combination of /Wall and /WX, to turn on all warnings (even those that are off by default), and to treat all warnings as errors.
Talking again about the 64 bit section above, there is a compiler option to give you warnings on possible 64 bit compatibility issues (/Wp64). This may give you some false positives, so you should sanity-check by actually compiling for 64 bit architectures.
IPv6
Networking code needs to be written in an IP version independent manner. The same code would work on IPv4 and IPv6 networks without modification. Microsoft provides a utility called 'checkv4.exe' which helps to identify code that might cause IPv6 problems and makes conversion easier.
Even if you don't plan to write to IPv6 specifically, if your program does network interaction you're better off making it IP version agnostic.
High DPI
I won't say too much about writing code for High-DPI displays beyond providing the link and saying this: Monitors are getting better at a much faster rate than before, you should expect the resolution and DPI density to increase quite a bit. This screws up a lot of programs in the areas of text (/ fonts), images, UI layout, and sizing issues for custom-drawn UI. Most displays are 96 DPI, and most application developers totally assume than all displays are 96 DPI. Weird sizing, overlap, and ugliness problems rear up when that assumption isn't true.
Re-Invent Less, Build More
I've noticed a tendency amongst many software developers to want to reinvent wheels. Sometimes they're convinced they can do a better job, sometimes solving a well understood problem once again is a welcome comfort for a programmer who is usually coding without a net.
Things that people enjoy rewriting:
- Memory managers
- Windows controls
- Network stacks
- Caches
- Encryption
Even for cases where someone can do a better job than Windows developers for systems such as these, it is still a bad idea. First, any new code will have bugs, and those bugs will take significant testing to work out. This assumes that the new code gets the same scrutiny as the code built into the system platform, which is unlikely for all but the largest projects (and even then, pretty unlikely). It must also get enhanced and evolved to match the code built into the platform as new Windows versions are released, patches and made, and service packs created.
When it gets down to it, though, the real problem is that they're rewriting code they don't need to. They're not writing code that adds value to their product that is unique and added value for their customers, they're redoing something for minimal benefit that will cost more in terms of development, servicing, and maintenance over the long term.
Comments