# IODD TRIM Tool - Version History
## 0.4.7.3
- **Checkbox re-flow fixed for a language change while the advanced panel is hidden:** with no `settings.ini` the advanced panel starts hidden, and switching to a long-string language (e.g. German) re-flowed the checkbox row while the checkboxes had no window handles — `GetPreferredSize` returned wrong widths, so the row overlapped once the panel was shown. The row is now re-flowed (deferred, after handle creation) whenever the panel becomes visible, in both places that show it (the "advanced settings" checkbox and the tab switch back to the TRIM tab)
## 0.4.7.2
- **Advanced-settings checkbox re-flow now also applies at startup (tester report):** the 0.4.7.1 re-flow ran inside `FormCreate`, before the window is realized, where `GetPreferredSize` does not yet return the final caption widths — so the German row still overlapped at launch and only corrected itself after a manual language round-trip. The re-flow is now additionally queued (`Application.QueueAsyncCall`) to run once the form has been shown, so the row is laid out correctly from the first paint
## 0.4.7.1
- **Advanced-settings checkbox row no longer overlaps in long-string languages (tester report, German):** the four checkboxes (allow system disk / mask serial / exclude repetitive messages / flush volumes) sat at fixed X positions, so auto-sized captions like "Seriennummer im Protokoll maskieren" ran into the next box. The row is now re-flowed after every language change using each checkbox's actual preferred width with a fixed gap, and the three longest translations of the mask-serial caption were tightened (German "Seriennummer im Log maskieren", French "Masquer le n° de série", Russian "Маскировать серийный номер" — the full "in the log" wording remains in the hint)
- **UASP method label punctuation made consistent (tester report):** "UASP-Methode (Experte):" ended with a colon while the neighboring advanced-settings labels (Timeout (ms), Wiederholungen, Batch-Einträge, …) do not; the trailing colon is removed in all 8 UI languages
## 0.4.6.1
- **USB-disconnect popup no longer tells NTFS users to reformat:** the bus-disconnect guidance ended with a leftover "Format the drive as NTFS to use Windows ReTrim, then try again" line that was written for the exFAT dead-end case but appeared unconditionally — confusing when the crashed drive was already NTFS (tester report). The line is replaced with "once the drive is detected again, refresh the disk list and run the operation again" in all 8 UI languages; the reformat advice remains only in its dedicated UAS + non-NTFS dead-end notice
- **Log error text is now reliably English:** requesting the en-US message from `FormatMessageW` still returned localized text on systems without the English MUI resources (e.g. German-only Windows, as the tester confirmed). `LastErrorToString` now consults a built-in English table for the ~35 Win32 errors this tool actually hits on storage paths (1117 I/O device error, 55 device no longer available, 1167 not connected, 121 semaphore timeout, 483 fatal device hardware error, …) and only falls back to `FormatMessageW` for uncommon codes
- **German grammar fix** in the ATA probe note: "vom tatsächlichen SSD" → "von der tatsächlichen SSD" (die SSD)
- **UASP method selector (Auto / SCSI UNMAP / ATA Pass-Through) is back in release builds** (advanced settings, tester request): forced SCSI UNMAP also trims FAT32/exFAT free space, which the NTFS-only Optimize-Volume -ReTrim path ignores, and skips PowerShell overhead. The control is labeled as expert-mode with a localized warning hint (all 8 UI languages), defaults to Auto, and is intentionally not persisted — it resets to Auto on every start, like the system-disk override, so a forced method cannot linger; an explicit forced method keeps its historical precedence over the "Windows ReTrim only" checkbox
## 0.4.5.1
- **English Windows error text in logs:** `LastErrorToString` now requests the English (en-US) message from `FormatMessageW` first and only falls back to the OS default language when no English resource is installed — previously a German/other-locale OS injected localized `errtxt` lines into otherwise-English logs (tester report: `winerr=1117` shown in German with English UI)
- **ATA-path capability labels made explicit (tester feedback, RTL9210B/JMS583/ASM2364 enclosures):** the disk-list tag now reads `ATA-TRIM:YES/NO` instead of `TRIM:YES/NO`, and the inspect detail label is `ATA TRIM Capable` — NVMe bridges emulate SAT IDENTIFY, so the old wording implied the SSD itself could not TRIM when only the ATA path was inapplicable. The inspect panel also appends a localized note that ATA results are bridge-reported (SAT) and that UAS devices TRIM via SCSI UNMAP/DSM (all 8 UI languages)
- **Friendlier ReTrim fallback prompt:** when the hardware TRIM paths are genuinely rejected by the bridge, the confirmation popup now states that for NTFS Windows "Optimize-Volume -ReTrim" is the safe, native path (same reassurance the Pinned-Volume-guard prompt already gave) instead of presenting ReTrim as a mere degraded fallback; the prompt is selected by rejection reason, so UAS sticks (e.g. Vansuny) get the same wording (all 8 UI languages)
## 0.4.4.1
- **Real Test STEP5 — 1GB file integrity verification:** after STEP4 verifies the 500MB `file_*.bin` set, a new STEP5 verifies the 1GB `gig_*.bin` files under `dumy/` with the same path-hash seed / expected-CRC path used for STEP4. Start/end step combos now include step 5 (default end step is 5); when STEP3 was skipped in the current session, STEP5 rescans `dumy/gig_*.bin` so a mid-run restart can still verify. Help text and start-step hints describe the five-step flow in all 8 UI languages
- **Verification result counts in the log:** STEP4, STEP5, and Verify-only completion lines now report how many files were checked, how many passed, and how many failed (missing file or CRC mismatch). A failed file is logged individually and the run continues through the rest of the set; any failure marks that step (and the overall result) as failed, while STEP5 still runs after a failed STEP4 so both sets are fully checked
## 0.4.3.1
- **USB hot-plug auto refresh:** the disk list now refreshes automatically when a USB disk is attached or removed. A dedicated notification window registers for disk device-interface notifications (`RegisterDeviceNotification` + `WM_DEVICECHANGE`), so arrival/removal is detected even for disks without any volume; a 1.2 s debounce collapses each plug event's message burst into a single refresh, and the refresh is deferred while a TRIM run or another refresh is in flight
- **Selection preserved across refreshes:** after an automatic or manual refresh, the previously selected disk stays selected as long as it is still attached with unchanged connection identity (PhysicalDrive number, size, bus/enumeration path); only when that disk is gone or changed does the selection fall back to the default
- **New "Exclude repetitive messages" option** (advanced settings, all 8 UI languages): suppresses the per-batch info lines emitted inside the TRIM iteration loop (batch send/complete lines, per-command diagnostics, volume dismount notices) in both the file log and the UI log. Warnings and errors are always logged, the pre-loop plan summary and the end-of-run totals remain, and a single notice line records that suppression was active
- **Settings are now persisted:** UI options are saved to `settings.ini` next to the executable on exit and restored at startup — language, dry-run, Windows-ReTrim-only, advanced panel visibility, timeout/retry/batch entries/batch run limit, serial masking, repetitive-message exclusion, volume flush, and the real-test step/speed-drop settings. The dangerous "allow system disk" override is intentionally **not** persisted and must be re-armed each start; if the folder is not writable, saving is silently skipped
- **Version is read from the executable:** the window title and `APP_VERSION` now come from the exe's embedded VS_VERSION_INFO resource at runtime instead of a compile-time `version.inc`, so the displayed version always matches the binary (single source of truth: the project's version info)
- **Release packaging:** Release builds run a post-build script that copies the output to a versioned filename (`iodd_trim_tool-x.x.x.x.exe`, version taken from the exe's FileVersion); Release builds also no longer define `DEBUG_MODE`, so debug-only per-command IO diagnostics and the debug UASP method selector are excluded from release binaries
## 0.3.0.2
- **Fixed unresponsive/uncancelable runs on flaky USB bridges:** the ATA Pass-Through TRIM path called `DeviceIoControl` (and the per-batch `CreateFileW`) synchronously on the executor thread, so a stuck bridge (e.g. some INITIO INIC security enclosures) could block it for minutes — the cancel button did nothing and the app appeared frozen. Each ATA batch now runs on a bounded worker thread: the executor polls for cancel every ~250 ms and enforces a wall-clock cap, so a user cancel is honored promptly and a genuinely hung bridge is abandoned (marked as a bridge hang) instead of pinning the run. The wall-clock cap is set well above the per-command timeout so slow-but-working bridges that need ~20 s just to open the handle per batch are not aborted
- **Stale disk selection guard on Start:** pressing Start used to run against the disk snapshot from the last refresh, with no check that reality still matched — a replugged device can take over the same PhysicalDrive number (TRIM would hit the wrong disk), and reformatting a volume (NTFS ↔ exFAT) silently invalidated the cached file-system list that drives the mode decisions (free-space eligibility, Pinned Volume guard, ReTrim fallback). Start now re-probes the selected disk (serial/size via a new lightweight `ProbeDiskIdentity`) and re-enumerates its volumes (letters, file system, partition offsets/lengths); on any mismatch it shows a localized "disk configuration changed — refreshing the list" warning, logs the specific reason, auto-refreshes the disk list, and aborts the run
- **Post-TRIM disk initialization is now conditional on the run outcome:** the pending GPT/MBR initialization after a full-device TRIM used to run unconditionally — even after a completely failed run, a user cancel, a detected bridge hang, or a **dry run** (which must not modify the disk at all). It now runs only for an executed run with at least one successful batch and no cancel/bridge-hang, and logs a localized "initialization skipped" notice otherwise. Partial success still initializes, since the leading LBAs (old partition table area) may already be trimmed
- **Fixed cancel being ignored during the Optimize-Volume -ReTrim fallback:** after the user cancelled, the run still entered the PowerShell ReTrim fallback and its multi-second remount waits, forcing a second cancel. The fallback is now skipped entirely when a cancel is pending, and the per-volume ReTrim loop breaks out promptly on cancel
- **Truthful ReTrim fallback prompt for the Pinned Volume guard:** when the tool itself blocked the raw paths (SCSI UNMAP / ATA Pass-Through) to keep a mounted NTFS volume online, the confirmation popup still claimed "hardware TRIM paths were all rejected by this USB bridge", which was misleading. The guard case now shows its own localized message explaining that the hardware paths were skipped on purpose to protect the mounted NTFS volume and that Windows "Optimize-Volume -ReTrim" is the safe, native path for NTFS; the original "rejected by the bridge" text remains for genuine hardware-path exhaustion
- The abandoned ATA worker owns its own pass-through engine and runs it **without any logger reference**: a worker still blocked in `DeviceIoControl` can outlive the executor thread and even the main form's logger, so it must not touch any external object. The executor now emits the per-command `scsi_status` diagnostic line itself after joining the worker (`SendTrimBatch` exposes the SCSI status as an out parameter), keeping log output identical to before
## 0.3.0.1
- **Fixed DSM TRIM false success:** the UASP DSM TRIM path could report a batch as **succeeded** (and even log "trimmed" sectors) when no volume range was actually trimmed — e.g. an unformatted UAS SSD with no mapped volume, or a batch whose ranges fell outside every mapped volume. `SendDsmTrimBatch` now returns the number of ranges actually applied, and the executor only treats DSM as successful when at least one range was trimmed; otherwise it falls through to the lower fallback paths (SCSI UNMAP / WRITE SAME(16) / ATA Pass-Through)
- Initialized the DSM `winerr` accumulator before the per-volume loop so the failure report no longer shows a stale error code when the loop does not run
- **Corrected Full-device TRIM on UASP:** DSM TRIM is issued on **volume handles with volume-relative offsets**, which is only valid for **free-space** mode. In **full-device** mode the ranges are absolute whole-disk LBAs, so DSM is now disabled for that mode and the run uses the physical-handle paths (SCSI UNMAP / WRITE SAME(16) / ATA Pass-Through) which address absolute LBAs correctly
- The **Pinned Volume guard** is now limited to free-space mode as well: full-device mode has no Optimize-Volume -ReTrim fallback, so pinning would dead-end every raw path; full-device now uses lock+dismount + raw paths as intended. BOT transport and the free-space + UASP path are unchanged
- **Robustness:** guarded `FindLastUsedLbaForVolume` against an empty drive letter (prevented a possible range-check crash in debug builds)
- **Performance:** the logger now keeps the log file open for the session instead of opening/closing it on every line, removing heavy per-line file I/O during large full-device runs
- **Performance:** the DSM entry-limit splitter now pre-allocates its output in a single pass, replacing tens of thousands of incremental reallocations when planning a full-device TRIM on a large disk
- **Diagnostics:** free-space planning now reports a specific reason when no ranges are produced — "no eligible volume mapped", "no free space (volumes appear full)", or "bitmap could not be read" — instead of a single ambiguous message
- **Admin check:** elevation is now determined from the process token (`TokenElevation`), correctly reflecting UAC, with the previous PhysicalDrive0 RW-open probe kept only as a fallback
- **Multilingual logs:** migrated the remaining hard-coded Korean log/status strings (CSV create/save failures, physical-address lookup failure, STEP4 verify status, post-TRIM GPT/MBR initialize messages, "no files in test folder") into the `uLang` translation table with full Korean/English/Japanese/Chinese/Spanish/German/French/Russian entries, so non-Korean UIs no longer show Korean text in these paths
- **Strict bridge probe:** `CheckBridgePassThroughProbe` now actively issues a read-only SAT (ATA Pass-Through) **IDENTIFY DEVICE** command in addition to the raw-handle open check; on a BOT bridge a failed SAT probe is surfaced as a warning (ATA TRIM likely unsupported), while UAS bridges remain non-blocking since they TRIM via SCSI UNMAP / DSM
- **Robustness:** `SaveFailureReport` now builds its JSON with `fpjson` instead of hand-rolled string concatenation, so quotes/backslashes/control characters in the device path and batch message text are always escaped correctly; the JSON and CSV reports also share a single timestamp instead of calling `Now` twice
- **Robustness:** added an explicit upper-bound guard on the per-batch ATA payload sector count — a batch exceeding the 16-bit PASS-THROUGH(16) block-count field now fails with a clear "reduce batch entries" message instead of silently truncating the `Word` and under-trimming
## 0.2.0.28
- Fixed remaining **multilingual UI gaps** in the Real Test area: the target-drive hint, Speed Chart button, chart window title/buttons/info text, dynamic status prefixes (`Status`, `Speed`, `Elapsed`, `Step`), and **FS policy** messages are now connected to the translation table
- Shortened the German **Speed drop wait** label to **Speed-Drop (Min)** so it no longer overlaps the adjacent input/control area
- Corrected Japanese and Simplified Chinese default status labels that still displayed English `Status: Idle` / `Status: Running`
## 0.2.0.27
- Adjusted the TRIM Execute tab layout so long option captions fit better: reduced the **Windows ReTrim only**, **serial masking**, and **flush volumes** control widths, tightened the advanced settings panel height, and moved the status/log group upward
- Fixed the advanced-options tab order after the layout adjustment
## 0.2.0.26
- Removed the visible **Expert ATA Pass-Through retry after ReTrim failure** option; auto mode now enables the guarded retry path internally, so after a ReTrim 43022 failure the existing warning/confirmation prompt can still offer ATA Pass-Through without an extra checkbox
- Updated the USB disconnect guidance in all 8 UI languages: users are now told **not to close the tool** and **not to use Windows “Safely Remove Hardware”**, but to physically unplug/reconnect first and wait for Windows re-detection
- Fixed the German advanced-options layout where **serial-number masking** text could overlap the adjacent **flush volumes** checkbox
- Added USB 3.0+ requirement wording to the USB guide; disk list/detail text now shows a USB requirement marker when speed clues are available (`USB2 - unsupported`, `USB3+`, `USB3+ likely`, or `USB3+ required`)
- Completed missing localized Expert ATA retry strings in Japanese, Simplified Chinese, Spanish, German, French, and Russian so no fallback English text remains if the string is referenced internally
## 0.2.0.24
- **ASM1153E regression guardrail:** fixed the UAS+NTFS dead-end where Pinned Volume guard skipped ATA Pass-Through, then `Optimize-Volume -ReTrim` failed with `StorageWMI 43022` and the run ended with `0` trimmed batches
- Added an optional **Expert retry** path: when ReTrim fails with 43022 under Pin guard, the tool can prompt once and run lock/dismount + ATA Pass-Through as a second chance
- **UI / mode selection:** fixed a **repeated “Restriction” (Full TRIM blocked)** dialog loop when a disk had mapped volumes but **free-space TRIM** was not allowed (for example **FAT12/16** reported as `FAT`): the mode handler no longer flips the radio back to **Full TRIM**, which re-triggered the same handler. Added `FRgModeUpdating` to avoid re-entrancy from programmatic `TRadioGroup` changes; **FS policy** label now matches the refresh path (`free-space mode disabled - …`) and **plain FAT** volumes get an explicit reason instead of the misleading “no NTFS/exFAT/FAT32 volume mapped” text
- **Disk list:** USB candidates with reported capacity **≤ 1 GiB** (1024³ bytes) are **omitted** from enumeration (small firmware / dongle volumes)
## 0.2.0.22
- **SSK exFAT post-failure hang fix:** after a SCSI UNMAP **bridge hang** is detected, the executor no longer runs the delayed **PhysicalDrive** `CreateFileW` reopen probe (that probe could block for minutes during USB reset/removal). Failure path now skips straight to USB reconnect / NTFS guidance
- SCSI UNMAP bridge-hang dead-ends now record a clear batch message: `SCSI UNMAP bridge hang detected; skipped unsafe fallback paths` (fixes empty `Batch %d failed:` log lines)
## 0.2.0.21
- Reduced the long **Starting from PC** dead-end on fragile UAS + non-NTFS bridges by capping the first SCSI UNMAP timeout to 2.5 s before skipping ATA Pass-Through on a detected bridge hang
- Added a short delayed USB disconnect probe after failed runs: the executor now polls `\\.\PhysicalDriveN` for up to 3 s before choosing between the USB-disconnect guidance and the non-NTFS guidance, covering Windows' delayed device-removal notification
## 0.2.0.20
- Updated the **USB bus disconnect guidance** to explicitly include Martin's requested recovery path: unplug/reconnect the drive, wait for Windows to detect it again, then **format as NTFS to use Windows ReTrim** and retry
## 0.2.0.19
- **USB bus disconnect guidance:** after a run, if a batch recorded `winerr=55` (`ERROR_DEV_NOT_EXIST`) or reopening `\\.\PhysicalDriveN` fails with `55` / `1167` (`ERROR_DEVICE_NOT_CONNECTED`), the tool shows a single localized notice (unplug/reconnect, wait for re-enumeration, optional NTFS ReTrim). When this notice is shown, the existing **UAS + non-NTFS** “reformat as NTFS” popup is **suppressed** so the user is not buried in two dialogs
## 0.2.0.18
- **TRIM compatibility warning** (`NeedsUserConfirm`): if the disk has at least one **NTFS** volume, the warning dialog is **skipped automatically** (same as the user clicking Yes). Rationale: `Optimize-Volume -ReTrim` remains a viable fallback after hardware paths fail, so the popup added little value for NTFS users
- Non-NTFS disks (exFAT/FAT32 only) still see the warning; declining still shows the informational message about NTFS being required for Windows ReTrim
## 0.2.0.17
- After the **TRIM compatibility warning**, if the user clicks **No** (decline hardware TRIM): **offer Windows ReTrim-only mode**
- When at least one **NTFS** volume exists on the disk, a second prompt asks whether to continue with **Optimize-Volume -ReTrim only** (automatically enables the "Windows ReTrim only" option for this run)
- When there is **no NTFS** volume (e.g. exFAT only), an informational message explains that ReTrim requires NTFS and suggests reformatting or enabling the checkbox manually after switching to NTFS — avoids users exiting with no path forward
## 0.2.0.16
- **Preflight TRIM warning no longer shown when ATA IDENTIFY reports TRIM** (`TrimCapable`, Word 169 — same basis as tools showing **TRIM: YES**)
- Windows `StorageDeviceTrimProperty` reflects what the **USB bridge tells storport**, which often fails (`winerr=24`) or reports `TrimEnabled=0` even when the **SSD behind SAT** correctly advertises DSM/TRIM in IDENTIFY
- In that common mismatch case the popup was misleading and only annoyed users; the executor still performs runtime validation as before
## 0.2.0.15
- Added **Path 3 fallback CDB configs: ATA Pass-Through(12)** — opcode `0xA1`, 12-byte CDB
- Some older / cheaper USB-SATA bridges only recognize the 12-byte ATA Pass-Through form and reject the standard 16-byte form (`0x85`) with sense `5/32/0` (INVALID OPCODE)
- The Path 3 CDB rotation now tries 5 configs in order: PT16 DMA+EXTEND → PT16 DMA → PT16 PIO → **PT12 DMA → PT12 PIO**
- PT12 cannot express LBA48 / EXTEND, but the DSM TRIM payload carries LBAs in the data-out buffer, not the CDB, so this is fine. PT12 sector count is 8-bit (max 255 sectors); we skip PT12 when payload exceeds 255 sectors to avoid silent truncation
- `SendTrimBatch` now auto-detects the CDB length from the opcode (`0xA1` → 12 bytes, `0x85` → 16 bytes) so storport sees the correct `CdbLength` field
- Added **preflight TRIM compatibility warning popup** (Method F)
- When `IOCTL_STORAGE_QUERY_PROPERTY (StorageDeviceTrimProperty)` fails or reports `TrimEnabled=0`, the user is now shown a non-blocking warning popup before TRIM starts: *"This USB-SATA bridge reports that TRIM is NOT supported. All TRIM paths (DSM / SCSI UNMAP / WRITE SAME / ATA Pass-Through) are likely to fail. Continue anyway?"* (translated to 8 languages)
- Avoids the previous experience where users on TRIM-incapable bridges (e.g. ROG ESD-S1C, certain VANSUNY / SSK exFAT setups) only discovered the dead-end after 30+ seconds of failed attempts
- The check is only a warning — users can still proceed if they want to verify the bridge state experimentally; the previous "hard block" on `TrimEnabled=0` has been relaxed because some bridges under-report TRIM capability while still honoring ATA Pass-Through
## 0.2.0.14
- Added new fallback path **Path 2.5: WRITE SAME(16) with UNMAP=1** (SBC-3 §5.41) for USB-SATA bridges that decline DSM TRIM and SCSI UNMAP at the IOCTL layer
- Some cheap UAS bridges (typically shipped with **VANSUNY / SSK / Hiksemi / Move Speed / KEXIN / ORICO / Netac / Fanxiang** drives in exFAT) report `TRIM not supported` via `StorageDeviceTrimProperty`. Storport then silently rejects `IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES` (DSM TRIM) and the SCSI UNMAP IOCTL with `winerr=5, scsi_status=0` even after dismount
- `WRITE SAME(16)` is a regular block-write opcode that bypasses the storport TRIM filter — only the bridge's own SAT layer decides whether to honor the `UNMAP=1` bit. Some bridges that decline the dedicated `UNMAP (0x42)` opcode still honor `WRITE SAME (0x93)` because they share the same write data path
- **Probe-first safety:** SBC-3 mandates that a device unable to unmap must instead write the data block (zeros) to every LBA in the range. To avoid silently zero-filling 100 GiB of "free" space, the executor first issues one small probe (max 8 sectors = 4 KiB) on the first range. If the probe fails, the run aborts WRITE SAME entirely and falls through to ATA Pass-Through
- Skip conditions for Path 2.5: Pinned Volume guard active, bridge hang detected on UNMAP, or probe already failed earlier in this run
- Updated UAS preflight transport message to advertise the new path: `UASP (DSM TRIM incl. FS-cooperative retry / SCSI UNMAP / WRITE SAME(16)+UNMAP fallback / ATA Pass-Through under Pinned Volume guard / Optimize-Volume -ReTrim fallback)`
## 0.2.0.13
- Fixed bus disconnection on **SSK / VANSUNY / Hiksemi / Move Speed / KEXIN / Netac / Fanxiang / ORICO** UAS USB-SATA bridges (typical Amazon-shipped China-made USB SSDs, all formatted in **exFAT** by the vendor)
- Root cause: these bridges hang for ~5–6 s on the first SCSI UNMAP, then the bus is reset by the host. The previous flow then issued ATA Pass-Through against the (now disappearing) device, which caused `winerr=55` "device no longer available" and full disconnection from the OS
- **Bridge-hang detection:** the executor now measures the elapsed time of the first SCSI UNMAP. If it returns `winerr=1117` (ERROR_IO_DEVICE) after `TimeoutMs - 500 ms` or longer, the bridge is treated as hung
- **Path 3 ATA Pass-Through is automatically skipped** when a bridge hang is detected, so the device stays enumerated and the run ends cleanly instead of crashing the bus
- Added **user-facing guidance popup** when the run dead-ends on a UAS bridge with **no NTFS volume** (so the `Optimize-Volume -ReTrim` fallback does not apply): "This USB-SATA bridge does not support TRIM on exFAT/FAT32. Please reformat the drive as NTFS to use Windows ReTrim, then try again." (translated to 8 languages)
## 0.2.0.12
- Fixed regression on **BOT (USB Mass Storage)** devices with NTFS volumes introduced by 0.2.0.10's Pinned Volume guard
- Affected case: IODD External HDD (BOT bridge, NTFS + FAT32 + exFAT volumes) that previously trimmed via ATA Pass-Through (worked in 0.2.0.9, failed in 0.2.0.10 / 0.2.0.11)
- Root cause: Pinned Volume guard was applied to BOT as well, but BOT bridges typically report "TRIM unsupported" to Windows storage stack, which makes the `Optimize-Volume -ReTrim` fallback fail with `StorageWMI 43022` ("not supported by the hardware backing the volume"). The guard skipped Path 3 (ATA Pass-Through), which is the only working hardware path on these bridges
- **Pinned Volume guard now applies to UAS transport only** (was UAS + BOT)
- BOT devices revert to 0.2.0.9 behavior: lock + dismount + Path 3 ATA Pass-Through (the only path that actually trims on bridges that decline TRIM at the SAT layer)
- UAS NTFS devices keep the 0.2.0.11 protection unchanged (Vansuny U52, SSK USB 3.2 SSD, ROG ESD-S1C, ...)
- Updated BOT preflight transport message to reflect that Pinned Volume guard is intentionally not applicable
## 0.2.0.11
- Fixed regression on **exFAT / FAT32** USB SSDs introduced by 0.2.0.10's Pinned Volume guard
- Affected case: JMicron Generic USB-SATA bridge with exFAT volume that previously trimmed via SCSI UNMAP single-descriptor mode (worked in 0.2.0.9, failed in 0.2.0.10)
- Root cause: Pinned Volume guard blocked the SCSI UNMAP path with `ERROR_ACCESS_DENIED`, but the `Optimize-Volume -ReTrim` fallback returns `StorageWMI 43025` ("This volume cannot be optimized") on non-NTFS volumes, so the run hit a dead end
- **Pinned Volume guard now activates only when at least one volume on the disk is NTFS**
- exFAT / FAT32 disks fall back to the legacy lock + dismount path (where the SCSI UNMAP single-descriptor retry can actually reach the bridge)
- NTFS disks (Vansuny U52, SSK, ROG ESD-S1C, etc.) keep the 0.2.0.10 protection unchanged
- Skipped Path 3 (ATA Pass-Through) entirely when Pinned Volume guard is active
- Previously, ATA PT was attempted after SCSI UNMAP was rejected; it was not actually blocked by the OS (ATA PT is wrapped in SCSI), so it stressed the bridge and wasted ~22 s on retries before reaching the PowerShell fallback
- Tightened `Optimize-Volume -ReTrim` fallback eligibility to **NTFS only** (previously NTFS / exFAT / FAT32) to match Microsoft's actual support matrix and avoid false positives that block working hardware paths
## 0.2.0.10
- Added **Pinned Volume guard** (Option A) for auto-mode UAS/BOT executions on disks with at least one mounted volume
- Volume handles are opened with `FILE_SHARE_READ|WRITE` but **never locked or dismounted**, so NTFS keeps owning the volume
- Any raw SCSI UNMAP / ATA Pass-Through targeting a mounted volume's LBA range is rejected by Windows storport with `ERROR_ACCESS_DENIED` **before reaching fragile USB-SATA bridges** (Vansuny U52, SSK USB 3.2 SSD, ROG ESD-S1C, ...)
- Tool then falls through cleanly to the existing `Optimize-Volume -ReTrim` fallback (Path 4), which uses the cooperative defrag stack and does not crash these bridges
- DSM TRIM (Path 1) still runs first and benefits compatible UASP devices (e.g. HP 911 Pro) at full hardware speed
- Legacy lock + dismount path is preserved only for the explicit DEBUG-forced **SCSI UNMAP** / **ATA Pass-Through** modes, where the user intentionally bypasses the Pinned Volume guard
- UASP / BOT preflight transport message now mentions the Pinned Volume guard
## 0.2.0.9
- Added user-selectable **"Windows ReTrim only (skip DSM / UNMAP / ATA Pass-Through)"** checkbox in the Advanced options panel
- Targets USB-SATA bridges that hang or disconnect during SCSI UNMAP / ATA Pass-Through (observed on **Vansuny U52**, **SSK USB 3.2 SSD**)
- When enabled, all hardware TRIM paths and the preflight Lock/Dismount step are skipped, and `Optimize-Volume -ReTrim` is invoked directly (without the confirmation popup, since the user explicitly selected this mode)
## 0.2.0.8
- Fixed `Optimize-Volume -ReTrim` fallback failing on bridges where SCSI UNMAP / ATA Pass-Through dismount the volume before the fallback runs (observed on **ROG ESD-S1C UASP**)
- Volume handles held by the executor are now released before invoking PowerShell so Windows can auto-remount the drive letter
- Added drive-letter mount polling (`GetVolumeInformationW`, up to 8 s) before each `Optimize-Volume` call; a warning is logged and the volume is skipped if it does not remount in time
- Forced UTF-8 console output for the `powershell.exe` child (`[Console]::OutputEncoding = [Text.Encoding]::UTF8`) and decoded captured stdout/stderr as UTF-8 so non-ASCII PowerShell error messages survive intact in the log
## 0.2.0.7
- Improved compatibility with USB-SATA bridges that decline standard TRIM IOCTLs (e.g. **Vansuny U52 USB 3.2 Gen 2**, **SSK USB 3.2 SSD**)
- Added **FS-cooperative DSM TRIM** retry: when the first DSM TRIM call returns `ERROR_INVALID_FUNCTION` (typical for bridges that don't advertise `StorageDeviceTrimProperty`), the tool re-issues the same batch without the `DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED` flag so NTFS/exFAT can dispatch the trim on its behalf
- Added **`Optimize-Volume -ReTrim` PowerShell fallback** (free-space mode + mounted NTFS/exFAT/FAT32) when DSM, SCSI UNMAP, and ATA Pass-Through all fail; a confirmation popup is shown before invocation, child stdout/stderr is logged, and remaining batches are skipped after success since `-ReTrim` covers all free space at once
- Updated UASP transport message to reflect the extended path set: `DSM TRIM (incl. FS-cooperative retry) / SCSI UNMAP / ATA Pass-Through / Optimize-Volume -ReTrim fallback`
## 0.2.0.6
- Fixed incorrect "Last used LBA" calculation for NTFS and exFAT in Drive Inspection tab
- NTFS: replaced free-range inference with backward bitmap scan via `FSCTL_GET_NTFS_VOLUME_DATA` for accurate highest-used-cluster detection
- exFAT: replaced FAT table scan with allocation bitmap backward scan (correctly handles "No FAT Chain" contiguous files that have no FAT entries)
- FAT32: unchanged (FAT table backward scan)
- Partition last-used-byte logic shared as `GetUsedBytesForVolume` in `D:\data\lazarus_lib\uDiskLastSector.pas` (used by this project and `iodd_HDD_4`)
## 0.2.0.4
- Added pre-TRIM step before file creation in Real Test STEP1 to optimize SSD write performance
- Added TRIM result logging with batch/sector statistics after each TRIM operation
- Added write speed chart: 2-second interval time-series data recorded during test, viewable via "Speed Chart" button after completion
- Speed chart supports Save CSV export and displays step boundary markers
- Fixed speed drop detection: replaced fixed-window average with Exponential Moving Average (EMA) for accurate detection
- Fixed speed drop wait not triggering due to 60-second cooldown blocking legitimate detections across steps
- Removed cooldown mechanism (unnecessary with EMA-based averaging)
- Display total disk sectors alongside GiB in Drive Inspection summary
- Changed "Last free start LBA" to "Last used LBA" for clearer disk usage display
- DSM TRIM initial failure and multi-descriptor SCSI UNMAP rejection now logged as WARN instead of ERROR
- Deferred volume Lock/Dismount to preserve mounted state for DSM TRIM (fixes ACCESS_DENIED on first attempt)
## 0.2.0.3
- Implemented 3-stage UASP TRIM strategy: DSM TRIM → SCSI UNMAP → ATA Pass-Through
- Added SCSI UNMAP single-descriptor fallback when multi-descriptor batch is rejected
- Volume Lock/Dismount now performed once before the batch loop and held for the entire operation (UASP and BOT)
- Improved cancel responsiveness: added cancellation checks inside UNMAP and ATA PT inner loops
- Changed default SCSI timeout from 20s to 5s (minimum 1s)
- Added UASP TRIM method selector in DEBUG_MODE builds (Auto / SCSI UNMAP / ATA Pass-Through)
## 0.2.0.2
- Added "Size (GB)" column after "Total Sectors" in the Inspect tab grid
- Added percentage display to "Free Sectors" and "Used Sectors" columns (e.g. `920664 (1.5%)`)
- Added auto-size columns after inspection to prevent data from being clipped
## 0.2.0.1
- Bug fixes across 7 source files (25 issues resolved)
- **Critical**: Fixed use-after-free in logger destructor when queued UI callbacks reference freed object
- **High**: Fixed SCSI sense data parsing to distinguish fixed-format (0x70/0x71) vs descriptor-format (0x72/0x73)
- **High**: Fixed worker thread accessing UI controls directly — TRIM options now captured on main thread before execution
- **High**: Fixed shared `TVolumeBitmapReader` concurrent access from worker thread — now uses dedicated instance
- **High**: Fixed `FreeOnTerminate=True` threads causing race condition in `FormDestroy` — properly stopped and freed
- **High**: Fixed Debug-build range-check crash on flexible array (`array[0..0]`) access in disk extent enumeration
- **High**: Added `{$packrecords 8}` for `LARGE_INTEGER`-containing records to prevent 32-bit build field misalignment
- **Medium**: Fixed `GetLastError` being clobbered before capture in `ProbeIdentifyDevice`
- **Medium**: Added minimum size guard (36 bytes) for `TStorageDeviceDescriptor` before accessing fields
- **Medium**: Added null terminator to device descriptor buffer to prevent unbounded string reads
- **Medium**: Fixed all early exits in real test `Execute` to set `FResultMessage` (previously silent failures)
- **Medium**: Fixed `FillFiles` batch directory logic — files 1000+ now correctly placed in `batch_N/` subdirectories
- **Medium**: Moved `CloseFile` into `finally` blocks to prevent TextFile handle leaks on exceptions
- **Medium**: Fixed logger deadlock potential — UI callback now invoked outside critical section lock
- **Medium**: Wrapped `AppendLineToFile` in `try..except` to prevent I/O exceptions from crashing worker threads
- **Low**: Fixed `FIsWriting` flag not being reset on early exit from file fill operations
- **Low**: Fixed `FileCrc` returning partial CRC on cancellation, causing false verification failures
- **Low**: Removed duplicate `FreeStatsSummary` append in disk selection change handler
- **Low**: Fixed `FlushVolumesIfRequested` using wrong field (`VolumePath` → `DriveLetter`) for device path
- **Low**: Fixed `GetFileSize` mishandling exact 4GB-1 byte files — added `GetLastError = NO_ERROR` check
- **Low**: Added thread safety to `SetUiSink` and `DisableFileOutput` (lock acquisition)
- **Low**: Fixed Chinese Traditional users incorrectly mapped to Simplified Chinese
- **Low**: Added `ERROR_INVALID_FUNCTION` as permanent ATA failure code to avoid useless retries
- **Low**: Fixed empty `MessageText` when all ATA retries exhausted without hitting permanent error
- **Low**: Capped retry backoff delay at 30 seconds to prevent `Cardinal` overflow
- **Low**: Fixed `JsonEscape` to handle control characters (`\n`, `\r`, `\t`, etc.)
- **Low**: Replaced O(n²) bubble sort with QuickSort in `MergeRanges` for fragmented volume performance
- **Low**: Protocol detection heuristic now scans only raw device properties, skipping binary header
## 0.1.0.5
- Added UASP (USB Attached SCSI) support with dual-path TRIM strategy
- UASP devices: SCSI UNMAP first, automatic fallback to ATA Pass-Through if UNMAP fails
- BOT devices: ATA Pass-Through (unchanged)
## 0.1.0.4
- Changed "Flush Volumes before TRIM" checkbox default to checked
## 0.1.0.3
- Changed Dry Run checkbox default to unchecked
## 0.1.0.2
- Added logical drive selector in the Real Test tab, allowing users to choose which volume to use for testing
- Added read-only drive detection: log and CSV files are no longer created when running from a read-only drive
## 0.1.0.1
- Initial release
- USB-SATA bridge ATA Pass-Through support for SSD TRIM
- TRIM execution with Full Device and Free Space Only modes
- Drive inspection tab with volume bitmap analysis and free-range CSV export
- Real test tab with 4-step write/delete/TRIM/verify cycle
- Multi-language support: Korean, English, Japanese, Chinese, Spanish, German, French, Russian
- Automatic OS language detection with manual language selection
- TRIM and ATA Pass-Through capability detection displayed in disk info
- Partition file system type shown in disk selection combo box
- GiB display alongside raw byte values in disk info
- Top toolbar buttons: IODD Product, IODD Manual, How to Use, TRIM Info, License
- Success/failure popup notifications for TRIM execution and real test verification
- Stop button support during Verify Only mode
- Administrator privilege requirement via application manifest