# Memory Management and Resource Cleanup in PowerToys Modules: A Complete Guide to RAII Best Practices

> Master memory management and resource cleanup in PowerToys modules with RAII best practices. Learn to leverage unique_ptr, shared_ptr, and WIL for automatic cleanup.

- Repository: [Microsoft/PowerToys](https://github.com/microsoft/PowerToys)
- Tags: best-practices
- Published: 2026-02-25

---

**PowerToys modules rely exclusively on RAII (Resource Acquisition Is Initialization) patterns using `std::unique_ptr`, `std::shared_ptr`, and WIL wrappers to ensure automatic cleanup of heap memory, Windows handles, GDI objects, and COM interfaces without manual `delete` or `CloseHandle` calls.**

The microsoft/PowerToys repository implements a strict zero-leak policy across its native C++ modules. Every resource—from heap allocations to OS handles—follows deterministic lifecycle management through modern C++ smart pointers and the Windows Implementation Library (WIL), eliminating manual cleanup code and ensuring exception safety.

## Core RAII Patterns for PowerToys Memory Management

### Heap Memory with Standard Smart Pointers

PowerToys uses `std::unique_ptr` for sole ownership and `std::shared_ptr` for shared ownership scenarios. The FancyZones module stores work-area configurations in a map of unique pointers to ensure automatic destruction when the configuration object is destroyed.

```cpp
// src/modules/fancyzones/FancyZonesLib/WorkAreaConfiguration.cpp
std::unordered_map<HMONITOR, std::unique_ptr<WorkArea>> m_workAreas;

void WorkAreaConfiguration::AddWorkArea(HMONITOR monitor, std::unique_ptr<WorkArea> workArea)
{
    m_workAreas.emplace(monitor, std::move(workArea));
}

```

When `WorkAreaConfiguration` goes out of scope, the `unordered_map` destructor automatically destroys all contained `unique_ptr` instances, which in turn delete the `WorkArea` objects.

### Windows Handle Management with WIL Wrappers

The Windows Implementation Library (WIL) provides `wil::unique_handle` and specialized variants for process, thread, event, and mutex handles. The runner's quick-access host uses these wrappers to manage process and event lifetimes.

```cpp
// src/runner/quick_access_host.cpp
wil::unique_handle quick_access_process;
wil::unique_handle quick_access_job;
wil::unique_handle show_event;
wil::unique_handle exit_event;

// Automatic cleanup on scope exit
wil::unique_handle token{ OpenProcessToken(GetCurrentProcess(),
                                          TOKEN_QUERY | TOKEN_DUPLICATE,
                                          &token_handle) };

```

These wrappers automatically invoke the correct Win32 cleanup function—such as `CloseHandle`—when the object is destroyed, preventing resource leaks even during early returns or exceptions.

### COM Object Lifecycle Control

PowerToys modules interact extensively with COM interfaces for shell integration and media handling. The `wil::com_ptr` template provides automatic reference counting through `AddRef` and `Release` calls.

```cpp
// src/modules/previewpane/SvgThumbnailProviderCpp/SvgThumbnailProvider.cpp
wil::com_ptr<IWICBitmapDecoder> decoder;
ThrowIfFailed(factory->CreateDecoderFromFilename(filePath.c_str(),
                                                 nullptr,
                                                 GENERIC_READ,
                                                 WICDecodeMetadataCacheOnLoad,
                                                 &decoder));

```

The `wil::com_ptr` destructor automatically decrements the reference count, ensuring COM objects are released exactly once when the last reference is destroyed.

### GDI Resource Cleanup

GDI objects including device contexts, bitmaps, regions, and icons are managed through WIL wrappers like `wil::unique_hdc`, `wil::unique_hbitmap`, and `wil::unique_hrgn`.

```cpp
// src/modules/fancyzones/FancyZonesLib/WindowUtils.cpp
wil::unique_hrgn hrgn{ CreateRectRgn(pos, 0, (pos + 1), 1) };

// src/modules/MouseUtils/MousePointerCrosshairs/dllmain.cpp
wil::unique_hwnd hwnd;  // Window handle wrapper

```

These wrappers ensure that `DeleteObject` or `DestroyWindow` is called automatically, preventing GDI handle leaks that could exhaust system resources.

## Module Lifecycle and Resource Cleanup

### Container-Based Ownership in FancyZones

The FancyZones module demonstrates complex object graphs managed through standard containers of smart pointers. The `WorkAreaConfiguration` class maintains a map of monitor handles to work-area configurations, where each work area owns its own zone layout data.

When the user changes display configurations or exits PowerToys, the `WorkAreaConfiguration` destructor automatically cleans up all associated work areas without explicit iteration or deletion code.

### Global State Management with Shared Pointers

Modules requiring global access to shared resources use `std::shared_ptr` with static initialization. The ZoomIt module manages its video recording session as a global shared pointer to allow multiple UI components to reference the same session while ensuring cleanup when the last reference is released.

```cpp
// src/modules/ZoomIt/ZoomIt/Zoomit.cpp
static std::shared_ptr<VideoRecordingSession> g_RecordingSession = nullptr;

// Start recording
if (!g_RecordingSession)
{
    auto session = VideoRecordingSession::Create(device, item, crop,
                                                frameRate, captureAudio,
                                                captureSystemAudio, micMonoMix,
                                                stream);
    g_RecordingSession = std::move(session);
}

```

The static `shared_ptr` is destroyed during module unload, which decrements the reference count and triggers the `VideoRecordingSession` destructor if no other references exist.

### DLL Unloading in the Module Loader

The module loader ([`src/tools/module_loader/src/ModuleLoader.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/tools/module_loader/src/ModuleLoader.cpp)) manages the lifecycle of PowerToys modules implemented as DLLs. Each loaded module is stored in a `std::unique_ptr<PowerToysModule>`.

When the runner shuts down, the module loader simply allows these unique pointers to go out of scope. This automatically invokes each module's `Dispose` method and unloads the DLL, eliminating the classic "forget to call `FreeLibrary`" bug.

## Exception Safety and Error Handling

PowerToys code rarely uses raw `try / catch` blocks for resource management. Instead, the project relies on WIL's `ResultException` for error propagation. Because all resources are wrapped in RAII containers, cleanup occurs automatically during stack unwinding when exceptions are thrown.

For example, in ZoomIt's screen-capture pipeline, a `wil::ResultException` can propagate up the call stack without leaking capture resources, as the `wil::unique_handle` and `wil::com_ptr` destructors run during exception handling.

## Summary

- **RAII is mandatory**: Every resource in PowerToys—heap memory, Windows handles, COM objects, and GDI resources—must be owned by a smart pointer or WIL wrapper.
- **Prefer `std::unique_ptr`**: Use for sole ownership within containers and member variables, as demonstrated in FancyZones work-area management.
- **Use `std::shared_ptr` for global state**: When multiple components need access to the same resource, such as ZoomIt's video recording session.
- **Adopt WIL wrappers**: Replace raw Windows handles with `wil::unique_handle`, `wil::com_ptr`, and GDI-specific wrappers to eliminate manual cleanup.
- **Leverage destructors for module unload**: The module loader stores DLLs in `std::unique_ptr`, ensuring automatic `Dispose` calls and library unloading without manual `FreeLibrary`.

## Frequently Asked Questions

### What is the primary memory management strategy used in PowerToys modules?

PowerToys modules follow the **RAII (Resource Acquisition Is Initialization)** pattern exclusively. All resources—including heap-allocated objects, Windows handles, COM interfaces, and GDI objects—are wrapped in smart pointers or WIL (Windows Implementation Library) containers. When these wrapper objects go out of scope, their destructors automatically release the underlying resources, eliminating the need for manual `delete`, `CloseHandle`, or `Release` calls.

### How does PowerToys handle shared resources between multiple UI components?

For resources requiring shared ownership across multiple components, PowerToys uses **`std::shared_ptr`**. A prominent example is the ZoomIt module's video recording session, stored as a static `std::shared_ptr<VideoRecordingSession>`. This allows both the main UI thread and capture pipeline to reference the same session object. When the module unloads, the static shared pointer is destroyed, decrementing the reference count and automatically cleaning up the session when the last reference is released.

### Why does PowerToys use WIL wrappers instead of standard C++ smart pointers for Windows handles?

While standard C++ smart pointers like `std::unique_ptr` can manage Windows handles with custom deleters, PowerToys prefers **WIL wrappers** (`wil::unique_handle`, `wil::com_ptr`, `wil::unique_hbitmap`, etc.) because they provide type-safe, purpose-built cleanup for specific Windows resource types. These wrappers automatically invoke the correct Win32 API cleanup function—such as `CloseHandle` for events, `DeleteObject` for GDI bitmaps, or `Release` for COM interfaces—without requiring developers to specify custom deleters or risk calling the wrong cleanup function.

### How does the PowerToys module loader ensure DLLs are properly unloaded without memory leaks?

The module loader ([`src/tools/module_loader/src/ModuleLoader.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/tools/module_loader/src/ModuleLoader.cpp)) stores each loaded PowerToys module in a **`std::unique_ptr<PowerToysModule>`**. When the runner application shuts down, these unique pointers simply go out of scope. This automatically triggers the destructor of each `PowerToysModule`, which calls the module's `Dispose` method to release internal resources and unloads the DLL. This RAII-based approach eliminates the classic "forget to call `FreeLibrary`" bug and ensures deterministic cleanup even if the shutdown sequence encounters errors.