Skip to content

Use Microsoft.IO.Redist in Framework FileUtilities/NativeMethods on net472 (#13078)#13428

Open
sachinsharma3191 wants to merge 6 commits intodotnet:mainfrom
sachinsharma3191:dev/13078-microsoft-io-fileutilities
Open

Use Microsoft.IO.Redist in Framework FileUtilities/NativeMethods on net472 (#13078)#13428
sachinsharma3191 wants to merge 6 commits intodotnet:mainfrom
sachinsharma3191:dev/13078-microsoft-io-fileutilities

Conversation

@sachinsharma3191
Copy link
Copy Markdown

@sachinsharma3191 sachinsharma3191 commented Mar 21, 2026

Summary

Addresses #13078 by replacing custom Win32 GetFullPath / GetCurrentDirectory paths in Microsoft.Build.Framework with Microsoft.IO.Redist when FEATURE_MSIOREDIST is enabled (net472).

Changes

  • Remove FEATURE_LEGACY_GETCURRENTDIRECTORY and FEATURE_LEGACY_GETFULLPATH from Directory.BeforeCommon.targets for all net4* targets.
  • FileUtilities.GetFullPath: use NewPath.GetFullPath when FEATURE_MSIOREDIST; otherwise System.IO.Path.GetFullPath.
  • NativeMethods.GetCurrentDirectory: use Microsoft.IO.Directory.GetCurrentDirectory() when FEATURE_MSIOREDIST; remove unused kernel32 imports and Win32 helpers removed with the legacy path code.
  • Add Microsoft.IO.Redist package reference to Microsoft.Build.Framework.csproj (conditional on FeatureMSIORedist, consistent with other projects).
  • Tests: FileUtilities_Tests expectation aligned with non-legacy behavior; Expander path-too-long cases gated with #if NETFRAMEWORK.

Notes

  • MSBuildTaskHost (net35) keeps its own utilities and is unchanged.
  • Validation: Microsoft.Build.Framework builds for net10.0 / netstandard2.0 locally; full net472 + tests should be run on Windows per repo guidance.

Fixes #13078

@sachinsharma3191
Copy link
Copy Markdown
Author

@dotnet-policy-service agree

sachinsharma3191 added a commit to sachinsharma3191/msbuild that referenced this pull request Mar 22, 2026
sachinsharma3191 added a commit to sachinsharma3191/msbuild that referenced this pull request Mar 22, 2026
@sachinsharma3191 sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch 2 times, most recently from b5b1ffe to 72bfc27 Compare March 22, 2026 01:38
…3078)

Add Microsoft.IO.Redist to Microsoft.Build.Framework for net4* targets. FileUtilities.GetFullPath and NativeMethods.GetCurrentDirectory use Microsoft.IO.Path and Microsoft.IO.Directory when FEATURE_MSIOREDIST is set.

Legacy Win32 GetCurrentDirectory/GetFullPathName are not enabled by default: the FEATURE_LEGACY_* compile constants are preserved only as comments in Directory.BeforeCommon.targets, while the DllImports and helpers remain available behind FEATURE_LEGACY_* preprocessor guards for opt-in builds.

Adjust Framework unit tests with a NETFRAMEWORK gate for path-too-long expander cases.
@sachinsharma3191 sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch from 72bfc27 to 96d5dfb Compare March 22, 2026 01:44
@sachinsharma3191 sachinsharma3191 marked this pull request as ready for review March 23, 2026 03:18
When Path.GetFullPath throws PathTooLongException, rethrow instead of
falling back to Microsoft.IO.Path.GetFullPath. The latter may not throw
for the same path, causing Regress451057_ExitGracefullyIfPathNameIsTooLong
tests to fail (they expect the task to return false, not succeed with a
long path).

Made-with: Cursor
…ths as invalid

- Paths with leading/trailing whitespace: Microsoft.IO.Path.GetFullPath may
  trim them, causing incorrect resolution. Treat as invalid (CanNotResolveHintPathWithSpace).
- Paths >= 260 chars: GetFullPath throws PathTooLongException on legacy
  Windows. Treat as invalid so callers skip NormalizePath and handle
  gracefully (Regress314573_VeryLongPaths).

Made-with: Cursor
@sachinsharma3191 sachinsharma3191 force-pushed the dev/13078-microsoft-io-fileutilities branch from b4db326 to ccc36bd Compare March 23, 2026 05:09
@AR-May AR-May self-assigned this Mar 24, 2026
catch (PathTooLongException)
{
// Trigger the same exception for truly invalid characters even if path is long
if (path.Contains('|'))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason only "|" is handled? I believe there are multiple invalid characters.


// Paths that exceed MAX_PATH (260) will cause GetFullPath to throw PathTooLongException on
// legacy Windows. Treat as invalid so callers skip NormalizePath and handle gracefully (e.g. RAR Regress314573).
if (path.Length >= NativeMethods.MAX_PATH)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PathIsInvalid usage is not limited to normalization. I am not yet sure about marking paths with trailing whitespaces as invalid, but marking all long paths as invalid may cause build failures on systems where long paths are allowed.

<DefineConstants>$(DefineConstants);FEATURE_INSTALLED_MSBUILD</DefineConstants>
<!-- Directory.GetCurrentDirectory The pre .Net 4.6.2 implementation of Directory.GetCurrentDirectory is slow and creates strings in its work. -->
<DefineConstants>$(DefineConstants);FEATURE_LEGACY_GETCURRENTDIRECTORY</DefineConstants>
<!-- <DefineConstants>$(DefineConstants);FEATURE_LEGACY_GETCURRENTDIRECTORY</DefineConstants> -->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: better remove the line alltogether.

This check can only be properly done after normalizing, so
\\foo\.. will be properly rejected. Also, reject \\?\GLOBALROOT\
(an internal kernel path) because it provides aliases for drives.
string redistResult = NewPath.GetFullPath(path);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since exceptions are significantly more expensive than simple branching, relying on the ArgumentException fallback for many paths could noticeably slow down builds. It would be better to avoid using exceptions for control flow if we can.


<Error Condition="!Exists('$(TlbExpPath)')"
Text="TlbExp was not found at '$(TlbExpPath)'. Ensure the .NET Framework SDK tools are installed." />
<Warning Condition="!Exists('$(TlbExpPath)')"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you explain why this needs to be downgraded from an error to a warning? I’m not sure how the change to this file relates to the changes in this PR.


throw new ArgumentException(Environment.GetResourceString("Arg_PathIllegalUNC"));
// Re-validate UNC roots that Redist might accept but MSBuild tests expect to fail.
if (redistResult.StartsWith(@"\\", StringComparison.Ordinal) && (redistResult is @"\\" or @"\\\\" or @"\\localhost" or @"\\XXX\"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems UNC paths handling changed significantly. Can you explain those changes in detail? Are there any behavioral changes compared to the previous version?

#endif
}

#if FEATURE_LEGACY_GETCURRENTDIRECTORY
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I thought that we are removing FEATURE_LEGACY_GETCURRENTDIRECTORY from project files. Do we still have this code path enabled anywhere?

@DustinCampbell
Copy link
Copy Markdown
Member

I intentionally left it for later when moving FileUtilities to Microsoft.Build.Framework, so I'm glad to see this in the works!

Note: There are a lot of tests that will fail because they expect exceptions to be thrown on .NET Framework. Microsoft.IO.Redist doesn't do all of the path validation that was done by System.IO.Path on full framework. Instead, it matches the behavior on modern .NET. So, I would expect there to be significant test updates for those tests that expect thrown exceptions on .NET Framework.

cc @JeremyKuhne (the Microsoft.IO.Redist author/guru) in case he has any thoughts.

@JeremyKuhne
Copy link
Copy Markdown
Member

I would expect there to be significant test updates for those tests that expect thrown exceptions on .NET Framework.

GetFullPath is what normally throws for "bad" paths (often called indirectly when calling other IO APIs). It no longer checks for anything other than embedded nulls. Paths will throw when used by the OS and whatever filesystem is involved (unblocking a number of more complicated setups).

If there are any questions or issues feel free to tag me when they arise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use Microsoft.IO.Redist in FileUtilities on net472 instead of custom methods

4 participants