This is more than just a quirk. Lets go through the bogons here:
1. Signal system confuses notifications with commands
2. API ties windows to threads
3. System allows you to send WM_DESTROY from one thread to another even though it provides undefined behavior
4. DestroyWindow() can only be called from a particular thread (related to item #2)
5. The API doesn't detect that the window has been destroyed as far as the application is concerned (whatever happens in ForgetWorkerThread() or PostQuitMessage(), the window actually goes away) without a DestroyWindow() call.
This is about an OS-layer API, not a windowing toolkit API. Tying the window to a thread at this layer makes it impossible to have a single-threaded UI application. It is inflexible.
Sorry. I've read your comment a couple of times and am unable to make any sense of it. Win32 is a (dated, clunky) API for writing GUIs (in part). "OS-layer" is babble in this context.
And yeah, i would stop signing your posts - it comes across as conceited somehow; like each comment is a big deal.
"This is about an OS-layer API, not a windowing toolkit API"
What does that even mean? Of course you can have single-threaded UI applications in Windows, the majority of them are. Have you ever written a Windows application in C or C++?
OK, so what's the alternative? Automatic locking at OS-level? How can the performance penalty in those many cases where it is not needed be justified? Same goes for your arguments 3, 4 and 5 btw. Why should such low-level functionality impose important design decisions on those who follow the documentation, just to protect the beginners who don't? I much prefer my raw control, thanks - and I'll happily take the few times I shoot myself in the foot with it, err, with it.
Despite its warts and its legacy cruft, the win32 is quite flexible, and easy to understand - at a very basic level, not much studying of under-the-hood stuff required. For an OS-level API, it packs a lot of power into a small set of concepts.
> Please don't sign comments, especially with your url. They're already signed with your username. If other users want to learn more about you, they can click on it to see your profile.
Well it seems that we hit the nesting limit, so I will reply to this post. I'm not including a URL, I'm just signing my name. Please keep in mind that is a guideline.
Wow, thanks for exposing me to how braindamaged win32 really is.
To be perfectly fair, this arrangement (which I don't like either) is actually inherited from the Windows 3 API (Win16) and very likely goes back all the way to Windows 1.
Irrespective of the article and its crux, the analogy is plain weird. Maybe he had a particular aspect of a prank call in mind, but it's tough to relate to.
Then WM_DESTROY should have been called NM_DESTROY, since it is clearly a notification message not a command. The user is not wholly innocent here, but the Windows API team should be blamed for the misleading naming as well.
No, because the NM_ messages are for standard controls only. There are more messages in the WM_ family that aren't really 'command' messages, but more 'notification' messages, like WM_CREATE. It's not like one can create a window by allocating memory, casting to an HWND and sending WM_CREATE to it.
I guess the conclusion is that one cannot blindly take WM_xxx message and think 'oh I can use ::SendMessage() and friends to make windows do certain things'. Only the documented behavior are valid use cases.
I guess one could say that the WM_xxx message should've been split up into more subclasses, but where to draw the line? I'm quite fine with using WM_xxx for all the fundamental window manager messages, as long as one minds their documented behavior there is no problem. (I realize that this last sentence can spark a whole discussion by itself on how far self-documenting a 'sane' API should be, but that's rather subjective imo).
I disagree with you and I think the grandparent is right. Even though the handle based architecture of the wind32 system is not object oriented, it was meant to have some object oriented features. If you think about this system as one in which a window handle is a window object and a message to that window represents a function call, then the WM_CLOSE is a public method that anyone can call. WM_DESTROY, on the other hand, would be a protected method that can only be evoked by the window system. Even though they decided that the overhead of a full OO system was too much, they still could have made the intention more clear by doing something like this:
WM_PUBLIC_CREATE and
WM_PROTECTED_DESTROY
Then there would be no confusion about who was supposed to send the messages and how they were meant to be interpreted. Of course they don't have to use this exact terminology, but you get the idea.
That's rather misleading - if WM_SETTEXT is a 'notification' than so is every function call in your program. There's no guarantee it will work, either, but neither is there a guarantee that 'myWin' will have a property 'setText' in JS or Python.
Using message queue semantics to implement Java-style OOP is the kind of thing that would put Alan Kay into a rage.
The failing of the Windows message system is not distinguishing between the 'command' messages that you are allowed to send to a window to effect some action (WM_NOTIFY and friends) and the 'event' messages caused by the window manager (WM_DESTROY, input events etc.). It should be apparent that getting a window to respond to an event is not the same thing as effecting that event.
Crucially, OOP languages without access modifiers (or ones you can #define away, like C++) have this same problem. If an ignorant programmer thinks he can destroy a window by calling its _onDestroy() function, you have solved nothing.
With sufficient language support you can probably save users from themselves in this case, but there are many other cases where a user can do the wrong thing by not understanding the system. At some point the effort doesn't justify itself.
"If you think about this system as one in which a window handle is a window object and a message to that window represents a function call, then the WM_CLOSE is a public method that anyone can call. WM_DESTROY, on the other hand, would be a protected method that can only be evoked by the window system."
Yeah well there's the error in your thinking, and exactly what the OP was about: window messages aren't that. Every message is a callback, a 'notification' if you will. If you call RegisterClass() and pass a WNDPROC that does nothing (ignores all messages), it can still be created/destroyed with CreateWindow()/DestroyWindow(), and the 'notifications' it gets (WM_CLOSE and WM_DESTROY) can happily be ignored ('happily' as far as the window manager is concerned).
Win32 is not an OO API, and thinking of it too much in those terms will cause all sorts of confusion. Win32 is a C api, and yes things like MFC put an OO wrapper around it, but at some point the abstraction will start to leak if you take it to the extreme without thinking about the underlying system. Win32 is based around the OS calling back into your code through a well-defined callback function (the WNDPROC), a function with a fixed signature, and taking two arguments that are for all intents and purposes void*'s. The message ('WM_xxx') is a way to tell you how to interpret its arguments, and inform you about changes in the life cycle or state of the window. Everything else (like being able to 'send' messages to HWND's and make them behave in a certain way) comes from there, and much of it is convention - which often works, but when it doesn't, it's not the design's 'fault'.
This is a very good example of the kind of thing I see in all sorts of frameworks, where someone (often me) is trying to do something that isn’t 100% plain vanilla, and TheOneTrueRightWayToDoThis isn’t obvious. Our hapless programmer thinks of something, tries it, it appears to work, and they move along. Meanwhile, it only appears to work “by coïncidence," and in reality there is a hidden bug waiting to bite.
The ideal situation is to do more research and really understand how the leaky abstraction actually works, but there is never enough time in the day and deadlines in the calendar are closer than they appear...
Haha, this happens to me a lot when learning new libraries or frameworks. My current solution is to just search StackOverflow with what I'm trying to do. Often, the Right Way is posted as a snippet, but even if there are no directly pertinent answers, related ones can also give insight on other things I'm doing wrong.
33 comments
[ 3.8 ms ] story [ 91.4 ms ] threadThe odd thing about this article is it tries to pretend it's the user's fault. Which is doubly odd as Raymond Chen's stuff is usually pretty good.
1. Signal system confuses notifications with commands
2. API ties windows to threads
3. System allows you to send WM_DESTROY from one thread to another even though it provides undefined behavior
4. DestroyWindow() can only be called from a particular thread (related to item #2)
5. The API doesn't detect that the window has been destroyed as far as the application is concerned (whatever happens in ForgetWorkerThread() or PostQuitMessage(), the window actually goes away) without a DestroyWindow() call.
In short, WTF.
[edit: format]
What UI APIs are there that don't have a concept of UI thread(s)? Coming from Windows, I'm used to UI work needing to be done on a UI thread.
I randomly googled these comments:
"Never, ever, touch the UI from outside your UI thread, in any version of Qt."
"Android UI toolkit is not thread-safe and must always be manipulated on the UI thread."
google "iOS 'non-UI thread'": 60 million results.
javascript: everything UI-related runs on one thread.
Do you do much UI coding?
And yeah, i would stop signing your posts - it comes across as conceited somehow; like each comment is a big deal.
What does that even mean? Of course you can have single-threaded UI applications in Windows, the majority of them are. Have you ever written a Windows application in C or C++?
OK, so what's the alternative? Automatic locking at OS-level? How can the performance penalty in those many cases where it is not needed be justified? Same goes for your arguments 3, 4 and 5 btw. Why should such low-level functionality impose important design decisions on those who follow the documentation, just to protect the beginners who don't? I much prefer my raw control, thanks - and I'll happily take the few times I shoot myself in the foot with it, err, with it.
Despite its warts and its legacy cruft, the win32 is quite flexible, and easy to understand - at a very basic level, not much studying of under-the-hood stuff required. For an OS-level API, it packs a lot of power into a small set of concepts.
You don't need to sign your posts as your user name appears above your post.
Specifically
> Please don't sign comments, especially with your url. They're already signed with your username. If other users want to learn more about you, they can click on it to see your profile.
You'll find the community here is great, but you should try to play by it's rules:)
To be perfectly fair, this arrangement (which I don't like either) is actually inherited from the Windows 3 API (Win16) and very likely goes back all the way to Windows 1.
I guess the conclusion is that one cannot blindly take WM_xxx message and think 'oh I can use ::SendMessage() and friends to make windows do certain things'. Only the documented behavior are valid use cases.
I guess one could say that the WM_xxx message should've been split up into more subclasses, but where to draw the line? I'm quite fine with using WM_xxx for all the fundamental window manager messages, as long as one minds their documented behavior there is no problem. (I realize that this last sentence can spark a whole discussion by itself on how far self-documenting a 'sane' API should be, but that's rather subjective imo).
WM_PUBLIC_CREATE and WM_PROTECTED_DESTROY
Then there would be no confusion about who was supposed to send the messages and how they were meant to be interpreted. Of course they don't have to use this exact terminology, but you get the idea.
The failing of the Windows message system is not distinguishing between the 'command' messages that you are allowed to send to a window to effect some action (WM_NOTIFY and friends) and the 'event' messages caused by the window manager (WM_DESTROY, input events etc.). It should be apparent that getting a window to respond to an event is not the same thing as effecting that event.
Crucially, OOP languages without access modifiers (or ones you can #define away, like C++) have this same problem. If an ignorant programmer thinks he can destroy a window by calling its _onDestroy() function, you have solved nothing.
With sufficient language support you can probably save users from themselves in this case, but there are many other cases where a user can do the wrong thing by not understanding the system. At some point the effort doesn't justify itself.
Yeah well there's the error in your thinking, and exactly what the OP was about: window messages aren't that. Every message is a callback, a 'notification' if you will. If you call RegisterClass() and pass a WNDPROC that does nothing (ignores all messages), it can still be created/destroyed with CreateWindow()/DestroyWindow(), and the 'notifications' it gets (WM_CLOSE and WM_DESTROY) can happily be ignored ('happily' as far as the window manager is concerned).
Win32 is not an OO API, and thinking of it too much in those terms will cause all sorts of confusion. Win32 is a C api, and yes things like MFC put an OO wrapper around it, but at some point the abstraction will start to leak if you take it to the extreme without thinking about the underlying system. Win32 is based around the OS calling back into your code through a well-defined callback function (the WNDPROC), a function with a fixed signature, and taking two arguments that are for all intents and purposes void*'s. The message ('WM_xxx') is a way to tell you how to interpret its arguments, and inform you about changes in the life cycle or state of the window. Everything else (like being able to 'send' messages to HWND's and make them behave in a certain way) comes from there, and much of it is convention - which often works, but when it doesn't, it's not the design's 'fault'.
The ideal situation is to do more research and really understand how the leaky abstraction actually works, but there is never enough time in the day and deadlines in the calendar are closer than they appear...
Is this a common pattern? Why?
Wouldn't it be simpler to just create a thread or separate process without an attached window?