TheForger's Win32 API Programming Tutorial
winprog.org
winprog.org
So, I came from university, had written Software during my master thesis with Qt, had some exposure to Gtk on my home PC a few years before, and I faced this obscure Win32 API, which looks ugly, appaling, antiquated and plainly awful.
But: Development was actually quite pleasant, and, while it didn't offer the flexibility of full-blown object oriented libraries like Gtk or Qt, it was amazingly good at getting stuff done. I wouldn't start a new project in it of course and it seems Microsoft has pretty much abandoned the API. But, I have to admit that I can see how it served Microsoft and its developer community well for a few years. In fact, a lot of things in there where quite thoughtful and I can still appreciate the non-bloaty way of doing things, if I see how many processes and memory an electron app uses that doesn't do much more than what a simple Win32 program does.
As you point out, the code you had to deal with was actually written using the Win16 API.
That Win16 API dates back to the first ever version of Windows which takes you back to a pre 1990 era.
As someone who did work on both Win16 and Win32, what I found amazing is Microsoft designed Win32 such that you could take your Win16 code, do a minimal number of changes and recompile that code for Win32.
That meant you could take your old Win16 code designed for a now obsolete version of Windows, do a minimal number of tweaks, recompile the code using a 32 bit compiler and have it running on the most resent 32 bit versions of Windows.
It was my first experience of a major coding upgrade and it was the last such upgrade that was anywhere near as simple as that upgrade.
Or 64-bit, for that matter.
Neither "WoW64" nor "WoW" are usually referred to as "hypervisors". That term is usually reserved for systems which run a full operating system using CPU virtualisation technology – Hyper-V, VMWare, VirtualBox, Xen, KVM, etc.
By contrast, WoW64 and WoW are rather thin translation layers. WoW64 just intercepts the 32-bit API calls, translates the parameters, invokes the 64-bit API, and then translates the results back to 32-bit. (WoW does basically the same thing for 16-bit calls to 32-bit.) This is possible because all of the Win32 API calls have Win64 equivalents (likewise, most Win16 API calls have Win32 API equivalents, except for a handful of obscure Win16 APIs which newer versions of Windows don't support.)
WoW runs under NTVDM which uses virtual 8086 mode, so you might call WoW a "hypervisor" in that sense (although the hypervisor is really NTVDM not WoW.) But WoW64 does not use any virtualisation technology at all.
I think xplorer² is written using WTL.
No they havent. Everything else in Windows land is just built on top of it.
I also made some Win32 development at one point. The programs made using only Win32 have a very snappy and light weight feel to them that is very nice. It is fairly low level and more for the machine than for humans. But I doubt you can have both of these things.
There were even some articles recently about that:
https://news.ycombinator.com/item?id=19883351
https://news.ycombinator.com/item?id=19873198
I started with a bit of Win16 and then moved to Win32, and don't really see why it gets a lot of hate; it's not perfect, but it looks like it does exactly what it was meant to do, and does it well. Perhaps it's because all of the tiny and fast applications I've used in the past were pure Win32. You can do a lot with it in only a few KB or tens of KB. In contrast, I don't find all the other multi-megabyte UI frameworks appealing.
I have a hard time imagining why anyone who isn't forced to use WinUI in their Win32 application (forced in the sense that some middle manager heard about it and believes it is a good idea so forces the developers to use it) would use it in the first place. I mean, many applications/frameworks do not even use stuff available through COM since Vista and instead prefer to recreate them using the plain C APIs and that is for a seemingly simpler and more understood variety of COM.
From a birds' eye view: Because it's just not productive to write apps without some type of modularity, and the WndProc abstraction encourages enormous monster functions that do everything. There's a reason why all UI frameworks have some kind of modular paradigm, whether it's object-oriented or functional reactive or what have you.
More specifically: A lot of the core APIs are just bad. RegisterWindowEx has a struct with a dozen random fields, and CreateWindowEx has a ton of confusing random parameters. For example, why do you have to specify a menu when you're creating a button? Why is dwExStyle the first parameter and dwStyle is the fourth? Why do you have to specify a cursor explicitly instead of just having a function that sets it if you want a non-default one? It's possible to make a low-level C API that's actually reasonably pleasant to use, as in GTK+. Win32 is just a mess.
The issues you mentioned do exist but they are just minor nitpicks - at the end of the day it doesn't matter at all if the dwExStyle parameter is at the first, fourth or at the last (my guess was that is first to allow for a quick textual search and replace in code to "upgrade" from "CreateWindow(..." to "CreateWindowEx(0, ..."). This is the sort of stuff that will never cause any sort of issue in practice.
Also there are no confusing random parameters in CreateWindow if you actually read the documentation. The menu parameter for a button isn't used for a menu but for an identifier that allows you to distinguish that button from other controls in the window.
Finally your winproc's switch can actually call dedicated functions. You can even write a couple of macros that do that for you so you do something like
static LRESULT HandlePaint(HWND hWnd, WPARAM wParam, LPARAM lParam);
static LRESULT HandleSize(HWND hWnd, WPARAM wParam, LPARAM lParam);
BEGIN_WINPROC(MyWinProc)
ON_WINPROC_MESSAGE(WM_PAINT, HandlePaint)
ON_WINPROC_MESSAGE(WM_SIZE, HandleSize)
END_WINPROC()
which is pretty much the same as what you'd do in other toolkits with setting up event handles such as gtk_signal_connect(GTK_OBJECT(widget), "expose_event", (GtkSignalFunc)handle_expose, NULL);
gtk_signal_connect(GTK_OBJECT(widget), "configure_event", (GtkSignalFunc)handle_configure, NULL);
(or your favorite toolkit's equivalent)What I dislike about Qt is that it's a C++ only library with no proper way to use it in C. And Gtk imposes a convoluted object oriented C library you should learn and the usual tried & tested RAD point & click to get forms done is way more complicated than it should be, vis a vis the MSVC form builder, the way .NET does it or hell, even VB6. In hindsight, Windows API/GDI was great.
GUIs tend to fit perfectly with object oriented systems, so it makes sense for a GUI library for a non-OOP language to need to "invent" one. I think one failing of the Windows API was that they made this only for visual windows instead of creating a more generic "HOBJ" object system with message passing that the GUI bits would be implemented on and other system and user APIs would also be able to use.
Though on the other hand Windows event-driven message passing was already a culture shock to most programmers at the time and this could make things even harder :-P
Win32 only provides the basic building blocks for the UI and Microsoft decided that this is enough (i disagree, i'd like them to add new APIs to Win32 for stuff like layout management, resizable dialogs, dockable panels, etc) and anything else should be provided by other libraries, so you just see people using these other libraries.
It is the equivalent to Linux syscalls, since it is the lowest stable layer in Windows land.
There are other win32 APIs that are quite interesting:
* the networking API (native proactor servers, something that Linux doesn't have yet AFAIK)
* the security and process API, which I had the pleasure to explore recently, in order to contribute to an open source server that launches a single user server for a connected user, under the user's profile. The CreateProcessAsUser function and its interactions with user authorization tokens is quite something.
Windows Internals is pretty good, but it's not a programming book per se. It's trying to document the design of the OS, help with troubleshooting when you don't have the source to the OS, and help developers understand the high-level concepts.
Not saying it's better or worse, of course. Just interesting.
I'd really love to play around with something as hyper-integrated as z/OS or something. So if anyone's got an old ES/9000 or something kicking around that they wouldn't mind sending me, let me know.
I/O completion ports? Those are great and I wish Linux had a similar API. Epoll doesn't fully solve the asynchronous I/O problem since files always have data available to read. The kernel should be able to do reads and writes in the background while my program does something else.
He uses C++ to wrap every resource acquisition in a constructor and every release of a resource in the corresponding destructor, so that by allocating new objects on the stack you avoid a whole swath of memory management errors. This was my first encounter with RAII (not sure if he calls it that), and it seems a particularly elegant way to write C++ and to make the raw Win32 C API a lot less scary.
And yet development was still painful. Sometimes I would want to add a feature and I'd be amazed that in 30 minutes it was done, but far more often there would be something that didn't work and I'd literally spend two weeks of free time changing the order of state setting calls, or setting state that the books didn't say needed to be set but I had seen other source code do it, reading other program source, searching online, asking in forums...
The last release was in 2005. I'd like to work on it more, but there is no way I'm ever going back to that codebase.
[0] https://www.wired.com/2005/11/chat-room-that-built-the-world...
http://www.flounder.com/controls.htm
To me at least, if you change the terms in that article, it looks a lot like the arguments in favor of moving away from ad hoc jQuery spaghetti and to React's single global rendering pass.
[1]https://web.archive.org/web/20090606220454/http://www.dilasc...
I note this version uses LPSTR rather than LPTSTR; perhaps it predates the UNICODE fiasco and the brief window where UCS-2 seemed like a good idea.
Like if I wrote a UWP/WinUI/etc program and disassembled it, wouldn't it just call Win32 functions in some system dll to do things like create windows, receive messages etc?
Learning Windows programming gave me so much insight to programs crashing and all the graphical bugs, beause I had caused them all in my own porograms before.
MFC is also quite bad because of huge abstraction leakages; it fills like Win32 with bolted on classes.
It seems like Win32 happened a bit by accident, a bit by overengineering and a bit by Microsoft's attempt to lock everyone to their 'special' development environments, which did not include C as a conscious choice by Microsoft.
Maybe I just haven't had my coffee yet, but I'm not understanding what this is supposed to mean.
It's also one of the first. Win32's origins extend back over 35 years and start on machines with a tiny fraction of the capability of even the smallest of today's modern PC's. It was also built with a different set of tradeoffs and in a radically different culture of software development. (Most modern developers hadn't been born at the time of it's initial design, and most of the people who were actively writing this stuff in the early-to-mid 1980's, I'm sure have moved on. 35 years is pretty much a career.)
> MFC is also quite bad because of huge abstraction leakages; it fills like Win32 with bolted on classes.
That's pretty much what it was... by design. There was a more object-oriented library that Microsoft developed, prior to MFC. They tested it on developers that had essentially just come up to speed on the (then-new) Win16 API, who all complained about having to ascend another learning curve for a fancy object oriented language and framework. Microsoft's reaction was to drop the older framework (referred to as AFX) and develop MFC as a thin wrapper around Win16. (This is part of why there are AFX prefixes scattered throughout the MFC codebase.... it's a legacy of the earlier framework.)
Even then, early versions of MFC had some utility, mainly around making things more type safe. Because it bound C++ instances to Window system instances, it made it easy to attach user data structures to UI components. (Otherwise, you were worrying about window or class words, which were Microsoft's original approach to that problem and used by MFC under the hood.) MFC also handled parsing of window message arguments. Win16 also sent messages with just a word and a long, when what you really might need is a set of mouse button flags and an x/y position or something. MFC helped with that too, by 'cracking' the message arguments into something closer to what you'd expect.
(To this day, Win32 documentation still includes instructions on parsing WPARAMS, etc.
https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-m... )
So the question is, back then was there any better GUI programming layers?
And the answer is yes, there was actually an API that was better and it was called OS/2.
At that time Microsoft was in a partnership with IBM to develop OS/2.
However, when the partnership fell apart, Microsoft just took big chunks of that OS/2 API and created a sub par version and called it the Windows API.
In a more Microsoft-centric world, I'm pretty sure the OS/2 API would've been exactly as you describe it and almost strictly in parallel with Windows. In fact, Microsoft did exactly this with Win32s (and Windows 95) In their relationship with Windows NT (which was once known as OS/2 NT or OS/2 3.0).
The same compiled binary could run on both the 'entry level' OS and the 'server level' OS, with a bit of care taken in the way the API was used.
That's a fairly broad description, isn't it? At least I have a hard time coming up with windowing API models that don't fundamentally work that way at the lower levels. (About the only exception think of is a text-based library that shipped with Microsoft BASIC 7.0, and that was mainly because the language didn't support any of the abstractions needed for callbacks.)
However OS/2 had many other layers to it's programming SDK that were not found in early Windows.
For example there was the Dos layer for things like processes, threads, semaphores, files, directories etc.
And then there was the Gdi layer for graphics.
Most of that functionality only made it's way into Windows with the advent of Windows NT (Windows 3.x/95 had poorer versions of those functions. For example running a process and capturing it's output was a real pain in Windows 3.x and Windows 95).
And based on the fact so many of those OS/2 Dos function have an equivalent Windows NT version I suspect Microsoft used that OS/2 design as their starting point for NT.
As just one of many examples consider DosCreateThread from OS/2 1.x
http://www.edm2.com/index.php/DosCreateThread_(OS/2_1.x)
It looks a lot like the CreateThread function found in Win32:
https://docs.microsoft.com/en-us/windows/win32/api/processth...
Microsoft learned from the mistakes IBM forced on them and did a better job. Right off the top of my head:
* IBM forced 80286 compatibility in an 80386 world.
* IBM forced incompatibility between Windows and OS/2 for the sake of preserving their mainframe graphics API's.
* OS/2 had a single messaging queue for the whole desktop.
* OS/2 was tied to the underlying hardware to the extent of being written partly in assembler.
OS/2 was great... I loved OS/2 2.0 in particular (and I worked for IBM testing OS/2 LAN Server for a summer)... but IBM had no idea how to address the market and no sense of how fast it was evolving.
MFC is horrible, I agree. I've had to work with it and the indirection just gets in the way and creates bloat. That is overengineering.
I have used wxWidgets a lot and really like it, but for modern C++ on Windows what do Microsoft recommend?
I can't seem to get any idea. Honestly, I would like to know.
I do not know what MS would recommend these days. From my point of view they basically functionally abandoned it but dragged it along because most of their stuff was built with it. The .NET libs was where all the new stuff was going (WPF, WinUI). WTL may be worth a look it looks like it comes out of the ATL/MFC lineage.
Especially when you take into account that the first GUI application MS made was Excel for the Macintosh. And then they more-or-less wrote Windows as a runtime environment for porting Excel to PCs.
Not getting into any of that old-school MS-stole-the-GUI flamewar business, I just think it's genuinely historically interesting to note the parallels when you play with each.
Maybe I haven't done enough WinAPI programming, but where is that true? I know there are a few helper functions that automatically dispatch an event to a callback, but I thought in general all events had to be intercepted and dispatched in the message loop
I've been doing Win32 (also called "Petzold-style") since 1995, and am pleased to say I'm converting to Qt for multi-platform capability.
When I look at the dreck necessary to create a dialog box in Win32, and compare to more modern frameworks, it's like milking a cow vs. buying milk at the store. Sure, you're "into the fundamentals", but is that really how you want to spend your time?
Because it's the native API on the most common desktop OS?
For example if you work on a game engine or toolset then you must know Win32 - especially if you work on the tools, even if you are using a wrapper like wxWidgets or Qt.
The only time where you can avoid Win32 is if you're using something that completely abstracts away the entire system, like Java Swing or a web browser.
Personally I work in wxWidgets but occasionally it's useful to dig into wxWidgets's internals which are (for the Windows version) written in Win32.