Long path support phase 2 — shell/UI, shared-directory recursion, exact-name paths, and path-helper audit
Summary¶
Core long-path support is already landed on main for filesystem operations. Phase 2
shell/UI hardening, exact-name path support, share-recursion unification, and helper
cleanup are now merged to main.
The implementation now covers:
- recycle-bin delete through
IFileOperationinstead ofSHFileOperation - dynamic path-helper cleanup for shell-facing module/profile/path assembly
- shared
IFileDialogwrappers for file/folder pickers - centralized shell icon/display fallback behavior for overlong paths
- silent
.lnkignore-by-extension in the relevant share/incoming scans - dynamic skin-resource path resolution without
MAX_PATHscratch buffers - consolidation of generic path semantics into
PathHelpersand shell/UI policy intoShellUiHelpers - one shared recursive share/unshare engine used by both the options directory selector and the Shared Files tree
- canonical path matching across DOS paths,
\\?\paths, slash variants, dot-segments, and 8.3 aliases - migration of persisted 8.3 shared entries to canonical long-name spellings during load
- exact-name namespace handling for paths that need Win32 namespace semantics to preserve leading/trailing spaces, trailing dots, and reserved DOS device-looking names such as
NUL.txt - directory recursion guards by filesystem object identity so junction/symlink loops do not explode recursive sharing
- deterministic regression coverage in
emulebb-build-testsfor delete, path-helper, shell/UI, exact-name, and shared-directory recursion behavior
Already Landed On Main¶
The following are no longer backlog items:
x64andARM64manifests carry<ws2:longPathAware>true</ws2:longPathAware>- startup detects and logs whether
LongPathsEnabledis enabled LongPathSeamscentralizes Win32 file APIs, CRT-bypass file descriptors,FILE*streams, and MFC safe-file opens- part files, config files, archive/import flows, ZIP/GZIP, web cert/key output, and similar high-value paths already use the shared long-path model
- real filesystem tests now cover long Unicode paths, Cyrillic/emoji path segments, generated payloads, copy/move/delete flows, and parity-style reference coverage
Implemented Scope¶
The active stabilization branches now cover the full FEAT-010 shell/UI and share-state tail:
PathHelpers.h: permanent generic path-helper layer for separator normalization, extended-length prefix handling, directory canonicalization, path joins, dynamic module/shell-folder retrieval, and related path-shape rulesShellUiHelpers.h: permanent shell/UI helper layer for picker normalization, shell icon/display fallback policy, shortcut ignore rules, and skin-resource resolutionIni2Helpers.h: INI-specific helper layer trimmed down to config/path decisions that reusePathHelpersinstead of duplicating generic path logicOtherFunctions.cpp/.h: sharedIFileDialogwrappers for folder pick, file open, and file save; compatibility entrypoints retained forSelectDir(...)andDialogBrowseFile(...)PartFileConvert.cpp,TreeOptionsCtrl.cpp,KnownFile.cpp,PPgFiles.cpp,MuleToolBarCtrl.cpp,StatisticsTree.cpp,CatDialog.cpp,PPgDirectories.cpp: migrated remaining browse/picker call sites to the shared wrappersDirectoryTreeCtrl.cpp,SharedDirsTreeCtrl.cpp,Emule.cpp: centralized icon/display fallback behavior for shell-facing UIPPgDirectories.cpp,SharedFileList.cpp,SharedFilesCtrl.cpp:.lnkfiles are now ignored by extension in the relevant share/incoming scans instead of consulting shell metadataFileInfoDialog.cpp,MiniMule.cpp,OtherFunctions.cpp: path-helper/module-path tails removed in the earlier FEAT-010 path-helper sliceEmule.cpp,MuleListCtrl.cpp: skin resource and background image resolution now use dynamic helper paths rather thanMAX_PATHbuffersTreeOptionsCtrl.cpp/.h,OtherFunctions.cpp/.h: dead legacy picker-era code was removed, includingCTreeOptionsFileDialog,SHBrowseSetSelProc, and the unused raw-bufferSelectDir(HWND, LPTSTR, ...)overload- broader
srchybridcallers now use shared helper rules instead of duplicatedslosh/unslosh/MakeFoldernamesemantics DirectoryTreeCtrl.cpp,SharedDirsTreeCtrl.cpp,SharedDirectoryOps.h: recursive share/unshare and descendant enumeration now go through one shared long-path-aware engine instead of divergent UI-local recursionSharedFileList.cpp,SharedFilesCtrl.cpp,Preferences.cpp,ListenSocket.cpp,KnownFileList.cpp,CatDialog.cpp: share-sensitive path matching now uses semantic path equality instead of raw spelling checksLongPathSeams.h: exact-name prefixing now covers trailing dot, leading/trailing ASCII space, and reserved DOS device-looking path components that require namespace semantics to preserve the real Win32 nameLongPathSeams.h,SharedDirectoryOps.h: recursive shared-directory expansion now uses directory object identity (volume serial + file ID) to suppress duplicate-target traversal and stop reparse-point loopsSharedFileList.cpp,SharedFilesCtrl.cpp,PathHelpers.h: drag/drop intake, single-share handling, and directory/file scans no longer rely onCFileFind; path-sensitive probes and enumeration now go through the shared helper/seam stackPreferences.cpp: loaded shared-directory and single-share entries are opportunistically migrated from 8.3 aliases to canonical long-name spellings
There are no remaining TODO:MINOR(FEAT-010) or TODO:MINOR(longpath) markers in the audited shell/UI long-path surface.
Fallback Policy¶
The Phase 2 implementation uses explicit shell/UI fallback behavior instead of relying on incidental shell success:
- icon lookup uses stable extension/attribute-based queries where possible rather than probing the full overlong path
- shell display names are optional enrichment; when the shell cannot provide one safely, existing caller text is preserved
- picker flows return full
CStringpaths from shared modern wrappers instead of caller-ownedMAX_PATHarrays .lnkfiles are ignored by extension in the relevant share/incoming warning flows instead of following shell metadata- directory-valued paths now share one normalization contract through
PathHelpersinstead of per-call-site trailing-\logic - exact-name paths that cannot be represented safely at shell boundaries stay on the filesystem-facing
LongPathSeamspath and do not get silently normalized into lossy shell spellings
Test Expansion¶
The stabilization branches now include:
- deep Unicode delete coverage for recycle-bin-enabled and direct-delete routing
- path-helper coverage for module paths, canonicalization, MediaInfo path joins, and MiniMule resource URLs
- shell/UI seam coverage for shortcut ignore rules, shell display fallback gating, icon-query routing, picker path splitting/finalization, and skin-resource resolution
- real-filesystem coverage for exact-name folders/files beginning or ending with
., ASCII space, NBSP, and EM SPACE - real-filesystem coverage for reserved DOS device-looking names such as
NUL.txt - share-list/path-matching coverage for prefixed vs unprefixed, 8.3 vs long-name, and exact-name spellings
- shared-directory recursion coverage for junction alias dedupe and junction-loop suppression by filesystem identity
Remaining optional verification is manual smoke coverage for representative browse/icon flows on a live UI.
Final Helper Architecture¶
The active stabilization branches now use a permanent helper split instead of the earlier transitional seam naming:
PathHelpers: generic path semantics onlyShellUiHelpers: shell/UI policy onlyOtherFunctionsSeams: delete-route seam/injection only where tests need it
This cleanup also removes duplicate extended-length prefix handling, duplicate
trailing-separator helpers, and duplicate directory-shape normalization from the audited
and overlapping srchybrid long-path surface.
For share-state specifically, the permanent split is now:
PathHelpers: canonical path normalization, comparison, containment, and enumeration helpersLongPathSeams: filesystem-facing Win32 calls and exact-name namespace preparationSharedDirectoryOps: the only recursive share/unshare implementation used by both directory treesShellUiHelpers: only shell-boundary policy and fallback behavior
Acceptance Criteria¶
- [x] Shell icon/display lookup is centralized and no longer scattered across the audited tree/list controls
- [x] Overlong paths still get sensible icons or graceful fallback behavior in directory/file UI
- [x] Browse-dialog flows are audited and routed through shared modern wrappers in the audited files
- [x] Delete-to-recycle-bin behavior for deep paths is explicitly hardened
- [x] Fixed-buffer path composition used in real path construction is reduced or eliminated in the audited files
- [x] Options-tree and Shared Files recursive share/unshare behavior now use one implementation
- [x] Share matching is stable across
\\?\, 8.3 aliases, exact-name namespace paths, and normal DOS spellings - [x] Recursive shared-directory expansion does not loop forever through junction/reparse aliases
- [ ] Manual smoke checks cover icon lookup and browse flows for deep Unicode paths where the shell APIs permit it
Explicit Boundaries¶
This item intentionally stops short of a full filesystem-object identity model for every path in the app.
- file-level hard-link dedupe is not implemented
- symlinked files outside shared-directory recursion are still matched by canonical path spelling, not file ID
- alternate data streams such as
file.txt:streamare not first-class app paths - nonstandard namespaces such as
\\.\,\??\, or\\?\GLOBALROOTare not first-class app paths - no live UNC/SMB share smoke test is recorded in this item yet
Reference¶
- Core implementation spec:
docs/history/features/GUIDE-LONGPATHS-IMPLEMENTATION.md - Microsoft shell/file API notes:
- https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shfileopstructa
- https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectoryw
- https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew
- https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathcanonicalizew