# How to Debug PowerToys Modules Effectively with Visual Studio: A Complete Guide

> Effectively debug PowerToys modules using the Visual Studio debugger. Attach to PowerToys.exe and step through source code with full symbol support for efficient troubleshooting.

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

---

**PowerToys modules are loaded as dynamic-link libraries (DLLs) into the central Runner process, allowing you to debug any utility by attaching the Visual Studio debugger to `PowerToys.exe` and stepping through source code with full symbol support.**

PowerToys is a collection of utilities for power users that runs as modular components within a unified host process. Debugging PowerToys modules effectively with the Visual Studio debugger requires understanding the dynamic loading architecture shared between the Runner and individual utilities. This guide walks through the exact workflow used by the **microsoft/PowerToys** repository maintainers to trace execution, inspect state, and diagnose crashes across both native C++ and managed C# codebases.

## Understanding the PowerToys Debugging Architecture

### The Runner and Module Loader

The **PowerToys Runner** (`PowerToys.exe`) serves as the host executable that loads each utility via the module loader subsystem. Located in `src/runner/`, the Runner calls `LoadLibrary` and `GetProcAddress` to dynamically load module DLLs that implement the `ITool` interface. Each utility—whether **FancyZones**, **PowerRename**, or **PowerToys Run**—resides in `src/modules/<module_name>/` and exports standard lifecycle methods including `Load`, `Enable`, and `Disable`. Because modules execute within the same process space as the Runner, a single Visual Studio debugging session can step across module boundaries without switching attach contexts.

### Debug Symbols and Configuration

When building the **Debug** configuration, the solution generates `.pdb` symbol files for both the Runner and every module, typically output to `x64/Debug/` or `x86/Debug/`. These symbols enable source-level debugging inside [`src/runner/ModuleLoader.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/ModuleLoader.cpp) and individual module implementations. The **Settings UI** runs as a separate WinUI process in `src/settings-ui/` that communicates with the Runner over named pipes, though most module logic debugging occurs within the main Runner process.

## Step-by-Step Guide to Debugging PowerToys Modules

### 1. Configure the Solution for Debug Mode

Open `PowerToys.sln` at the repository root and select the **Debug** configuration to ensure `.pdb` files are generated for all projects. According to the build guidelines in [`tools/build/BUILD-GUIDELINES.md`](https://github.com/microsoft/PowerToys/blob/main/tools/build/BUILD-GUIDELINES.md), this configuration preserves optimization settings compatible with breakpoints while producing the symbol files required for stack unwinding and variable inspection.

### 2. Launch the Debugger and Enable Your Target Module

Press **F5** or select **Debug → Start Debugging** to build the solution and launch `PowerToys.exe` under the debugger. Once running, enable your target module through the PowerToys Settings UI (or via hotkey if already configured). This action forces the Runner to load the module's DLL into the process space, making its code available to the debugger.

### 3. Setting Breakpoints in Module Source Files

Navigate to the module's source code—for example, [`src/modules/fancyzones/FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZones.cpp)—and set breakpoints on the relevant functions. Breakpoints appear hollow until the DLL loads; once you trigger the module's activation, Visual Studio resolves the symbols and the breakpoint icon fills, indicating active binding. For instance, placing a breakpoint inside `FancyZones::OnHotKeyPressed()` will pause execution when the hotkey combination is pressed.

```cpp
void FancyZones::OnHotKeyPressed()
{
    // VS will stop here when the FancyZones module is active
    // and the hot‑key is triggered.
    LOG_DEBUG(L"FancyZones hot‑key pressed"); // Uses PowerToysLogger
    // …rest of implementation…
}

```

### 4. Inspecting State Across Module Boundaries

When execution pauses, use **Debug → Windows → Locals**, **Watch**, and **Immediate** to inspect variables. Step through code using **F10** (Step Over) and **F11** (Step Into) to follow logic across the boundary between [`src/runner/PowerToys.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/PowerToys.cpp) and the module's implementation files. The call stack window shows the full execution path from the Runner's message loop through the module loader and into the specific utility code.

## Advanced Visual Studio Debugging Techniques for PowerToys

### Attaching to a Running Process

If you need to debug a module loaded after initial launch (such as after changing a Group Policy setting), use **Debug → Attach to Process…**, select `PowerToys.exe` from the list, and then enable the target module. This method is essential for diagnosing issues that only reproduce after specific runtime configuration changes without restarting the entire debugging session.

### Mixed-Mode Debugging for C++ and C#

PowerToys combines native C++ modules with managed C# components in the Settings UI. To debug both simultaneously, configure the debugger type in project properties under **Debugging → Debugger Type** to enable both **Native Code** and **Managed (CoreCLR)** engines. This allows seamless stepping across the interop boundary when investigating issues in the settings view models located in [`src/settings-ui/PowerToys.Settings.UI/Settings/SettingsPageViewModel.cs`](https://github.com/microsoft/PowerToys/blob/main/src/settings-ui/PowerToys.Settings.UI/Settings/SettingsPageViewModel.cs).

### Troubleshooting Symbol Loading Issues

If breakpoints remain hollow (unbound), open the **Modules** window (**Debug → Windows → Modules**) and verify the correct `.pdb` file is loaded for the DLL in question. You can manually load symbols via **Load Symbols** if the automatic search path fails, pointing directly to `x64/Debug/` or your custom output directory. Refer to [`doc/devdocs/tools/debugging-tools.md`](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/tools/debugging-tools.md) for specific symbol path configurations used by the PowerToys team.

### Using PowerToysLogger for Diagnostic Logging

Complement breakpoint debugging with the `PowerToysLogger` framework described in [`doc/devdocs/logging.md`](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/logging.md). Logs write to `%LOCALAPPDATA%\Microsoft\PowerToys\PowerToys_<date>.log` and provide historical context for race conditions or timing-sensitive bugs that are difficult to catch interactively.

```csharp
// Example: Enabling a module programmatically for quick debugging
// (Only for local development – not part of the shipped product)
using Microsoft.PowerToys.Settings.UI.Library;
var settings = SettingsRepository.GetInstance();
settings.SetEnabled(ModuleEnum.FancyZones, true);

```

## Summary

- **PowerToys modules** are DLLs loaded by the Runner process via `LoadLibrary`, enabling single-process debugging of all utilities.
- Use the **Debug** configuration to generate `.pdb` symbols, then launch with **F5** to attach to `PowerToys.exe`.
- Enable the target module through Settings UI to trigger DLL loading, then set breakpoints in files like [`src/modules/fancyzones/FancyZones.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/fancyzones/FancyZones.cpp).
- Configure **mixed-mode debugging** to step through both native C++ Runner code and managed C# Settings UI components.

- Consult [`doc/devdocs/tools/debugging-tools.md`](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/tools/debugging-tools.md) for official guidance on symbol paths and remote debugging scenarios.

## Frequently Asked Questions

### How do I debug a PowerToys module that crashes during startup?

Attach the Visual Studio debugger to `PowerToys.exe` before enabling the problematic module, then enable it through the Settings UI. Set breakpoints early in the module's `Load` or `Enable` methods—typically found in the module's main `.cpp` file under `src/modules/<module_name>/`—to catch initialization errors before they propagate.

### Can I debug both C++ and C# code in the same PowerToys debugging session?

Yes. Configure the startup project properties to use the **Mixed** debugger type (supporting both Native and Managed CoreCLR) in **Debugging → Debugger Type**. This allows you to step from the native Runner code in [`src/runner/ModuleLoader.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/ModuleLoader.cpp) directly into managed Settings UI code in `src/settings-ui/` without restarting Visual Studio.

### Where are the debug symbols located for PowerToys modules?

Debug symbols (`.pdb` files) are generated in `x64/Debug/` or `x86/Debug/` (depending on your platform target) when building the **Debug** configuration. If Visual Studio cannot locate symbols automatically, manually specify the path through **Debug → Windows → Modules → Load Symbols**, or copy the `.pdb` files adjacent to their corresponding DLLs.

### Why won't my breakpoints hit in a PowerToys module?

Breakpoints remain inactive if the module DLL has not yet been loaded into the Runner process. Ensure you have triggered the module activation (via Settings UI or hotkey) so that [`src/runner/ModuleLoader.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/runner/ModuleLoader.cpp) executes `LoadLibrary`. Additionally, verify that the timestamp of the source file matches the compiled binary and that the correct `.pdb` symbol file is loaded in the Modules window.