> It feels like there was kind of a sweet spot before GPUs took over everything where there's software like demos and games that likely don't require a lot of Windows API and are possibly old enough to be possible to emulate on one of these fast new Macs.
I'm reminded of FamiTracker, one of these traditional Win32 (MFC eew) apps which has managed to survive into the modern day with a technological stack largely unchanged, outside of newer compilers and me replacing DirectSound with WASAPI in Dn-FT. But even in simple apps there's a surprising amount of complexity, ranging from threading deadlocks from poor architecture, to a Wine bug causing the window to fail to redraw when resized (https://bugs.winehq.org/show_bug.cgi?id=52903, related https://bugs.winehq.org/show_bug.cgi?id=52515).
I’d imagine there might still be a surprising number of MFC apps still in circulation.
PuTTY, for example, uses MFC (I’d completely forgotten MFC was a thing until I had to write a custom port of PuTTY for a previous company ~5 years ago).
The issue for a lot of devs is if you want to use any of the new stuff you can't ship a single file portable executable. You have to do a packaged installer. So MFC warts and all can still be faster and lighter than other options unfortunately. I was hoping WinUI3 would have a way to do single file delivery but it doesn't as far as I can tell.
For GUI things, I prefer a nice installer that will place the files in a sensible location (Program Files) and that will give me a shortcut in the Start Menu, I don't need a single file - and I suppose most users are likely to misplace the single executable or keep it in the Downloads folder forever.
Also, what counts as a single file? You can ship a (modern) C# app as a single-file executable. It will be hundreds of megabytes, but to the user, it's just a single file.
A nice installer is ideal, but more work to setup I think (I've never figured out how to write an installer so far). And I want the ability to download many versions and put them side by side for eg. running old versions, or bisecting bugs.
In my case the app in question is about ~6MB and an occasionally used utility, I could make a setup version that dynamically loads things... but why? Most users would prefer to just use it, delete it, and if they need it again grab it again.
Oh I've had so many nightmares trying to extract the underlying files from Inno Setup installers. 7-Zip doesn't work, innoextract and such is fiddly to find and IIRC doesn't work 100% of the time (forgot).
In fact, I just upgraded an MFC music application[0] to VS2022 and 64-bit this year, marking the first release in 15 years.
I'm not a Windows developer, and had to learn it while working on this upgrade. The MFC documentation has clearly atrophied, but has far as I can tell, there's also not blessed upgrade path for C++ applications.
There is C++/WinRT + WinUI 3, but looking at the screenshot that will pretty much require re-designing the whole UI so it's not going to be a simple upgrade.
It's a technical application, so I'm not entirely thrilled by doing that. I just looked for documentation to replace a popup or preference window with WinUI 3, but didn't see anything that made sense for me.
A project like this is vital for software preservation. The closest thing to it besides what the author listed in the article is Apple's Rosetta, but add that to the list of similar but not quite exact comparisons noted by the author and frankly do any of us really believe Apple will keep Rosetta around forever? We saw how that went last time.
We really need open source, community-maintained versions of things like Rosetta so that old apps written for different architectures or even different operating systems can continue to run on modern operating systems. I applaud the author. We need more people like that interested in this stuff.
> There are other projects to run old Windows programs. WoW64 is the name of the system within 64-bit Windows that makes old 32-bit Windows programs run. Wine shims the Windows API onto your host system — see the great How Wine works for a deep dive on what that means. And system emulator projects like qemu emulate a full x86 machine such that you can install Windows onto them. But Wow64 requires running 64-bit Windows, Wine requires x86 hardware, and qemu requires installing the full Windows OS into the emulator to run a Windows program.
I use BoxedWine (http://www.boxedwine.org/) on my desktop environment in the browser (https://github.com/DustinBrett/daedalOS) to run exe's directly. Works quite good for things like SkiFree or even Notepad++. Happy to see projects like Retrowin32 to move this tech forward, or at least keep it alive.
> I think I started thinking about this idea when I read the remark "win32 is the stable Linux userland ABI". That is, Linux churns so much that a given program written today won't work in a year; meanwhile, programs written in the Windows 95 32-bit era are guaranteed to never change again.
I think the point is that the Linux kernel ABI is super stable, but the Linux userland ABI (meaning, the system libraries your program uses) has breaking changes.
Traditionally, Linux binaries aren't distributed with their libraries; it's expected that the distro will place the libraries in the right version and in the correct place. Unfortunately distros rarely keep around old libraries indefinitely. This means that distributed binaries will eventually fail to run. (get any .deb from an old Debian and try to run on a new Debian, it likely won't work. Now, get any installer from Windows and try to run.. on Wine, it will probably work)
So, if you want stability, you need to use one of those package formats that bundle the application and all its libraries (like Snap, AppImage and Flatpak), that looks awfully like Windows installers :t except that Windows application don't need to bundle system libraries like the libc, because Windows extends their stability guarantees to dlls like user32.dll - that is, Windows has a _stable userland ABI_. Contrast this with Linux, where glibc is known to break from time to time.
So.. suppose you have a binary to run but doesn't have all of its libraries - the binary expects that system libraries are to be found at their well known places. Would you prefer this binary to be a Linux binary, or a Windows binary? Since Linux can run Windows programs just fine (or sometimes, better than Windows) with Wine, win32 might as well be the stable userland that Linux is missing
> Windows has a _stable userland ABI_. Contrast this with Linux, where glibc is known to break from time to time.
Linux also has a stable userland ABI, it is just that unlike Windows said stable ABI does way less. You can make very advanced programs with the stable APIs Windows provide, since they feature not only simple console stuff, I/O, etc but also user interfaces with widgets[0], 3D graphics, audio, video[1], etc.
Also glibc is stable if a program relies on public APIs, the breakage comes from programs relying on stuff they weren't meant to use. The biggest annoyance (not only with glibc but most libraries) is that you have to build your program on a system with the oldest libraries you plan on supporting (this is not a requirement on Windows) but with VMs, etc this should not be much of a problem in practice.
[0] X11 on Linux is also technically stable since the protocol is compatible even with machines from the early 90s and the libraries have a stable ABI, but by itself it doesn't provide much.
[1] Codecs notwithstanding, though Microsoft's own codecs should be fine to rely on
> Windows application don't need to bundle system libraries like the libc
That might be a bad example, because Windows applications are expected to bundle their own libc (MSVCRT). I'm not sure if modern Windows even ships with a copy -- it definitely doesn't ship with copies of all the versions that applications might depend on.
Linux follows the Windows model, where the stable interface is the kernel (Linux's syscalls, Windows's ntdll). This in turn implies that libc is a non-system runtime dependency, and a Linux application that wants to be portable across more than one release of Ubuntu (or whatever) should bundle its own copy.
Complaints of Linux ABI stability is mostly a story of old-timers trying to imitate how Unix worked in the '80s and '90s, with libc baked into the OS. Younger developers are more likely to use static linking or Docker/Flatpak/Snap.
> I'm not sure if modern Windows even ships with a copy -- it definitely doesn't ship with copies of all the versions that applications might depend on.
>That might be a bad example, because Windows applications are expected to bundle their own libc (MSVCRT). I'm not sure if modern Windows even ships with a copy -- it definitely doesn't ship with copies of all the versions that applications might depend on.
It went the other way - Microsoft moved away from the "unstable C runtime" policy and introduced the Universal CRT (IIRC in 2015), officially being promoted to a Windows component, a guaranteed stable ABI, and shipped with Windows since W10.
>Linux follows the Windows model, where the stable interface is the kernel (Linux's syscalls, Windows's ntdll).
Ntdll is decidedly not guaranteed to be stable, and its direct use is explicitly discouraged by Microsoft. Win32 has always been the official and stable interface for NT-based OSes.
I've only tried this with one .deb, but I've been running a 7ish year old version of a program I don't like the new version of, so far without problems. Originally a jessie package, now on ubuntu 22.04
> Contrast this with Linux, where glibc is known to break from time to time.
glibc has a very stable ABI, i.e. older binaries compiled against older versions of it should work fine on current versions (since the breaking libc6 transition which was several decades ago), they employ symbol versioning to ensure old binaries still find the symbols with the behavior they expect.
Of course, there are other "system libraries" that change ABI/SONAMEs more often, so what you are saying still generally applies, but I'd say glibc is not one of them.
glibc have versioned symbols. A glibc library can claim itself to be compatible of multi (old) version glibc. But it seems except some long term support versions. It didn't actually claim to be compatible with every old versions? The one shipped with Debian 11 have symbol of 2.2.5 and then there's a big gap and jump outright to the range of 2.3.x
IMHO it means both Linux and Windows are obsolete. The MMURTL Operating System specifically opted for no DLL's and static linking of all applications. The operating system ABI is guaranteed to be stable.
This makes upgrading the OS a heck of a lot easier too.
It's absolutely true. It's not true of the kernel. But if you depend on any .so files provided by your distro, then you can reasonably assume that once the version of your distro goes EOL you're going to running your app in a docker container later (unless you continually maintain all the breakages). That's why I always make static binaries. Since if you only link the kernel, then you're good. WIN32 is also pretty stable. Always has been compared to the Linux userspace. You have to go back 20 years (like the OP) before unmaintained WIN32 apps start falling apart.
> > programs written in the Windows 95 32-bit era are guaranteed to never change again
> Yeah, well, programs written for Linux 20 years ago also never change again. That's the whole point of closed-source binary-only programs...
This sentence is poorly phrased, but from the context it looks like they meant: programs written in the Windows 95 32-bit era are guaranteed to continue running without ever changing again
> programs written in the Windows 95 32-bit era are guaranteed to continue running without ever changing again
This needs the caveat of how well written those programs were: even with all the compatibility features modern Windows have, some programs did abuse the underlying APIs and had memory issues.
Also some programs at the time used "thunking" to mix 16bit and 32bit code (via DLLs) or simply used some DOS-based programs for some tasks even though the "main" program was 32bit, so those wont work with 64bit Windows. AFAIK Borland C++ 5 is a case of an application doing both, even though the main IDE is 32bit (and works in modern Windows), some of the helper stuff relies on DOS and Win16 code that doesn't work.
I have run into something similar with really old MinGW compiled applications. Old MinGW used to start allocating the heap at an non-recommended, but technically working address. After Windows 7, however, MSFT enforced memory safety rules a bit more firmly, making this address non-valid and breaking the program. Visual Studio provides a tool to rebase the heap allocation address, so that old 15+ year old program works again in Win10. I chose another address, admittedly one that might fall into the same category as before, but all of us at work hope to be off this old program by the time that happens again. Or we'll just virtualize it again.
> I don't think that's true. It's more like "programs written 20 years ago (against glibc 1.x) don't work on recent Linux anymore"
Technically Linux-the-kernel can run programms written 20 (and more) years just fine, it is the libraries that are the problem. However FWIW some libraries, like OpenGL, xlib and glibc do provide backwards compatibility - assuming no blatant abuse is done.
I made this test[0] in 2018 by compiling some binaries with a toolkit of mine in some ancient RedHat Linux from 1997 and they were working perfectly fine in 2018 Debian since it didn't rely on libraries that change their APIs/ABIs every time there is a new fashion. I haven't repeated it recently but i'm pretty sure a similar test with binaries from 1997 that only rely on libraries with stable ABIs will still work on my current openSUSE Tumbleweed installation, showing that more than two decades of backwards compatibility could be possible.
Presuming good faith, one could argue that copying over a dynamically linked binary from an older system will likely not "just work" on a newer install.
Yes I can make it work as well, but that could likely have been the experience described
> It's kind of amazing to look at the archaeology of how Windows works because it's the accumulation of cruft over literally decades. For example every exe file first begins with a DOS program that prints "This program cannot be run in DOS mode" followed by more headers that are then parsed by Windows.
That was not the case with defrag or regedit (or was it another register editor tool...). I remember using it without windows to backup and restore the register. This allowed me use windows as I wished and if it simply became slower because of the register, I could simply restore it.
This is exciting for me personally. I have a lot of code over the years for my gamedev hobby written for win32 that isn't easy to port elsewhere. Making them available on the web would be a fun way to keep them alive.
It seems we get all kinds of Windows emulation, but was there ever a marginally worked out attempt of "just" providing the necessary libraries for a basic Win32 application, open source or commercial? So not aiming at a binary-level compatibility, but source-level.
Backwards compatibility (i.e. practicality) has been one of Microsoft’s strategic advantages. Just like compound interest, it accretes value in a powerful way.
I'm reminded of FamiTracker, one of these traditional Win32 (MFC eew) apps which has managed to survive into the modern day with a technological stack largely unchanged, outside of newer compilers and me replacing DirectSound with WASAPI in Dn-FT. But even in simple apps there's a surprising amount of complexity, ranging from threading deadlocks from poor architecture, to a Wine bug causing the window to fail to redraw when resized (https://bugs.winehq.org/show_bug.cgi?id=52903, related https://bugs.winehq.org/show_bug.cgi?id=52515).