diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs index 18243a75c56702..0fdb798af5334e 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Win32.cs @@ -131,7 +131,14 @@ private static ProcessModuleCollection GetModules(int processId, bool firstModul var modules = new ProcessModuleCollection(firstModuleOnly ? 1 : modulesCount); - char[] chars = ArrayPool.Shared.Rent(1024); +#if RELEASE + int minimumLength = Interop.Kernel32.MAX_PATH; +#else + // use a smaller value to ensure that at least for DEBUG builds + // we test the code path that rents a bigger array from ArrayPool + int minimumLength = Interop.Kernel32.MAX_PATH / 4; +#endif + char[]? chars = ArrayPool.Shared.Rent(minimumLength); try { for (int i = 0; i < modulesCount; i++) @@ -171,14 +178,29 @@ private static ProcessModuleCollection GetModules(int processId, bool firstModul module.ModuleName = new string(chars, 0, length); - length = Interop.Kernel32.GetModuleFileNameEx(processHandle, moduleHandle, chars, chars.Length); + while (true) + { + length = Interop.Kernel32.GetModuleFileNameEx(processHandle, moduleHandle, chars, chars.Length); + if (length == chars.Length) + { + minimumLength = chars.Length * 2; + char[] toReturn = chars; + chars = null; + ArrayPool.Shared.Return(toReturn); + chars = ArrayPool.Shared.Rent(minimumLength); + continue; + } + + break; + } + if (length == 0) { HandleLastWin32Error(); continue; } - module.FileName = (length >= 4 && chars[0] == '\\' && chars[1] == '\\' && chars[2] == '?' && chars[3] == '\\') ? + module.FileName = chars.AsSpan().StartsWith(@"\\?\") ? new string(chars, 4, length - 4) : new string(chars, 0, length); @@ -187,7 +209,10 @@ private static ProcessModuleCollection GetModules(int processId, bool firstModul } finally { - ArrayPool.Shared.Return(chars); + if (chars != null) + { + ArrayPool.Shared.Return(chars); + } } return modules; diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs index 2631cb8b1a3dcb..b8f7d8f6ae0844 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessModuleTests.cs @@ -92,32 +92,36 @@ public void ModulesAreDisposedWhenProcessIsDisposed() Assert.Equal(expectedCount, disposedCount); } - [ActiveIssue("https://github.com/dotnet/runtime/pull/335059")] + [SkipOnMono("Assembly.LoadFile used the way this test is implemented fails on Mono")] [ConditionalFact(typeof(PathFeatures), nameof(PathFeatures.AreAllLongPathsAvailable))] [PlatformSpecific(TestPlatforms.Windows)] public void LongModuleFileNamesAreSupported() { - // To be able to test Long Path support for ProcessModule.FileName we need a .dll that has a path >= 260 chars. + // To be able to test Long Path support for ProcessModule.FileName we need a .dll that has a path > 260 chars. // Since Long Paths support can be disabled (see the ConditionalFact attribute usage above), // we just copy "LongName.dll" from bin to a temp directory with a long name and load it from there. // Loading from new path is possible because the type exposed by the assembly is not referenced in any explicit way. const string libraryName = "LongPath.dll"; + const int minPathLength = 261; string testBinPath = Path.GetDirectoryName(typeof(ProcessModuleTests).Assembly.Location); string libraryToCopy = Path.Combine(testBinPath, libraryName); Assert.True(File.Exists(libraryToCopy), $"{libraryName} was not present in bin folder '{testBinPath}'"); - string directoryWithLongName = Path.Combine(TestDirectory, new string('a', Math.Max(1, 261 - TestDirectory.Length))); + string directoryWithLongName = Path.Combine(TestDirectory, new string('a', Math.Max(1, minPathLength - TestDirectory.Length))); Directory.CreateDirectory(directoryWithLongName); string longNamePath = Path.Combine(directoryWithLongName, libraryName); - Assert.True(longNamePath.Length > 260); + Assert.True(longNamePath.Length > minPathLength); File.Copy(libraryToCopy, longNamePath); + Assert.True(File.Exists(longNamePath)); Assembly loaded = Assembly.LoadFile(longNamePath); + Assert.Equal(longNamePath, loaded.Location); - Assert.Contains(Process.GetCurrentProcess().Modules.Cast(), module => module.FileName == longNamePath); + ProcessModule[] longPathModules = Process.GetCurrentProcess().Modules.Cast().Where(module => module.FileName.Contains(libraryName)).ToArray(); + Assert.Contains(longPathModules, module => module.FileName == longNamePath); } } }