Here is the latest edition of my known bugs and "features" in Windows

Problems with Messages

You might innocently suppose that if you post yourself a message, the operating system would be capable of dispatching it to your window procedure without messing it up. Unfortunately this is not true at all. For some messages the operating system gets in the way and does things to the message. This is especially true of the WM_CHAR message:

  1. If you post yourself a WM_CHAR message on Windows NT/2000, the OS will zero the high word of wParam preventing you from using this part of the message for your own purposes. The message is passed correctly on Windows 9x.
  2. If you try to mix DP4 with Borland C++, and possibly MFC, in some situations these products may hook messages and/or take over dispatching of messages. These products dispatch want they want to and not what you asked for - in the case of WM_CHAR messages only the lowest byte of wParam is dispatched for some (but not all) values of wParam! Borland sends a whole pile of messages at the top of the WM_USER range that it has invented and which may clash with your own WM_USER messages.

From 4.523/4.619 the DP4 terminal manager will accept the message RegisterWindowMessage("DP4_DDI_INCH") as a synonym for WM_CHAR messages to stop mangling.

WM_ACTIVATEAPP

If you right click on an applications task bar icon to get a context menu and then click in a different application the application will lose activation but won't get sent a WM_ACTIVATEAPP message in good time. It won't see the message until it gets another message. You can solve this problem by posting yourself (and ignoring) a WM_NULL message when you get a WM_EXITMENULOOP message. Most of Microsoft's own apps seem to be immune to this problem - possibly because they mainly have timers.

WM_NCPAINT messages

According to Microsoft wParam for this message was unused in 3.1, and is an HRGN in the Win32 API. Actually it is identical in all versions of Windows since 3.1. wParam is either 1, indicating the complete non client area needs painting or it is an HRGN indicating the area that needs painting. In the latter case the HRGN is in screen coordinates so you have to fiddle about with origins (strangely DCX_INTERSECTRGN seems to do this automatically, but as it crashes Windows it's not a lot of help)

WM_QUERYENDSESSION/WM_ENDSESSION

On Windows NT if you reply with 1 to WM_QUERYENDSESSION you will get a WM_ENDSESSION message regardless of whether the session actually ends. This message works properly in Windows 9x and 3.1 but is unusable for a server type program in Windows NT. Server programs have to return 0 to WM_QUERYENDSESSION messages if they get one and a client app is active. (This is probably why you can shutdown a computer when Exchange is running)

If a service is allowed to interact with the desktop it will receive these messages when the current user logs off, which probably should not happen.

WM_WINDOWPOSCHANGED

Microsoft include the following advice on this message:

The WM_SIZE and WM_MOVE messages are not sent if an application handles the WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient to perform any move or size change processing during the WM_WINDOWPOSCHANGED message without calling DefWindowProc.

You need to bear in mind that the position and size of a window can change without your getting WM_WINDOWPOSCHANGED messages. For example you get WM_MOVE and WM_SIZE messages at creation time. Calling ScrollWindow() or ScrollWindowEx() to move child windows sends move messages without WM_WINDOWPOSCHANGED messages. I've not investigated what MoveWindow() does as I always use SetWindowPos().

Problems with Paint Messages

Microsoft Documentation states:

This does not mean that the message retrieved by GetMessage() is never a paint message, but that if you don't do something that causes the window to be validated you get the paint message again.

It also states:

This is very misleading. Paint messages don't have update regions, only windows do. When PeekMessage() does pick up a paint message (which it indeed does sometimes) GetUpdateRect() would return 0 if the window had a null update region. However quite often PeekMessage() will return a paint message for which the update region is not null. This is probably something to do with the way Windows combines multiple paint messages for a window, or it may just mean that something got invalidated after the message was retrieved. If you use RedrawWindow(hWnd,0,0,RDW_INTERNALPAINT) to get a null update region paint message I am not at all sure how it interacts with normal paint messages - if the window is already invalid you will inevitably get an ordinary paint message before the internal one. I do use RDW_INTERNALPAINT a lot, but combine it with a 'dirty bit' set in the data associated with the Window and use RedrawWindow(hWnd,0,0,RDW_VALIDATE|RDW_NOINTERNALPAINT) to validate windows. Incidentally you can get paint messages with null update regions from windows as well. For example if you restore a minimised app which has child windows entirely covered up by other windows you sometimes get this kind of paint message for the covered windows. You can also get them when dragging windows across other windows. If you do get a paint message like this you still need to validate the window otherwise you'll keep on getting the paint message, so that's another lie in the statement above.

GetQueueStatus(QS_PAINT) (or any call that includes QS_PAINT) is unreliable. The function can (and frequently does) return TRUE without a subsequent call to GetMessage() or PeekMessage() returning immediately. (This is more or less stated in the documentation). What the use of this function is escapes me, as it would only be useful if a TRUE return indicated that GetMessage()/PeekMessage() could be called successfully. In many cases where you want to process a message if there is one but not hang around otherwise MsgWaitForMultipleObjects() is the best function to use.

Z Order and Processing paint messages in the right order

If your application has more than window you need to consider potential problems that can arise when Windows overlap. Microsoft documentation states the following:

The update and visible regions of a child window are also affected by the child window's siblings. Sibling windows are any windows that have a common parent window. If sibling windows overlap, then setting the update region for one affects the update region of another, causing WM_PAINT messages to be sent to both windows. Sibling windows receive WM_PAINT messages in the reverse order of their position in the Z order. Given this, the window highest in the Z order (on the top) receives its WM_PAINT message last, and vice versa.

This is completely untrue. When a window is first shown or drawn again with RedrawWindow() , then its child windows receive paint messages in Z order, so that the child that is supposedly on top gets its paint message first. If other child windows overlap it they will (perversely) draw over it unless you use WS_CLIPSIBLINGS. When a window has the clip siblings style (which all top level windows are forced to have automatically by Windows) then it does not draw over windows notionally higher in the Z order.

In fact this is so reliably wrong, that you can simply mentally invert the Z order for non WS_CLIPSIBLINGS windows - to bring one to the top you would use SetWindowPos(hWnd,HWND_BOTTOM,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE).

However be warned that for WS_CLIPSIBLINGS windows Z order does work correctly - and it will therefore cause problems if you sometimes use it and sometimes don't. You need to remember that when you give a window WS_CLIPSIBLINGS style it will obligingly hide itself behind the windows above it in the Z order, but it won't appear above non WS_CLIPSIBLINGS windows beneath it. This can lead to some very strange messes on screen.

When Windows are created they always go to the bottom of the Z order, unless you use WS_VISIBLE on a top level window, (personally I only use this on child windows and when the parent is still invisible), so if you create windows with the ones you want on top first you'll get good results without WS_CLIPSIBLINGS. If you use WS_CLIPSIBLINGS you'll have to create the windows the other way round or change their Z order afterwards.

Using WS_CLIPSIBLINGS is a disaster for performance, as you should use CS_PARENTDC class style for child windows, and WS_CLIPSIBLINGS and CS_PARENTDC are incompatible with each other. (I'm not sure which 'wins' - I think WS_CLIPSIBLINGS does, though the warning message issued from DBWIN with 3.1 debug binaries implies the reverse).

Before deciding to rely on Z order being back to front I did find a way to make overlapping child windows paint correctly according to Z order: when processing a paint message enumerate the children in reverse Z order (by calling GetNextWindow() or GetWindow()) and call UpdateWindow() for any that have a non null update region. You should do this after validating the parent window and should test that the parent window has not become invalid again. Otherwise your program will behave disastrously with full drag enabled. (Actually I don't call UpdateWindow() I send myself a paint message directly which seems to work fine and is quite a bit faster, but perhaps that only works because I deal with paint messages in a special way).This won't deal with the situation where two overlapping children have invalid regions but the parent doesn't, but you can cure this by sending an internal paint message to the parent to get the child to paint.

I also hardly ever use WS_CLIPCHILDREN as it stops you using CS_PARENTDC. CS_PARENTDC should be used as much as possible - it really does improve performance. You just have to be careful to make sure your paint code is automatically clipped to the window (e.g. by not using DT_NOCLIP on text output into the window)

It's a good idea to use WS_EX_NOPARENTNOTIFY for child windows unless you really want the notification messages.

Full Drag

If you want your program never to display incorrectly with full drag enabled then you should add the following lines of code to your processing of paint messages for windows that have children.

if (GetUpdateRgn(hWnd,hRgn,FALSE))
  RedrawWindow(hWnd,0,hRgn,RDW_ALLCHILDREN);

For some bizarre reason this line of code actually improves performance!

Otherwise it can happen that the update region for a child grows between Windows deciding to send you a paint message for it and your retrieving and validating its update region. Since Windows will also invalidate the parent in these circumstances you will end up with a window with gaps in the child windows where the parent shows through. Adding the above code stops this happening.

You must make sure that you never attempt to send or receive messages in the middle of painting Windows. I wasted hours and hours trying to work out why the non client part of my windows disappeared when I sized a window with full drag enabled. The reason was they were outputting diagnostics to a window in a different application via WM_COPYDATA messages. For some reason this completely stopped the non client area from painting.

Painting Windows

According to Microsoft you're supposed to use BeginPaint() EndPaint() to deal with paint messages. Well they do work (though all the problems I've mentioned above happen even when you follow Microsoft guidelines to the letter) but they don't get best performance or anything like it. One reason is that when you use BeginPaint() EndPaint() you almost always get WM_ERASEBKGND and WM_NCPAINT messages (you get WM_ERASEBKGND messages even for WS_EX_TRANSPARENT windows!!). If your window doesn't have a non client area the WM_NCPAINT is a waste. I never erase the background until I do the paint, as if you can use some kind of 'opaque' text output function you won't get flicker whereas erasing and drawing in transparent mode flickers badly. In any case there can be quite a long delay between a WM_ERASEBKGND and a WM_PAINT message leaving your application looking a bit bare. I moved to using a sequence like GetDC(), GetUpdateRgn(), SelectClipRgn(). If I do need an WM_NCPAINT I call RedrawWindow(..,ERASENOW). (Ironically it's the only way to get NCPAINT messages in good time, but you don't get a WM_ERASEBKGND message unless you'd caused one by doing InvalidateRect(hWnd,0,TRUE) or something. To validate the window (which you must do ASAP) I call RedrawWindow(hWnd,0,hRgn,RDW_VALIDATE+other_flags). (NB only validate the region you actually paint, unless it's null when you have to validate everything to stop the paint message being sent again - in 32 bit Windows another thread could cause something more to get invalidated in the mean time) I found the RDW_NOFRAME cannot safely be used to suppress unwanted WM_NCPAINT messages. If you pass this flag it stops frames being painted for child windows as well even if you use RDW_NOCHILDREN!!. Amazingly these 5 calls together are faster than a single call to BeginPaint() (at least for me). Not enough to worry anyone except a control freak perhaps . The only drawback is that you get warnings about not calling GetUpdateRgn(,,TRUE) or BeginPaint() from the debug binaries. I don't understand why Microsoft bother to go to the trouble of providing APIs if they're going to complain when you use them... However in the course of experimenting I did discovered some more fairly serious problems with some Windows APIs.

By the way I'm not the only programmer who doesn't like using BeginPaint(), EndPaint(). At least I generally follow the recommended practice of sending myself paint messages to update the screen. Most of Microsoft's text editors studiously avoid sending paint messages as far as is possible - presumably so they can use faster GetDC(), ReleaseDC() type logic to update the screen.

WS_EX_TRANSPARENT windows

These don't work properly. I used to use this style for Windows that were supposed to be non rectangular (e.g. rounded buttons). It's much better just not to use WS_CLIPCHILDREN, then the parent will show through anyway. If two WS_EX_TRANSPARENT maps ever overlap Windows gets very confused. You may need WS_EX_TRANSPARENT on windows that don't have CS_PARENTDC class style.

WS_EX_TOPMOST windows

If you try having owned Windows that have WS_EX_TOPMOST style be prepared for Windows to randomly decide to stop them being topmost or to make the owner topmost as well. The only solution is to sit on WM_WINDOWPOSCHANGING messages and add flags such as SWP_NOZORDER and SWP_NOOWNERZORDER. (I usually use SWP_NOSENDCHANGING on my own calls to SetWindowPos() so if I see a WM_WINDOWPOSCHANGING message it usually means I'm having a fight with Microsoft). Incidentally SWP_NOOWNERZORDER allows an owned window to go behind its owner - something that is potentially quite useful as anyone who has ever tried using the 'pin ' controls to stop windows disappearing in MSDEV will realise.

Efficient moving/resizing of Windows

If you have to rearrange a Window (for example resizing it in response to Display properties 'Appearance' changes you'll probably want to call SetWindowPos() a lot. If you do then you'll get a lot of WM_PAINT and other messages. You can use SWP_NOREDRAW but this can cause problems, especially on the 'frame' window as it stops paint messages going to any unrelated windows that get uncovered. One solution is to use DeferWindowPos(). However this API is problematical as it's not guaranteed to work. Certain combinations of calls to this function just don't work even when they claim to . (For example if not all the windows concerned are siblings). There is a semi undocumented parameter called SWP_DEFERERASE (it's in the published header files but not mentioned anywhere). This is very useful as it stops WM_ERASEBKGND messages getting sent to uncovered Windows until they're painted, which cuts out a lot of unnecessary flicker (and messages). In a similar vein, if you're going to destroy a lot of Windows at once it is a good idea to hide them with SetWindowPos(...,SWP_DEFERERASE|SWP_HIDEWINDOW) first.

Incidentally SetWindowPos() documentation states:

This may or may not be true (I think not) , but you still need to pass SWP_NOSIZE and SWP_NOMOVE if you didn't intend to change the size.

It's worth specifying SWP_NOSENDCHANGING on calls to SetWindowPos() unless you need the change to fiddle with the call in the WM_WINDOWPOSCHANGING message. This flag has always existed and worked (it was in the public 3.1 header files but not mentioned in the documentation).

Visible regions of Windows

Each Window has a visible region. When you get a DC for a window output is always clipped to this area and there is not a lot you can do about it (thank goodness). Windows maintains the visible region automatically, and you can't change it (there are new APIs for doing this actually but I've not gone into them). Unfortunately it is possible for Windows to lose track of what this region should be.

For example if you set the WS_VISIBLE bit directly then even completely invalidating the Window and attempting to redraw won't do any good, because Windows won't know its really visible now. That may mean it's best to use ShowWindow() to show or hide a window. Unfortunately you get some probably useless messages when you do this. I believe that as long as you change something fairly dramatic at the same time as setting/unsetting the visibility bit (e.g. move/resize/change Z-Order/reparent) it is safe to cheat like this. Changing a style bit results in WM_STYLECHANGING messages and WM_STYLECHANGED messages, so using SetWindowPos with SWP_NOSENDCHANGING|SWP_HIDEWINDOW/SWP_SHOWWINDOW is better (and possibly more legitimate).

I've found that various APIs can cause a Window to go to sleep like this. For example there is a proper way to do this on purpose (by sending a WM_SETREDRAW message to DefWindowProc()). However SWP_NOREDRAW passed to SetWindowPos sometimes effectively does this too (for example if you move an obscured window up in the Z-order with SWP_NOREDRAW.

In early versions of 4.6xx I tried using WM_SETREDRAW. It wasn't a success because of this kind of problem.

Buggy APIs

DrawText()

Depending on whether your program is 16 bit or 32 bit, and the version of Windows it is running on the DrawText() function can behave in quite different ways.

DeferWindowPos

See Efficient moving/resizing of Windows.

GetDCEx()

There is a documented API call GetDCEx() which is supposed to be a sophisticated version of GetDC(). A very slight amount of disassembly of Windows 3.1 shows this is more or less the 'Real' function that GetDC and BeginPaint() wrap (I don't know how to debug more recent versions of Windows but I'm sure they're a lot more similar than Microsoft would like us to think). Unfortunately Microsoft have not fully documented it and not all the documented features work.

There is a flag called DCX_INTERSECTRGN. The documentation of WM_NCPAINT messages suggest that it is jolly useful for processing them. It is for a little while. Then (on Windows 95 at least) all running programs GPF at intervals . Amusingly your program is usually the last to die. There is obviously a massive memory leak, but it doesn't show up with resource meter (which dies first!) . I rather wonder if this isn't the cause of the mysterious GPFs that always seem to kill Windows 95 after a while. I intend to investigate this on NT more thoroughly - I left a program looping continuously creating and destroying Windows and using this API. It takes around 20 minutes to kill 95. NT survived for as long as I tested it, but perhaps it wasn't long enough (about 2 hours.)

In the 32 bit version a new flag DCX_VALIDATE is documented. It is suggested that combined with an otherwise undocumented flag DCX_INTERSECTUPDATE that you have an equivalent of BeginPaint(). You don't. Neither of these flags works at all (at least on 95 and 3.1, I'd lost interest in them by that time and haven't tried them on NT). If you try to use them in a 16 bit Windows program DBWIN reports them as invalid flags (though it does pass them through to the core function). BeginPaint() does not use either of these flags. It uses some other undocumented flags. I have the 'real' windows.h for Window 3.1 (it comes with the far east SDK). This reveals that a lot of the 'new' features of the Win32 API were lurking in 3.1 all the time, including all these flags and more. But the flags BeginPaint() passes to GetDCEx() don't make a lot of sense. There are some other interesting undocumented flags on this function but I've not investigated them all yet. They probably work better than the public ones.

GetExitCodeProcess()

On Windows NT it is impossible to discover when a 16 bit program terminates by checking GetExitCodeProcess() or using WaitObject() as the process handle you have is for an NT VDM. There is an incredibly complex work around for this problems documented in the MSDN KB. In DP4 I check the executable type and OS before running programs (e.g. in the loader) and don't try to get exit codes from 16 bit processes.

GetQueueStatus()

You would call this function to discover something about the state of the message queue, but the return value is so unreliable that the function is all but useless.

LoadLibraryEx()

The LOAD_LIBRARY_AS_DATAFILE flag does not work very well on Windows 9x. The handle returned does not work with high level resource loading functions such as LoadIcon(). It does work with the low level resource functions such as FindResource()/LoadResource(). Using these you can implement your own versions of the high level functions, so why Windwos 9x does not use the same code to implement the high level functions is a mystery.

RedrawWindow()

This extremely useful function (the nearest you can get to the internal functions for invalidating and painting and validating a window and its children) is not implemented properly in Windows CE. In Windows 3.1 the function sometimes causes DBWIN messages about invalid regions from inside Windows for no apparent reason - in this case use one or mother APIs to replace it if you don't want your program to crash in the checked build.

Small Icons

Windows 95 programs are supposed to have small 16*16 icons. Unfortunately the task bar icon is very likely not 16*16. If you run with an appearance scheme such as Windows standard (Extra large) or Pumpkin (large), or you're running a Far East version of Windows task bar icons can be 20*20 or 24*24. In this case Windows 95 /NT will mangle your 32*32 icon. If you are running with 65536 or more colours it'll do a good job. If you only have 16 or 256 colours it'll look a mess in all probability. It may be worth adding icons of custom sizes (at least 20*20 and 24*24). Unfortunately this can confuse some configurations of Windows (possibly a driver related problem). Make sure the 32*32 and 16*16 icons come first in the icon file.

Undocumented Windows

Bitmap display

You cannot use regular BMP APIs to load bitmaps with palettes, at least in 256 color modes, as the palette information is ignored and inaccessible. You have to load the bitmaps as low level resources and do your own palette management. Unless you are absolutely certain of the entire contents of your window it is safest only to use background palettes. If you use a foreground palette then you can get an endless series of paint messages as two palettes fight to the death.

Although it is not explicitly documented as doing so the SetDIBits() and StretchDIBits() functions can work with compressed bitmaps if you fiddle with the header structures. I believe that once IE4 is installed these functions can also decompress and display JPEGS directly (another instance of an unfair advantage given to MS's own programmers)

Windows 95 and the Win32 API

About the only good reason for writing Win32 programs on Windows 95, assuming they would have been comfortable small or medium model programs, is that are a lot of Win32 APIs without a 16 bit equivalent. If your program is reasonably straight forward (without all the high technology things such as OLE,ODBC,OPOS or Owhatever) there's a good chance it would go up to 30% faster as a 16 bit app (on Windows 9x, 32 bit apps are much faster on NT). But guess what - most of the APIs DO exist.

In the first place API changes that are just new flags will almost certainly work. As I mentioned earlier a lot of these flags were really there all the time.

The Windows 95 SDK includes a new windows.h and libw.lib. This has a few new functions and flags in, but not many - Microsoft don't want anyone except themselves to be able to write 16 bit programs (there are plenty left in Windows 95).

Some changes only happen when your program is marked as a Windows 95 program (version 4) . For example there are certain messages that only Windows 95 programs get. The version of RC in this kit allows you to do this. Strangely enough a considerable number of Windows 95 programs that were direct replacements of Windows 3.1 programs only differ in this one byte!

However lots of the new APIs are accessible to 16 bit apps - provided they import them dynamically or in a .DEF file. Of course you have to guess whether the parameters of the functions are going to be like the 32 bit functions or like 16 bit functions - but this is only a problem for pointers to structures. I've not done much of this - just used it for new SystemParametersInfo() values only documented for the Win32 API and for implementing proportional scroll bars in 16 bit programs, but I suspect almost everything exists, except Kernel functions.