1. 10

  2. 6

    I’ve been slowly documenting weird Windows API stuff, but I thought this one was a bit more interesting than usual. Specifically, this touches lower-level Win32 window concepts and the implications that result (which I didn’t cover in the article, that’s about the issue I had and the one weird trick I had to solve it). For example:

    • Monkey patching, like Ruby and Smalltalk, but in “low-level code”, enabled by the window system
    • GUI programming comes to similar solutions to distributed systems (message passing and things that encapsulate state around an event loop). As an example compare: a window’s property list to an Erlang process dictionary. It even has atoms!
    1. 4

      For what it’s worth, I never considered Windows subclassing similar to monkey patching: you’re patching one instance, not a class, avoiding the scope issues you have in Ruby or Smalltalk, and you can cleanly chain back up the tree across multiple subclasses. The result was something that, to me, felt a bit closer to ad hoc prototype-based programming, and the distinction mattered, because instantiating a new e.g. list view in a different part of the program wouldn’t give me the modified list view, whereas making a new number in a random Rails app would have things like #seconds.

      Unrelated, you can use SetWindowLongPtr and GWLP_WNDPROC and the like in 32-bit apps, too. It’s defined at compile time, similar to how TCHAR works. I personally would probably use the newer stuff even in an older code base, but can completely get matching the style of the existing program. I’m only mentioning in case you thought you had to use the older stuff in a 32-bit program.

      1. 1

        That’s cool!

        I’ve run into some strange Win32 stuff that I’ve since written wrappers around for Janet, but I mostly don’t interact with Win32 event loop at the moment.

      2. 3

        Oh yes, this technique is very useful. My gui library uses it to inject javascript-style event handlers. Here’s the code


        Which calls this function:


        Which actually forwards back to a virtual method in the D class for future customization. But if you scoll down a little bit there’s also this bit of magic:

        if(SimpleWindow.triggerEvents(hWnd, iMessage, wParam, lParam, pos[0], pos[1], (*te).parentWindow.win) || !lastDefaultPrevented)
        					return CallWindowProcW((*te).originalWindowProcedure, hWnd, iMessage, wParam, lParam);

        Which is a bit hacky, but the triggerEvents will send it through as my low-level event struct, which is then processed into a high-level Event class, which user code can process JS-style

        listBox.addEventListener("click", (e) {

        To override the normal window processing.

        It actually works surprisingly well and I do this to all windows; back in the first link where I do the SetWindowLong, you will notice EnumChildWindows(p.hwnd, &childHandler, cast(LPARAM) cast(void*) p);, that is, it does the override hook recursively on any automatically-created children too.

        A bit crazy but like I said, it really does work surprisingly well and wasn’t actually that hard to cram in. I actually mostly like the Windows API. People call me a lying troll for saying that, but it is pretty complete so it helps you get a lot of work done, and it offers these hooks so you can get around it if it happens to be in your way. And most the quirks actually do make sense once you get to know them.

        I legitimately think Microsoft have done a pretty solid job.

        1. 6

          A bit crazy but like I said, it really does work surprisingly well and wasn’t actually that hard to cram in. I actually mostly like the Windows API. People call me a lying troll for saying that, but it is pretty complete so it helps you get a lot of work done, and it offers these hooks so you can get around it if it happens to be in your way. And most the quirks actually do make sense once you get to know them.

          It’s funny to me I’ve been using the Linux desktop for what, 12 years? And I still don’t really know how to write a desktop application - it’s overwhelming and the toolkits seem mostly bad. The furthest I got was Gtk#. Meanwhile, I’ve doing Win32 C programming for 2-3 years (albeit some high-level experience w/ Windows Forms before that), I’d call myself intermediate at best with it, but I can make useful applications with it. It’s actually a well designed API (especially compared to its contemporaries), albeit one with little magic. Ironically, Windows still has the “you can choose several toolkits” problem, but for the most part, they’re just dipping mustards around Win32, and you can feel confident that if you pick one, even if it doesn’t get showered with love, it’ll do what you want for years. I can’t say the same with the X11 parts bin.

          I’ve got more Windows API related nuclear takes, if anyone’s curious…

          1. 3

            I used Qt for a while back around when version 4.1 was coming out and I didn’t hate it, but it wasn’t great on Windows and besides, using it from D (which I switched basically everything to in 2007sih) just wasn’t very practical. And I really hate GTK as a user. Its api isn’t too bad, with the C interface and the XML interface definitions you can use it from other languages pretty easily. But on Windows it is pretty bad and even on Linux like… I just hate it. That horrible file dialog really drives me nuts, among other things.

            I also got meh about sdl.dll so…. I actually decided to write all my own stuff just on top of xlib and the OS. All the code scattered in here https://github.com/adamdruppe/arsd among many other things. But the main low-level file is simpledisplay.d which is a Win32 implementation on Windows and generally a X simulation of it on Linux, though in some cases I adapted the Windows API to better fit X (like async callback based get clipboard).

            Then for the higher level controls like buttons and checkboxes, I started writing minigui.d which is kinda the same - Win32 controls there with kinda (but not exactly, it is custom so I embrace that in some ways) simulated versions on X all done myself. And yeah it has grown to another 12,000 lines lately…. there’s just soooo many little details to get right to make it feel decent.

            Having done all this myself gives me a perspective I don’t see much. There’s actually a lot I like about X too, and getting this deep has given me a decent view on its history (and X generally maintains compatibility pretty well too actually, but it does so little by itself in terms of high level controls that this doesn’t help applications much as most everyone uses a toolkit and THOSE break every other week). I can see the extensions and version history in some of the specs and understand where they were coming from as I develop my own thing. Always want to keep things “simple” and “mini” but eventually use cases make you do more and more and before you know it, you have 32,000 lines of code lol.

            But seriously, and comparing it against the Windows functions too, you can see a lot of the same cycles. The X drag and drop spec started out as a kind of simplification of the Windows one… but before long, turned into basically a clone, just with more async and distributed aspects to fit into X’s networked and mechanism-not-policy model (which there’s a lot I like about, like the drag and drop or clipboard protocols being lazy is really cool. “Copy” side doesn’t actually copy, it just advertises that it has something and can offer in a list of formats. Paste side then asks for it to actually be delivered as one of those formats, and the “copy” side can do it on-demand and stream the data over. And if the programs are written well on both sides, the streamed data is part of the event loop, meaning pasting huge videos or something doesn’t lock up the UI; you can do a progress bar and offer cancellations and such. That’s legitimately really nice. Though most things don’t do much with that underlying potential ugh.)

            Anyway, using X makes me appreciate Win32 a lot more because it is so much more complete. But I still actually kinda like X too, especially I love doing things myself :) (ps don’t ask me what i think about wayland unless you want an angry, profanity laden rant.)

            1. 2

              Always down for the nuclear takes.

              1. 6

                Windows is more meaningfully extensible than the X11 toybox; things more useful than silly applets like conky or dmenu. Sounds crazy, but it’s true.

                A while back, I wrote a shell extension (which seemed hard at the time, but after doing it, I have a much better grasp on it, and tbh if I had context before, it would have been easier). After I wrote it, I get it for free in both the file manager and any file dialogs in applications I open. And it’s based on common APIs that other things share.

                How would I accomplish that on Linux? I might be able to hack at and patch my desktop/toolkit (assuming I know what layer needs modified), but what about if I have an application with a different toolkit? Maybe the toolkit had the hooks for it (KDE sounds like it might have), but I suspect those have rotted by now. How do I keep my patch up to date, or build it on my normal distro like Debian?

                In the Windows world, this kind of extensiblity was anticipated and well supported. The problem was the lacking documentation to deal with that complexity and unambitious developers. And I didn’t even need the source to Windows to make it.

                1. 2

                  I’d both agree and disagree. I used a custom shell on Windows for a while and tweaked a lot of settings that was really cool, but like it still kept raising windows when I didn’t want it to. So it depends on what you want to change. But for the majority of things I think I agree with you.

                  So I also kinda have that feeling about scripting. I think Windows’ scripting is really underrated because there’s a LOT of COM automation you can do, including through the windows scripting host, that puts a lot of linux app’s scriptability to shame. But I hesitate to take a strong stance on this because I haven’t actually done a whole lot of Windows scripting, so there’s probably lots of filth I just haven’t seen due to inexperience.

            2. 2

              Heh, I feel like every C++ app I’ve touched that doesn’t use MFC or OWL basically hits on what you’re doing. The strings instead of methods is admittedly different, but the rest is familiar.

              And I’m with you: I honestly think Windows’ APIs are overall very well-designed. The issues are more the amazing commitment to back compact leaving cruft, the lack of foresight resulting in FooBarEx and the like for early calls, and the occasional just absurd API (early DirectX, the tracing APIs, etc.). Even COM is a great API.

              1. 3

                That amazing commitment is absolutely worth the cruft though. I first learned Win32 programming - actually around 2003ish - from an old Windows 95 help file I downloaded to a floppy disk. (I had a hand-me-down computers with no internet access, so I’d take disks to the school, download stuff, then bring them back home.) Obviously Windows 95 was a bit old by 2003…. but virtually all the stuff in that old file not only still applied as learning material, it all still actually worked practically! Indeed, most that stuff still works today.

                Which like the link says is actually really nice since sometimes the old, limited functions are actually exactly what you need and a lot simpler to use. And, of course, having old programs you can pull up and still actually use is excellent for so many reasons.

                1. 3

                  Just to be clear, you and I are agreeing vigorously. I was just saying, “I think Windows gets a bad rap due to these reasons.” (I do, however, sincerely believe that the tracing API in Windows is just absolutely bananas. That part’s a complete pile, and I’m looking forward to the eBPF and dtrace stuff coming in Windows 11 as a possible way out of the madness.)

                  1. 3

                    Unless you’re Bruce Dawson, of course. That guy makes makes ETW look easy.

                    1. 2

                      indeed. I’m just using the excuse to talk a little :P

                  2. 1

                    One of the major reasons I didn’t do the COM-based drop target is because pretty much every single piece of documentation assumes “yeah, just inherit COleDropTarget in your CView”, which would be great if I were using MFC. I think I understand how to do it from rawdog Win32 now, but it is a tricky when I don’t necessarily need the benefits it has.

                    1. 1

                      Yeah, it is kinda annoying when the docs assume MFC. I’ve written tons of low level libraries in D and have similar pains. Now D is different than C in that it actually has some built-in COM support; if you write a class that inherits from IDispatch, the language will help you out a little by wiring together the vtables for you. Then the language itself doesn’t do QueryInterface but you can metaprogram that if you want so it isn’t too hard to use. In C, of course, you’d have to do structs of function pointers you populate yourself. More tedious work but not fundamentally too different.

                      But a drop target isn’t too hard, here’s my COM class that forwards to an application-defined class:


                      then you call RegisterDragAndDrop with the hwnd. The Linux implementation was WAY more work (also in that file if you’re curious, just scroll down 100 or so lines)

                      The COM api and the html5 api are (not coincidentally given browser dnd history) very similar and it actually isn’t too hard to use. But not as easy as the drop files window message lol.

                      1. 2

                        It’s actually less the documentation and other people’s advice. I guess by the 2000s everyone was using C++ for Windows development!

                2. 1

                  I used this trick when working on accessibility (via the UI Automation API) in a GUI toolkit that uses GLFW. To implement UI Automation, a window needs to handle the WM_GETOBJECT message, but GLFW doesn’t let you add custom message handlers. So I subclassed the GLFW window.