Writing Win32 apps like it's 2020: Introduction
Why should anyone write a new application using '90s technology like Win32 API in 2020?
This is the first part of a three-part series on Win32 development:
Let’s start with the obvious question right away: Why should anyone write a new application using ’90s technology like Win32 API in 2020?
Well, if you are working at a company like ENLYZE, there are plenty of good reasons to do so:
- You need to send your customer a small and self-contained application which should just work without any installation
- You don’t know if your customer’s computer is brand new or 20 years old, but you can be sure that it’s running Windows (welcome to the world of industrial manufacturing)
- You want to create a familiar and functional user interface by using the operating system’s UI components instead of reinventing the wheel
- You only want to do the job once, and therefore need a framework that is not going to be abandoned tomorrow
If that also applies to your situation, welcome aboard!
Even if not, this post may show you some interesting Windows frameworks to look at. However, if your goal is to write a multiplatform application, you are probably looking for something like Qt or even Electron (if you don’t mind the umpteenth Chrome installation on your computer).
Through the jungle of Windows frameworks
The Win32 API is probably the longest-living user interface framework still in use, with its initial release through Windows NT 3.1 in 1993 and its origins dating back to Windows 1.0 for 16-bit processors of 1985.
It even predates the first C++ standard by a few years. As such, don’t expect any fancy OOP classes or templates. All you get is a purely procedural C interface, whose manual resource management and lack of encapsulation can make your life unnecessarily difficult in 2020.
However, it has two major advantages no other Windows framework can offer: Being the native user interface API, it comes preinstalled on every Windows version without requiring you to ship additional dependencies. Furthermore, Microsoft has been keeping the API stable for more than 25 years, even when transitioning to 64-bit processors. Win32 has thereby become a solid foundation not just for applications, but even for various user interface frameworks.
After releasing Win32, Microsoft introduced several other frameworks to ease Windows development, all with their own caveats:
Microsoft Foundation Classes
The Microsoft Foundation Classes (MFC) certainly make up the most prominent C++ framework around the Win32 API.
They encapsulate almost all parts of the procedural Win32 API into object-oriented C++ classes. They go even further and provide you with things not found in Win32, such as window splitters or helpers for a document/view architecture. As you can imagine, all these features combined make MFC quite a heavyweight framework. Your total binary size will increase significantly if you decide to use MFC. Furthermore, MFC is as old as the Win32 API itself and therefore carrying a lot of legacy on top.
If you want to develop a new Windows application using modern C++, there are usually better alternatives to MFC.
Active Template Library
The Active Template Library (ATL) proves that Win32 APIs can also be encapsulated into C++ classes in a lighter way.
It is a mostly header-only library providing smart pointers and RAII wrappers around a delicate part of Win32, namely COM. ATL is mature code with a stable interface, and as it comes bundled with Visual Studio, you don’t have the burden of managing the dependency yourself. Consequently, you are advised to use it whenever your application deals with COM.
There is no point in doing COM without ATL in 2020.
Windows Template Library
The Windows Template Library (WTL) builds on top of ATL to provide similar template classes around the Win32 user interface APIs, not just COM.
It can be considered a lightweight alternative to MFC.
However, it is a project run by a single Microsoft employee and severely lacking documentation, which is why I haven’t used it for my applications yet. Your mileage may vary though.
Windows Implementation Library
A young member to the 3-letter abbreviated frameworks is the Windows Implementation Library (WIL), another C++ header-only framework.
It provides all sorts of wrappers for automatic resource management and simplified usage of Win32 APIs to make them ready for the modern C++ age. Sounds exactly what we are looking for, right? Well, not necessarily.
WIL was originally called the Windows Internal Library and, as the name suggests, used exclusively inside Microsoft for Windows development. As such, it generally targets the latest version of Windows and doesn’t try to be a solution with backward compatibility in mind. This is no option for the applications I am developing, which may still find themselves running on an ancient Windows XP machine. After all, backward and forward compatibility is also one of the major advantages of the Win32 API that shouldn’t be sacrificed easily. No matter if that is a constraint for you or not, WIL is definitely worth a look though. Even if you’re unable to use it directly, you may be able to take over some of its concepts in your application :)
.NET Framework and .NET Core
Finally, let’s not forget about the .NET Framework and its Windows Forms library for user interface design.
This usually doesn’t fit into an article about C++ development due to its entirely different paradigm: Code for the .NET Framework is likely written in C#, memory is garbage-collected, and the entire source is compiled to an intermediate language instead of native machine instructions. However, along with its intuitive UI designer, .NET is often the tool of choice for Windows developers to quickly create graphical applications. Does that mean we can just drop C++ and go with C# and .NET for all our tasks? Hell no!
While compiled .NET executables are lightweight enough to fulfill that constraint, they always depend on the heavyweight .NET Framework.
And here comes the culprit:
Different Windows versions ship with different .NET Framework versions preinstalled, and the compatibility matrix isn’t that easy.
You want to target Windows 7? Then your application needs to be written for .NET Framework 3.5, which is the only version that comes with Windows 7. But keep in mind that your app won’t run under Windows 8 or 10 out of the box, which only ship with .NET Framework 4.x. Yes, you read that right, .NET Framework 4.x won’t run applications written for .NET Framework 3.5.
You are also screwed if you jumped on the train too early: .NET Framework 1.x and all applications written for it are no longer supported starting with Windows 7.
And if you still need to target out-of-the-box Windows XP installations, .NET Framework is no solution for you at all, because it doesn’t come preinstalled on XP.
Of course, the situation is different if you develop around a .NET Framework version that works on all your target platforms and always ship its installation package along with your application. But then your software can no longer be considered lightweight or self-contained. It may even require an installation with administrative rights on the target computer, which is the last thing we want here.
The experts among you may come and tell me:
Hey, there is a new kid in town.
It’s called .NET Core and .NET Native and supports compiling C# code to native executables without any external framework dependencies!
That’s right and I’m pretty sure it has a bright future. However, it only targets Windows 10, which makes it no option if you need to support older Windows versions.
Since the release of Windows 8 in 2012, Microsoft has been trying to move people away from Win32 and came up with a variety of new frameworks:
WinRT, UWP, WinJS, ReactXP, and more recently WinUI.
So far, none of this has proven to be a long-time safe bet. WinJS confirms these fears, having already been abandoned after just 4 years. And yet another time, all of these frameworks are no options if your application needs to run under Windows 7 or earlier.
As you see, nothing beats the backward and forward compatibility as well as the size of an application developed using only Win32 API. Some lightweight C++ frameworks exist though, which wrap nicely around Win32 APIs and help us in situations where a pure C interface would be unnecessarily difficult to use.
Win32 is an integral part of the Windows operating system, which is not going to disappear tomorrow. Due to the large reliance of components like .NET Framework on Win32, it is also unlikely that it ever will. And as long as Windows system applications like Explorer and huge codebases like Microsoft Office are based around Win32, it is still a safe bet for writing new applications.
In the next posts, I am going to write a small DPI-aware wizard-style application using modern C++ features and Win32 API. Making your application look properly on today’s high-resolution screens is one of the most neglected aspects in Win32 development. I will also present some useful C++ wrappers along the way.