10 Things I Hate About Win32 |
|||||||||||||||||||
|
"Remove head from sphincter, then design an API." I've come across a few problems with Win32 programming while implementing the vGUI library, and they quite often involved a lot of effort in tracking down the solution. Google helped with most of these problems, but they were often hard to find. Therefore the main purpose of this article is to collect this knowledge together in a hopefully Google friendly form. For the record, Win32 is a reasonably good API for the most part, in my opinion, but some parts of it suck big time. The biggest source of problems seems to have been poorly designed common controls, which are then rolled out to millions of Windows users, so the poor developers have to cope with the badly behaving/buggy versions of these controls. Even newer controls that were introduced in Windows 95 have poor design - the tree control, for example, is a classic case in how not to design an API. Almost anything is possible (probably) with this control, but you just try to work out how to do it. It's much too complicated. Anyway, here's the 10 Things I Hate About Win32:
Ok, so it's more than 10, but that's how many things I have to moan about!
Keywords: SetWindowLong, SetWindowLongPtr, GWL_STYLE, GWL_EXSTYLE This is possibly one of the nastiest problems with controls - mainly because there isn't an easy or comprehensive solution. Basically, some controls will only look at their style flags when they
are created. You can, of course, change the style flags at runtime with
So you either have to find out the documented behaviour (good luck), or try it and see if it works. And then test it on all the various versions of the common controls, to see if there are any variations. This is not a pleasant prospect, so I strongly recommend you do not change a control's style flags on the fly, unless it is clearly documented as a valid way to communicate with the control. Or unless you really need to. Fortunately most controls will have some other way of doing what you want to do - usually via a control-specific message - but it's not always obvious from the documentation. For example, the ListView control has a number
of 'extended style' flags. You might be forgiven for thinking (as I did)
that these are normal extended style flags. They're not. To use the
extended style flags you need to send the Keywords: SetParent, WM_COMMAND, change parent, controls, listbox, editbox, combobox This one is even more annoying than the previous item, mainly because it's caused by such a stupid implementation. Put simply, some of the common controls (EditBox, ListBox and ComboBox)
will not allow you to move them to another window using the standard Win32
function, Therefore, avoid re-parenting these controls. I used to do this in vGUI to implement tab controls, but had to change to using a child dialog to avoid this particular problem. See Microsoft Knowledge Base article Q104069 for details. Note: The MSKB article suggests a workaround which is not particularly attractive, but may be of use if you really need to reparent controls. Goddamn Combo boxes(Not written yet)
(Not written yet)
(Not written yet)
Keywords: IsDialogMessage(), WM_GETDLGCODE, keypress, dialog, WM_ACTIVATE If you use any modeless dialogs, then you need to call
The practical result of this is that you need to process
The simplest way to do this is to have a global variable or a singleton object that keeps track of this dialog handle. You can leave the message unclaimed, so that you don't change the existing processing of these messages. By way of example, here is the code from the generic DialogProc from my vGUI library. Some of this code is vGUI specific, but you'll get the general idea:
Then, in your message loop, you just need to check for an active
modeless dialog box before calling
Once you have this mechanism in place, a lot of the dialog's keyboard
handling routines will work much better (i.e. they will actually work).
Keywords: WM_ENTERIDLE, dialog, hang, freeze, infinite loop Well, it might work, but I've never been able to get it to work using the Microsoft documentation. The following code from vGUI offers the clearest explanation of what I've tried and what happens. (This code is disabled in vGUI at the moment, because it hangs the program.)
DrawFocusRect() doesn'tKeywords: DrawFocusRect, wrong colour, EOR This is quite a simple one, but vexing nevertheless. The This is very useful if you are doing owner-draw listboxes/listviews. However,
Either way, there's usually no good reason to make your control use a different
colour focus rectangle to all the other controls in Windows, so you usually
want the standard colour to be used. The simplest way to do this is to call
This ensures any changes you make to the DC do not affect the rendering
done by Here's some source code as an example, taken from the vGUI library. It is used to draw items in a 'listbox' control:
Icons? Any size you like, Sir, as long as it's 32x32Keywords: LoadIcon, DrawIcon, icon, wrong size, scaled Another really annoying one - basically, if you're doing anything with icons in Win32, be aware that many of the Win32 functions do not understand any icon size other than 32x32. A good example of this is If you want to load a non-standard size icon (i.e. other than 32x32) then
use A reasonable rule of thumb seems to be that Win32 functions with
'Icon' in the name may only handle standard icons. Look for a newer
function - for example, Note: The 32x32 size comes from, I believe, two system metrics:
Nested dialogsKeywords: WM_GETDLGCODE, GetNextDlgTabItem, infinite loop, hang, crash Although you can nest one Win32 dialog as a child of another, you should be very wary of using more nesting levels than that - including making a dialog a child of a control in the parent dialog. Due to a bug in the Windows tab order logic, you can put your application into an infinite loop without realising it, and it will take you ages to work out why. For example, in vGUI, my original code for implementing tab controls created a child dialog for each tab, and attached them as children of the tab control. This meant that (logically, and in theory) tab ordering/Z-ordering would always be correct. Sadly, when you clicked in certain controls on the tabs, the program
would go into an infinite loop, because Windows would repeatedly send it
The bug is explained somewhat in Microsoft Knowledge Base Article Q149501, which is about property sheets that exhibit similar problems. The workaround contained in that article did not work in the general case
so I fixed my code to make the child dialogs be children of the main dialog,
instead of being children of the tab control. This works fine - as long as
you ensure the position the dialogs correctly in the Z-order (i.e. above
the tab control in Z-order). Before you try that, read the item on
confusing Z-ordering to save yourself
some grey hairs and frustration.
Keywords: DialogProc, DWL_MSGRESULT, dialog message not processed I've put this one in here because it's an easy one to forget. A However, some messages need more than just a handled/did not handle response - but don't
just try to return this from the What you need to do is this, assuming that
SetWindowLong(hwndDlg, DWL_MSGRESULT, lResult);
This one is fairly clearly documented, but it is very easy to forget now and then, which is why it's in my list. It's especially easy to make this mistake because Windows uses a non-standard
boolean type, Keywords: SetWindowPos, GetWindow, GW_HWNDNEXT, GW_HWNDPREV, hWndInsertAfter, Z order Like the previous item, this is a clarification issue. The documentation is not wrong - it's just a bit obtuse in places. Windows stores Z-order information in a counter-intuitive way. At least to me, with my background of graphics applications, items in a Z-ordered list are stored lowest item first, highest item last. Windows does not do this - in a list of sibling windows, the first window is the one at the top of the Z-order. For example, here is a screen grab from Microsoft Spy++, looking at the dialog of one of my applications:
This dialog contains a tab control, and two child dialogs that are used to display the controls on each tab (they're the last 3 items in the list). The dialogs need to be in front of the tab control - and they are. You can see from the picture that the tab control is last in the list, which means it is at the bottom of the Z-order. The two child dialogs are before the tab control in the list, which means they are above the tab control in the Z-order. That is a bit confusing, but it gets really confusing with window relationships,
as in calls to The problem is compounded with the hWndInsertAfter Handle to the window to precede the positioned window in the Z order. So when you call You may be thinking "What's the problem? It makes perfect sense!" - in which case, lucky old you. The rest of us have to try to cope with the slightly counter-intuitive terminology.
|