Skip to content

Code Quality: Replace storage helper P/Invokes - #18936

Open
0x5bfa wants to merge 2 commits into
files-community:mainfrom
0x5bfa:manualpinvokes2
Open

Code Quality: Replace storage helper P/Invokes#18936
0x5bfa wants to merge 2 commits into
files-community:mainfrom
0x5bfa:manualpinvokes2

Conversation

@0x5bfa

@0x5bfa 0x5bfa commented Sep 9, 2026

Copy link
Copy Markdown
Member

Resolved / Related Issues

Steps used to test these changes

  1. Open regular folders and folders containing many files; verify that files and folders are enumerated correctly and scrolling remains responsive.
  2. Toggle hidden files, system files, and dotfiles; refresh and navigate between folders to confirm the filters are applied.
  3. Enable folder sizes and verify that folder size calculation completes and updates the displayed values.
  4. Run regular and recursive searches, including folders containing shortcuts; cancel a search or navigate away while it is running.
  5. Open the breadcrumb chevron flyout in a folder with mixed numeric names; verify that subfolders are naturally sorted and icons load.
  6. Test an access-denied folder and, if available, a network share or WSL path; verify that failed enumeration is handled without a crash.
  7. Repeatedly refresh folders and close a tab during enumeration or search; verify that the app remains stable.

Copilot AI lite review requested due to automatic review settings September 9, 2026 05:01
Signed-off-by: Tera <62196528+0x5bfa@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e6f11a71f7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

short subsNameLength = MemoryMarshal.Read<short>(buffer.AsSpan(10));
short printNameOffset = MemoryMarshal.Read<short>(buffer.AsSpan(12));
short printNameLength = MemoryMarshal.Read<short>(buffer.AsSpan(14));
string pathBuffer = Encoding.Unicode.GetString(buffer, 16, (int)bytesReturned - 16);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Decode the reparse path after the Flags field

For every symbolic link, SYMBOLIC_LINK_REPARSE_BUFFER.PathBuffer begins at byte 20 because the four-byte Flags field occupies bytes 16–19, while the name offsets are relative to PathBuffer. Decoding from byte 16 therefore prepends the flags to the substitute name and starts the print name two UTF-16 characters early; the resulting target commonly contains NUL characters and can make Path.GetFullPath throw or otherwise prevent navigation to the link target. Decode from byte 20 (and adjust the returned-length calculation), or retain the prior two-character offset compensation.

AGENTS.md reference: AGENTS.md:L12-L12

Useful? React with 👍 / 👎.

Comment on lines +77 to 78
FSCTL_GET_REPARSE_POINT
IO_REPARSE_TAG_SYMLINK

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore CRLF endings in the changed text files

All seven text files modified by this commit contain only LF endings, including these newly added declarations, despite the repository requirement that changed text files use CRLF. Convert the affected files to CRLF before merging so subsequent Windows-side edits do not produce line-ending churn.

AGENTS.md reference: AGENTS.md:L6-L7

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The updated Win32 helper logic introduces correctness and stability regressions (notably symlink reparse buffer parsing and uncaught exceptions in string reads) that can break core navigation/enumeration flows.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR replaces several storage-related Win32 helper P/Invokes with CsWin32-provided Windows.Win32.PInvoke calls in the Files.App WinUI codebase, aiming to simplify interop usage and improve maintainability while keeping existing storage behaviors.

Changes:

  • Switched folder-size and other filesystem enumeration code to use CsWin32 FindFirstFileEx/FindNextFile with FindCloseSafeHandle.
  • Updated storage helpers and Win32 helper methods to use CsWin32 APIs for attributes, file handles, and reparse point/ADS handling.
  • Extended CsWin32 generation inputs to include FSCTL_GET_REPARSE_POINT.
File summaries
File Description
src/Files.App/ViewModels/Properties/Items/FolderProperties.cs Updates comment references while keeping folder-size behavior aligned with the new Win32 API usage.
src/Files.App/ViewModels/Properties/Items/BaseProperties.cs Migrates folder-size enumeration to CsWin32 FindFirstFileEx/FindNextFile and safe handle disposal.
src/Files.App/Utils/Storage/Helpers/StorageHelpers.cs Replaces attribute probing with CsWin32 GetFileAttributes and updates directory checks.
src/Files.App/Helpers/Win32/Win32Helper.Storage.cs Replaces multiple storage helper interop calls with CsWin32 equivalents (CreateFile, attributes, streams, reparse points).
src/Files.App/Helpers/Navigation/NavigationHelpers.cs Updates symlink tag comparison to use CsWin32 constant.
src/Files.App.CsWin32/NativeMethods.txt Adds FSCTL_GET_REPARSE_POINT to CsWin32 generation list.
Review details
  • Files reviewed: 6/6 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 798 to 802
public static SafeFileHandle OpenFileForRead(string? filePath, bool readWrite = false, uint flags = 0)
{
return new SafeFileHandle(Win32PInvoke.CreateFileFromApp(filePath,
(uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | (uint)(readWrite ? FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE : 0u), (uint)(Win32PInvoke.FILE_SHARE_READ | (readWrite ? 0 : Win32PInvoke.FILE_SHARE_WRITE)), IntPtr.Zero, Win32PInvoke.OPEN_EXISTING, (uint)Win32PInvoke.File_Attributes.BackupSemantics | flags, IntPtr.Zero), true);
return PInvoke.CreateFile(filePath!,
(uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ | (uint)(readWrite ? FILE_ACCESS_RIGHTS.FILE_GENERIC_WRITE : 0u), FILE_SHARE_MODE.FILE_SHARE_READ | (readWrite ? 0 : FILE_SHARE_MODE.FILE_SHARE_WRITE), null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_FLAG_BACKUP_SEMANTICS | (FILE_FLAGS_AND_ATTRIBUTES)flags, null);
}
Comment on lines 897 to +905
public static string? ReadStringFromFile(string filePath)
{
IntPtr hFile = Win32PInvoke.CreateFileFromApp(filePath,
(uint)FILE_ACCESS_RIGHTS.FILE_GENERIC_READ,
Win32PInvoke.FILE_SHARE_READ,
IntPtr.Zero,
Win32PInvoke.OPEN_EXISTING,
(uint)Win32PInvoke.File_Attributes.BackupSemantics,
IntPtr.Zero);

if (hFile.ToInt64() == -1)
{
using SafeFileHandle hFile = OpenFileForRead(filePath);
if (hFile.IsInvalid)
return null;
}

const int BUFFER_LENGTH = 4096;
byte[] buffer = new byte[BUFFER_LENGTH];
int dwBytesRead;
string szRead = string.Empty;

unsafe
{
using (MemoryStream ms = new MemoryStream())
using (StreamReader reader = new StreamReader(ms, true))
{
while (true)
{
fixed (byte* pBuffer = buffer)
{
if (Win32PInvoke.ReadFile(hFile, pBuffer, BUFFER_LENGTH - 1, &dwBytesRead, IntPtr.Zero) && dwBytesRead > 0)
{
ms.Write(buffer, 0, dwBytesRead);
}
else
{
break;
}
}
}
ms.Position = 0;
szRead = reader.ReadToEnd();
}
}

Win32PInvoke.CloseHandle(hFile);

return szRead;
using FileStream stream = new(hFile, FileAccess.Read);
using StreamReader reader = new(stream, detectEncodingFromByteOrderMarks: true);
return reader.ReadToEnd();
Comment on lines +981 to +988
uint reparseTag = MemoryMarshal.Read<uint>(buffer);
short subsNameOffset = MemoryMarshal.Read<short>(buffer.AsSpan(8));
short subsNameLength = MemoryMarshal.Read<short>(buffer.AsSpan(10));
short printNameOffset = MemoryMarshal.Read<short>(buffer.AsSpan(12));
short printNameLength = MemoryMarshal.Read<short>(buffer.AsSpan(14));
string pathBuffer = Encoding.Unicode.GetString(buffer, 16, (int)bytesReturned - 16);
var subsString = pathBuffer.Substring(subsNameOffset / 2, subsNameLength / 2);
var printString = pathBuffer.Substring(printNameOffset / 2, printNameLength / 2);
@yair100
yair100 self-requested a review September 9, 2026 14:12
@yair100 yair100 added the ready for review Pull requests that are ready for review label Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Pull requests that are ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants