Beginner tutorial · about 15 minutes

Build your first native Windows application.

Start with an empty folder and finish with a real, resizable HWND window. Every command and file you need is on this page.

You needWindows 10 1809+ or Windows 11
TimeAbout 15 minutes plus the first download
ExperienceBasic C++ syntax; no Win32 or CMake experience required
ResultA native x64 Debug application
Step 0

Know what the tools do

You do not need to create a .sln file, configure include directories, or download WIL manually.

Step 1

Install Visual Studio and Git

  1. Install Visual Studio 2026 Community or Visual Studio 2022 Community.
  2. In Visual Studio Installer, select Desktop development with C++.
  3. Under Individual components, confirm these items:
    • MSVC x64/x86 build tools
    • C++ CMake tools for Windows
    • Windows 11 SDK or a recent Windows 10 SDK
  4. Install Git for Windows if git is not already available.
  5. From the Start menu, open Developer PowerShell for VS 2026. For VS 2022, open its corresponding Developer PowerShell.
git --version
cmake --version
cl

Success means all three commands are found. cl prints a compiler version and then reports that no source files were supplied; that final message is expected.

Step 2

Create an empty project folder

New-Item -ItemType Directory -Path "$HOME\source\mwfl-hello"
Set-Location "$HOME\source\mwfl-hello"
New-Item -ItemType File CMakeLists.txt, main.cpp, app.manifest

Open this folder in your editor. Its final shape is:

mwfl-hello/
├── CMakeLists.txt
├── main.cpp
└── app.manifest

1. CMakeLists.txt

This downloads a tested release, creates a Windows GUI executable, enables C++20, and links mwfl.

cmake_minimum_required(VERSION 3.21)
project(mwfl_hello LANGUAGES CXX)

include(FetchContent)
FetchContent_Declare(mwfl
  GIT_REPOSITORY https://github.com/mwfl/mwfl.git
  GIT_TAG v0.1.0
  GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(mwfl)

add_executable(mwfl_hello WIN32 main.cpp app.manifest)
set_property(SOURCE app.manifest PROPERTY VS_TOOL_OVERRIDE "Manifest")
target_compile_features(mwfl_hello PRIVATE cxx_std_20)
target_compile_options(mwfl_hello PRIVATE /W4 /permissive- /EHsc /utf-8)
target_link_libraries(mwfl_hello PRIVATE mwfl::app)

2. main.cpp

#include <mwfl/mwfl.h>

using mwfl::operator""_dip;

class MainWindow final : public mwfl::WindowBase {
public:
    void BuildUI() override {
        SetTitle(L"My first mwfl application");

        mwfl::ControlHost ui{*this};
        ui.Add(message_, L"Hello from a native HWND window.");
        ui.Add(close_, L"Close");

        SetLayout(mwfl::Column()
            .Margin(24_dip)
            .Gap(12_dip)
            .Add(message_, mwfl::Stretch())
            .Add(close_, mwfl::Fixed(38_dip)));
    }

    mwfl::EventResult OnCommand(
        const mwfl::CommandEvent& event) override {
        if (event.IsClicked(close_)) {
            Close();
            return mwfl::EventResult::Handled();
        }
        return mwfl::EventResult::Propagate();
    }

private:
    mwfl::Label message_;
    mwfl::Button close_;
};

int WINAPI wWinMain(
    HINSTANCE instance, HINSTANCE, PWSTR, int show) {
    return mwfl::RunApplication<MainWindow>(instance, show);
}

3. app.manifest

The manifest enables current Common Controls and Per-Monitor-V2 DPI behavior.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity name="mwfl.hello" processorArchitecture="*"
    version="1.0.0.0" type="win32" />
  <dependency><dependentAssembly>
    <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
      version="6.0.0.0" processorArchitecture="*"
      publicKeyToken="6595b64144ccf1df" language="*" />
  </dependentAssembly></dependency>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
    </windowsSettings>
  </application>
</assembly>
Step 3

Configure the project

Run one of these commands from the folder containing the three files.

cmake -S . -B build -G "Visual Studio 18 2026" -A x64
cmake -S . -B build -G "Visual Studio 17 2022" -A x64

The first configure needs internet access and may take a minute while Git downloads mwfl and WIL. Success ends with:

-- Configuring done
-- Generating done
-- Build files have been written to: .../mwfl-hello/build
Do not mix Visual Studio generators in one build folder. If you switch between VS 2022 and VS 2026, use a different folder such as build-vs2022.
Step 4

Build and run

cmake --build build --config Debug --parallel 2
& .\build\Debug\mwfl_hello.exe

A resizable native Windows window should open with a label and a Close button. Resize it to see the retained layout update the real child controls.

If the build succeeds but the executable path differs, locate it with:

Get-ChildItem build -Recurse -Filter mwfl_hello.exe

Run in Visual Studio

  1. Choose File → Open → Folder and select mwfl-hello.
  2. Wait for CMake generation to finish.
  3. Select mwfl_hello.exe as the startup item.
  4. Press F5 to debug or Ctrl+F5 to run without the debugger.
Step 5

Make and verify a change

Change the title or label in main.cpp, save the file, then rebuild:

cmake --build build --config Debug --parallel 2
& .\build\Debug\mwfl_hello.exe

You normally configure once and rebuild after source edits. Run the configure command again only after changing CMake options or dependency settings.

When something fails

Troubleshooting

“Could not find any instance of Visual Studio”

Install the Desktop development with C++ workload, use the generator matching your installed Visual Studio version, and reopen Developer PowerShell.

Git clone or FetchContent fails

Confirm git --version works and that GitHub is reachable. Corporate proxies may require Git proxy configuration. Retry the configure command after connectivity is restored.

Generator does not match the previous generator

You reused a build directory with another Visual Studio version. Create a new directory, for example build-vs2022 or build-vs2026.

Linker cannot find wWinMain

Keep WIN32 in add_executable and use the exact wWinMain signature shown above.

The window is blurry or controls look old

Ensure app.manifest is listed in the executable sources and retains the Common Controls and DPI declarations.

x86 configuration is rejected

mwfl intentionally supports only x64 and ARM64. Use -A x64 for a normal PC.

Start over safely

Close Visual Studio, remove only the project’s generated build directory, and repeat Configure and Build. Do not delete your three source files.

Continue learning

Next steps