My Photo
Location: Downham Market, Norfolk, United Kingdom

Tuesday, October 24, 2006

Windows generally does a pretty good job of achieving binary application compatibility between platforms. With a bit of care you can quite easily build a single executable that runs fine under all versions of Windows from 95 to Me and NT4 to XP. BBC BASIC for Windows is a case in point; the only place in the code where the version of Windows is detected is when adding an icon to the 'compiled' executable, because the improved resource-editing features in Windows NT/2000/XP make it practical to support multi-format icons.

This is one area where Windows is vastly superior to Linux. The concept of binary compatibility between Linux distributions is largely non-existent (I believe it can be achieved, but it is difficult and probably involves a lot of version checking and OS-specific configuration). Linux users typically think nothing of having to recompile an application from scratch when moving it to a different platform, even when the processor is the same. This isn't very practical for a commercial application, nor for a non-computer-savvy user.

However having sung the praises of Windows in this respect I am really annoyed with Microsoft's decision to discontinue support for Windows Help (WinHelp) in Windows Vista (see BBC BASIC for Windows has used HTML Help for its main help and tutorial facilities from the beginning, so fortunately they are not affected, but its context-sensitive help - both in dialogue boxes and in the main editing window - uses WinHelp. Attempting to access it under Vista simply results in the appearance of a "Why can't I get help from this program" window.

Whilst I have no real issue with Microsoft's desire to ditch WinHelp in favour of HTML Help for the main help viewer - HTML Help is certainly more functional - the same does not apply to context help or popup windows. HTML Help does provide these facilities, but they are substantially less well executed than the WinHelp equivalents. Here are a few respects in which HTML Help's popup windows are inferior:
  • Formatting rules are very strange. Whilst WinHelp will attempt to produce a sensibly-shaped window HtmlHelp will sometimes produce a long narrow strip the full width of the screen.
  • HtmlHelp popup windows don't position themselves to avoid overlapping the taskbar, with the result that the bottom of the window can be behind the taskbar and hence invisible (and of course it can't be moved by the user).
  • With WinHelp you can right-click on the popup and either copy it to the clipboard or print it. HtmlHelp provides no similar facility.
  • Even if you've specified a margin of several pixels in the HH_POPUP structure you don't necessarily get any margin if the popup meets the edge of the screen.
  • To force a line break you have to double line space the source text used by the HTML Help compiler. Single line terminations seem to be ignored, and the lines are concatenated when displayed in the popup.

These are not the only problems. Because Vista does not ship with Windows Help all context-sensitive help in common dialogue boxes is absent! A nice feature of all versions of Windows from 95 to XP is that the common 'system' dialogue boxes (GetOpenFileName, Print, ChooseFont, ChooseColor etc.) come with built-in context help. You can either right-click on a control, or click on the ? button in the title bar, to get a concise description in a tooltip of what that control does. All this has completely disappeared in Vista, and there's no way of getting it back (short of using custom dialogue templates and procedures for every one).

I've read comments that modern programs have so many controls that to provide context-sensitive help for every one would be impractical, and that 'per page' help (e.g. activated using the F1 key) is the way to go. Well, yes, maybe but where does that leave the common dialogue boxes - you can press F1 till the cows come home and nothing will happen. And what about binary compatibility, which is where we came in? Existing programs which use WinHelp simply won't work properly.

Since Microsoft announced that, after all, WinHelp will be available for Vista as a user download some people seem to be planning to continue using it. But the devil is in the detail: Microsoft clearly states that "third-party programs that include .hlp files are prohibited from redistributing the Windows Help program together with their products" so it isn't an option for commercial applications like BBC BASIC for Windows.

So if I want to support Windows Vista I'm going to have to bite the bullet and replace WinHelp with HTML Help for the main context help in the editor window. Context help in the common dialogues will disappear, except for those that I've customised (although whether it's sensible to have context help in just some of the dialogue boxes is debatable). In my view Microsoft didn't properly consider the impact on context help and popups when deciding to drop WinHelp, and there's bound to be a fuss when more people discover it.

Tuesday, October 17, 2006

I have recently been suffering from what I perceive to be shortcomings in Microsoft's API documentation. In particular the following characteristics of Windows API functions seem to be completely undocumented:
  1. Memory alignment requirements. For example the 'PlgBlt' function requires its lpPoint parameter to be DWORD aligned.
  2. Structure padding requirements. For example the 'FtpFindFirstFile' function writes past the 'end' of the WIN32_FIND_DATA structure, because it assumes it has been padded to a multiple of 4 bytes.
  3. Message loop requirements. For example the 'TrackPopupMenu' function must be called from a thread with a message loop (message pump).

In the case of (1) and (2) a C or C++ programmer generally doesn't need to worry, because the Windows header files set #pragma pack(8) which I understand means that the C compiler will automatically align and pad a structure to the size of its largest member. So for example if the largest member is a DWORD then the memory address of the structure is guaranteed to be a multiple of 4 and the structure will be padded to a multiple of 4 bytes.

However this doesn't help a BBC BASIC programmer. In version 5.30a of BBC BASIC for Windows I have arranged that all structures are aligned on DWORD boundaries so that goes a long way to overcome issues causes by item (1). Item (2) is still a problem though, because BBC BASIC doesn't pad structures. The saving grace is that many Windows structures don't need to be padded (they are already the right length) or, if they do, most Windows API functions don't write into the padding region.

FtpFindFirstFile (and I presume InternetFindNextFile) are unfortunately exceptions. They return a WIN32_FIND_DATA structure and if you add together the lengths of all the structure members you get 318 bytes, which is the size of structure that BBC BASIC creates. However a C compiler will actually allocate 320 bytes and what's more that is the amount of data that FtpFindFirstFile returns! Calling the function from BBC BASIC will cause the two bytes immediately following the structure (on the heap) to be corrupted, with disastrous consequences. To avoid that you must add a dummy, sacrificial, member onto the end of the WIN32_FIND_DATA structure declaration.

It is interesting to speculate why those functions write 320 bytes, when the last two bytes don't contain any useful information. One suggestion is that they simply do a structure assignment, which in C copies a number of bytes from source to destination equal to the size of the structure, i.e. the value returned from sizeof(WIN32_FIND_DATA). This is 320 because of the automatic padding. Looked at from this perspective it is fortunate that most API functions don't work that way, and indeed both FindFirstFile and FindNextFile, which also use the WIN32_FIND_DATA structure, return only 318 bytes.

Some Windows 'experts' claim that since the Windows API is a C API (that is, it is documented using C syntax) and since the C header files are considered to be an integral part of the SDK, it is not necessary to state which API functions have special alignment and padding requirements. They would say that any programming language claiming to be binary-compatible with the Windows API needs to support the alignment and packing requirements implied by the #pragma pack(8) directive (and by implication be able to parse the C header files). Personally I think that's unreasonable, and the information should be explicitly included in the docs.

A similar justification cannot be made for item (3) above. Certainly if one is writing a simple single-threaded Windows application, with a conventional window procedure, then it will not be an issue because the calling thread (the only one) is guaranteed to have a message loop. However many non-trivial applications use multiple threads, and the fact that TrackPopupMenu must be called from a thread with a message loop is liable to trip people up - it certainly did me when I first discovered it!

One 'expert' did claim that it was obvious that this was a requirement because the function creates a window, but that's not valid because several other functions that create windows (MessageBox, GetOpenFileName etc.) don't need to be called from a thread with a message loop - generally because they create modal windows with their own private message loops. TrackPopupMenu behaves like a modal window too, at least when called with the TPM_RETURNCMD flag.