# Prework: native Win32 tray utility (C++, zero dependencies)

This is setup guidance for building a **new** app in this environment, not
documentation of an existing one. Read it before writing any code.

The target shape: a single-executable Windows tray utility with no runtime to
install, no third-party libraries, 0% CPU at idle, and a signed-or-not installer
that a non-technical user can run.

---

## 1. Confirm the toolchain before writing anything

Do not scaffold until all four of these answer:

```bat
where cl                                REM MSVC compiler
where rc                                REM Windows SDK resource compiler
where ISCC                              REM Inno Setup 6 command-line compiler
powershell -Command "$PSVersionTable.PSVersion"
```

`cl` and `rc` are only on PATH inside a Visual Studio developer shell. The build
script must call `vcvars64.bat` itself rather than assuming the environment:

```bat
set "VCVARS=C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvars64.bat"
if not exist "%VCVARS%" ( echo [ERROR] Install VS Build Tools 2022 with the C++ workload. & exit /b 1 )
call "%VCVARS%" >nul
```

If VS Build Tools is missing, the install is *Build Tools for Visual Studio 2022*
with the **Desktop development with C++** workload, not the full IDE.

Inno Setup 6 is a separate download and does not come with VS.

## 2. Decide these before the first line of code

- **What the left-click does.** A tray utility has exactly one obvious primary
  action. Pick it, and make the icon click do that. Everything else is
  right-click menu.
- **Where settings live.** Prefer one INI under `%APPDATA%\<App>\config.ini`
  over the registry, it is inspectable, portable, and trivially deletable.
  Decide now whether uninstall removes it (usually: no, and say so).
- **Whether a global hotkey is needed.** If yes, it must be user-rebindable and
  the app must still run when registration fails, because something else on the
  machine will eventually claim the combination.
- **Single instance or not.** Almost always single. Named mutex, checked before
  the window is created.

## 3. Project skeleton

```
app.cpp             all application code, one file is fine and stays fast to read
resource.rc / .h    icon, version info, manifest reference
app.manifest        DPI awareness, long paths, common controls v6
build.bat           vcvars -> rc -> cl -> link
installer.iss       Inno Setup script
assets/app.ico      icon
tools/              icon generator, test harness
```

Resist adding a solution/vcxproj. A batch file that calls `cl` directly is
faster, has no hidden state, and is far easier for an agent to reason about.

## 4. Compiler flags that matter here

```
cl /nologo /W4 /O1 /GS- /MT /GL /EHsc /DUNICODE /D_UNICODE ^
   /Fo:build\ /Fe:build\App.exe app.cpp build\resource.res ^
   /link /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /LTCG ^
   user32.lib shell32.lib ole32.lib oleaut32.lib advapi32.lib shlwapi.lib
```

- `/MT`: static CRT. This is the flag that makes the exe standalone. Without
  it, users need a VC++ redistributable, and bug reports follow.
- `/O1` over `/O2`: optimise for size; a tray app is not compute-bound.
- `/GL` + `/LTCG`: whole-program optimisation.
- `/OPT:REF /OPT:ICF`: strip unused code and fold identical functions.
- `/SUBSYSTEM:WINDOWS`: no console window.
- `/DUNICODE /D_UNICODE`: always. Use `wchar_t` and the `W` APIs throughout;
  mixing ANSI in later is a rewrite.

Add `/W4` from the start and keep it clean. Fixing warnings later is worse.

## 5. app.manifest is not optional

Without it, expect blurry UI on high-DPI displays and 260-character path
failures. Minimum:

- `dpiAwareness` = `PerMonitorV2`
- `longPathAware` = `true`
- dependency on Microsoft.Windows.Common-Controls version 6

Reference it from `resource.rc` so it is embedded in the exe rather than
shipped as a loose file.

## 6. Architecture: event-driven, not polled

The idle cost of a tray app is its entire reputation. That means:

- A plain `GetMessage` loop. No timers unless something genuinely needs them,
  and no background thread that wakes to check state.
- `Shell_NotifyIcon` for the icon; handle `TaskbarCreated` (registered window
  message) so the icon comes back when Explorer restarts.
- Prefer the modern COM interface for whatever is being automated, with the
  legacy `SystemParametersInfo`-style call as a fallback. Query the interface,
  and if it fails, degrade rather than error.

Target: 0% CPU at rest, low single-digit MB private working set. Measure it in
Task Manager before calling the app done.

## 7. Hardening checklist, write these cases down as tests first

A utility that touches the filesystem will meet all of these in the wild:

- Configured folder is empty, missing, or on a disconnected drive
- A file is deleted between enumeration and use
- A file is corrupt or not actually the format its extension claims. Skip it,
  do not abort the cycle
- A second copy of the app is launched
- Explorer restarts
- The hotkey is already registered by another process
- The exe is moved after autostart was configured. Detect and repoint

`tools/` should hold a script that drives these end to end.

## 8. Installer

Inno Setup 6, `installer.iss`, built with `ISCC installer.iss`. Decide:

- 64-bit Program Files (`ArchitecturesInstallIn64BitMode=x64compatible`)
- Start Menu entry always; desktop shortcut optional via a task
- Generated uninstaller registered in Settings ▸ Apps
- `AppId` must be a stable GUID. Changing it later orphans existing installs

Unsigned installers trigger SmartScreen. Either budget for a code-signing
certificate or state the "More info → Run anyway" path plainly on the download
page. Do not pretend the warning will not appear.

## 9. Hosting the download

Cloudflare Pages, static. For a small binary, commit it to the served
directory and link it directly. Once it exceeds ~25 MB, move it to R2 and
stream it through a Pages Function instead, see the Corvus prework for that
pattern.

Keep `/` as a human landing page and put the binary at a stable path that never
changes across releases, so existing links keep working.

---

## Conventions for the agent working in this repo

- One source file until it genuinely hurts. Do not pre-split.
- No dependencies. If a library seems necessary, first check whether the Win32
  API already does it, it usually does.
- Every write to user data is preceded by a validity check on what is there.
- Comments explain *why* a Win32 call is ordered the way it is, never *what*
  it does.
- Build with `build.bat` and read the actual warnings before reporting success.
