The FAT32 formatter that ignores the 32 GB limit.
A native Windows WPF (.NET 10) tool that formats drives as FAT32 — including drives larger than 32 GB, which Windows' built-in formatter refuses to do.
Looking for instructions on how to use the app rather than build it? See USER_GUIDE.txt.
The 32 GB FAT32 cap in format.com / Disk Management is an artificial
restriction Microsoft added — the FAT32 format itself supports volumes up
to 2 TB (with 512-byte sectors). This is handy for SD cards, USB drives, or
external SSDs that need to stay FAT32 for compatibility with cameras, game
consoles, car stereos, smart TVs, etc. — devices that often can't read
exFAT or NTFS.
This tool calculates the BPB (BIOS Parameter Block) fields, FAT size, and cluster size using the same formulas Windows itself uses internally, then writes the FAT32 structures directly to the volume, bypassing the size check.
- Lists removable and fixed drives, with size/label/filesystem shown
- Shows only removable (USB/SD) drives by default — fixed/internal drives are hidden until you explicitly check "Show internal/fixed drives too"
- Blocks formatting your Windows system drive
- Advisory warnings for unusual drives (a "removable" drive reporting multiple terabytes, a very small volume, or a fixed drive once you've opted into seeing them) — these don't block you, they just make you look twice
- Custom volume label and selectable allocation unit (cluster) size, or "Default (recommended)" to match Windows' own sizing logic
- Confirmation checkbox + a final "this will permanently erase..." dialog before anything is written
- Reads back the boot sector, FSInfo sector, and FAT entries after writing to confirm they actually landed correctly, rather than just assuming success
- Optional full-format mode that scans every cluster for bad sectors and marks any found in the FAT (quick format is the default either way)
- Remembers your last-used cluster size and "show fixed drives" choice between launches
- Live log and progress bar while formatting
- Dark, native UI (including a themed confirm/result dialog) that matches Windows' dark mode title bar
- A headless CLI mode for scripting/automation — see Command-line usage
- Native builds for both x64 and ARM64 (Windows-on-ARM) Windows devices
- This permanently erases all data on the target drive. There is no undo.
- It requires Administrator privileges (the app prompts for UAC elevation automatically) because it locks/dismounts the volume and writes raw sectors directly to disk.
- Quick format is the default — it lays down a clean, empty FAT32
filesystem without scanning for bad sectors. There's also a full
format option (a checkbox in the GUI,
--fullon the CLI) that scans every cluster for bad sectors and is much slower, proportional to the drive's size. - This software is provided as-is (see License) with no warranty. Test on a spare/cheap drive first before trusting it with anything that matters.
- Windows 10 (2004+) or Windows 11 — x64 or ARM64 (Windows-on-ARM)
- .NET 10 Desktop Runtime if running a framework-dependent build, or nothing extra if using a self-contained published build
You'll need Visual Studio 2022 (Community is fine) with the ".NET desktop development" workload, or just the .NET 10 SDK for the command line.
cd Phat32
dotnet build -c ReleaseOr open Phat32.sln in Visual Studio and press F5. The app will
prompt for UAC elevation each time it launches (required for raw disk access).
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=trueThis bundles the .NET runtime into a single portable .exe that runs on a
bare Windows install with nothing pre-installed. The project is configured
with IncludeNativeLibrariesForSelfExtract so WPF's native rendering
libraries (e.g. PresentationNative_cor3.dll, wpfgfx_cor3.dll) get bundled
into the exe and self-extracted to a temp folder at startup — without that
setting, single-file WPF publishes can launch and exit instantly with no
window and no visible error, since the native renderer can't be found.
For Windows-on-ARM devices, swap the RID:
dotnet publish -c Release -r win-arm64 --self-contained true -p:PublishSingleFile=trueThis cross-publishes fine from an x64 machine (no ARM64 hardware needed to build it) — but that also means the ARM64 build has only ever been cross-compiled, never actually run on real ARM64 hardware. Treat it as unverified until it's been tested on an actual Windows-on-ARM device.
Or just run package.ps1 from the repo root — it does the above publish
step and also bundles the resulting exe with USER_GUIDE.txt and LICENSE
into a ready-to-share zip under dist/ (the same contents the GitHub
Actions release workflow produces, but without needing to push a tag):
.\package.ps1 # builds and zips BOTH win-x64 and win-arm64
.\package.ps1 -Arch x64 # just win-x64
.\package.ps1 -Arch arm64 # just win-arm64
.\package.ps1 -Version 1.2.0 # name the zip(s) explicitly
.\package.ps1 -SkipBuild # re-zip without rebuildingIf Windows blocks the script from running (...cannot be loaded because running scripts is disabled...), either run it via
powershell -ExecutionPolicy Bypass -File package.ps1, or allow local
scripts for your account once with
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned.
If
dotnet publishfails withNU1100errors aboutMicrosoft.NETCore.App.Runtime.win-x64or similar, that's a NuGet restore problem (usually a corporate proxy blocking nuget.org), not a project issue — see Troubleshooting.
The build is unsigned by default. On a machine that's never run it before, expect a SmartScreen "Windows protected your PC" prompt, and possibly a flag from behavior-based EDR/AV (the disk-locking + raw-sector-write pattern overlaps with how wiper tools behave, regardless of signing). If you're distributing this beyond your own machine, signing with a code-signing certificate or Microsoft Trusted Signing is worth doing:
signtool sign /fd SHA256 /a Phat32.exedotnet publish fails with NU1100 errors — NuGet can't download the
runtime packs needed for a self-contained build. Check your sources:
dotnet nuget list sourceMake sure nuget.org is listed and enabled; on a corporate network it may
need to be added or proxied through an internal feed. As a quick workaround,
drop --self-contained true -r win-x64 and just run dotnet build — the
app will run fine with the .NET 10 Desktop Runtime
installed instead.
PHAT32 can also run headlessly for scripting, RMM deployment, or imaging workflows instead of using the GUI:
Phat32.exe --drive E --yes
Phat32.exe --drive F --yes --label BACKUP --cluster 4096
Phat32.exe --drive G --yes --allow-fixed
Phat32.exe --help| Flag | Meaning |
|---|---|
--drive <Letter> |
Required. Drive letter to format, e.g. E or E: |
--yes |
Required. Confirms permanent erasure — there's no interactive prompt in CLI mode |
--label <Label> |
Volume label, up to 11 characters |
--cluster <bytes|default> |
Allocation unit size: 4096, 8192, 16384, 32768, 65536, or default |
--allow-fixed |
Required to target a non-removable (fixed/internal) drive |
--full |
Full format: scan every cluster for bad sectors instead of a quick format |
Exit codes: 0 success, 1 bad arguments, 2 system drive refused, 3
fixed drive refused (use --allow-fixed), 4 drive not found, 5 format
failed, 99 unexpected error.
Note: the app's manifest always requires Administrator, so even CLI invocations trigger UAC unless the calling shell is already elevated. If you run it from an already-elevated PowerShell/cmd, output prints inline in that same window. If not, Windows elevates a new process that can't share the original console, so PHAT32 opens its own console window for output instead — the exit code is still the most reliable thing to check from a calling script either way.
Publishing a GitHub Release (tag + release through the GitHub UI, or
gh release create) triggers .github/workflows/release.yml, which builds
a self-contained single-file exe for both win-x64 and win-arm64 (as a
matrix build), attempts to get each signed, and attaches two zips
(Phat32-<tag>-win-x64.zip and Phat32-<tag>-win-arm64.zip, each
containing the exe + USER_GUIDE.txt + LICENSE) to that release automatically.
Signing is via SignPath Foundation, which signs qualifying open-source projects for free. The publisher shown to users will be "SignPath Foundation," not an individual name — that's the tradeoff for free, properly-trusted signing instead of paying for your own certificate.
One-time setup, once your SignPath OSS application is approved:
- In the SignPath dashboard, create a project for this repo and a signing
policy under it (e.g.
release-signing). Note the project slug and signing policy slug. - In this repo's Settings → Secrets and variables → Actions:
- Add secret
SIGNPATH_API_TOKEN(generate this in SignPath; the associated user needs the Submitter role on your signing policy). - Add variable
SIGNPATH_ORGANIZATION_ID(shown in SignPath, top-right corner next to your organization name).
- Add secret
- In
.github/workflows/release.yml, update theproject-slugandsigning-policy-slugvalues under the "Submit signing request to SignPath" step to match what you created in step 1.
Until SIGNPATH_API_TOKEN is set, the workflow still runs and still
produces a release zip — it just ships Phat32-unsigned.exe instead of a
signed Phat32.exe, with a warning in the workflow log. Nothing breaks
either way; signing just turns on automatically once the secret exists.
It writes, per Microsoft's published FAT32 File System specification:
- Boot sector (sector 0) + backup boot sector (sector 6)
- FSInfo sector (sector 1)
- Two FAT copies, each sized via the standard FAT32 FAT-size formula
- An empty root directory (with a volume-label entry, if one is set)
It locks and dismounts the volume first (FSCTL_LOCK_VOLUME /
FSCTL_DISMOUNT_VOLUME), writes everything with direct sector-aligned
WriteFile calls, then unlocks it so Windows remounts the new filesystem.
.github/workflows/release.yml (repo root) handles the build/sign/release
pipeline described above, and package.ps1 (repo root) does the same
build-and-bundle step locally without needing a tag/release. Everything
else lives under the project folder:
Phat32/
Phat32.csproj .NET 10 WPF project, requireAdministrator manifest
app.manifest Forces UAC elevation prompt
icon.ico App icon (exe icon + window/taskbar icon)
App.xaml / App.xaml.cs App entry point; branches into CLI mode or the GUI
MainWindow.xaml / .cs The UI: drive picker, label, cluster size, confirm/format
Models/DriveItem.cs UI-facing drive info
Dialogs/
AppDialog.xaml / .cs Themed confirm/info/error dialog (replaces MessageBox)
Services/
NativeMethods.cs P/Invoke: CreateFile, DeviceIoControl, WriteFile, console interop, etc.
DriveEnumerator.cs Lists candidate drives, flags the system drive
Fat32Parameters.cs BPB field math (cluster size table, FAT size formula)
Fat32Formatter.cs Builds, writes, and verifies boot sector / FSInfo / FATs / root dir
DarkTitleBar.cs Shared dark-title-bar helper for both windows
AppSettingsStore.cs Persists last-used UI choices to %AppData%
CliRunner.cs Headless command-line mode
- Add an "Eject" / safe-remove step after formatting removable media.
- Add exFAT as an alternative for very large drives where 32 KB FAT32 clusters start to feel wasteful (exFAT has no such size cap at all).
Issues and pull requests are welcome.
MIT — see LICENSE.
