Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ private static ProcessModuleCollection GetModules(int processId, bool firstModul

var modules = new ProcessModuleCollection(firstModuleOnly ? 1 : modulesCount);

char[] chars = ArrayPool<char>.Shared.Rent(1024);
int minimumLength = Interop.Kernel32.MAX_PATH;
char[]? chars = ArrayPool<char>.Shared.Rent(minimumLength);
try
{
for (int i = 0; i < modulesCount; i++)
Expand Down Expand Up @@ -171,14 +172,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<char>.Shared.Return(toReturn);
Comment on lines +187 to +189
Copy link
Member

Choose a reason for hiding this comment

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

what is the purpose of toReturn - can't you do:

                               ArrayPool<char>.Shared.Return(chars);
                                minimumLength = Math.Min(minimumLength * 2, short.MaxValue);
                                chars = ArrayPool<char>.Shared.Rent(minimumLength);

Copy link
Member

Choose a reason for hiding this comment

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

@danmosemsft This would call Return multiple timeswhenRent` throws an exception.

It would be fine to write it this way if the Return is not inside finally block (ie we let GC take care of collecting the buffer when exception is thrown).

Copy link
Member

Choose a reason for hiding this comment

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

ah yes - tricky. might be worth a comment.

chars = ArrayPool<char>.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);

Expand All @@ -187,7 +203,10 @@ private static ProcessModuleCollection GetModules(int processId, bool firstModul
}
finally
{
ArrayPool<char>.Shared.Return(chars);
if (chars != null)
{
ArrayPool<char>.Shared.Return(chars);
}
}

return modules;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

Expand All @@ -28,6 +30,61 @@ public void TestModuleProperties()
}
}

[ConditionalFact(nameof(IsProcessElevated))]
[PlatformSpecific(TestPlatforms.Windows)]
public void TestModuleLongPath()
{
// // Metadata version: v4.0.30319
// .assembly Test
// {
// .ver 0:0:0:0
// }
// .module Test.dll
// // MVID: {48E60D10-353B-45DC-AA09-3A1E6B0FD382}
// .imagebase 0x00400000
// .file alignment 0x00000200
// .stackreserve 0x00100000
// .subsystem 0x0003 // WINDOWS_CUI
// .corflags 0x00000001 // ILONLY
byte[] assemblyImage = Convert.FromBase64String(
Copy link
Member

Choose a reason for hiding this comment

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

@steveharter @GrabYourPitchforks (as you are the owners of System.Reflection.Emit) I remember that Full .NET Framework offered a way to dynamically generate, save to disk, and load from disk a managed assembly. Is this still supported?

We need to test the long process module paths and our current best idea is to dynamically load such an assembly to the test runner process.

Copy link
Member

@stephentoub stephentoub Jan 4, 2021

Choose a reason for hiding this comment

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

dynamically generate, save to disk [...] Is this still supported?

No.
#15704

Copy link
Member

Choose a reason for hiding this comment

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

@stephentoub do you know if we have any existing test that we could just extend? Like something that validates that a .NET executable with path > MAX_PATH can just run for example?

I was thinking about creating a new standalone .exe with just a very long name and asserting the module inside. Something like the standalone coreclr regression tests: https://github.com/dotnet/runtime/tree/master/src/tests/Regressions/coreclr

My other idea is to... add a new project with a very long output dll name to the System.Diagnostics.sln, reference it from this tests project, and load it dynamically.

I am not sure if this would work if we run our test on machines where long path support is disabled on purpose. In such a case we would have to give it a short name, and after detecting that it's fine (conditional fact), rename the file and then load it. Any thoughts on that?

Copy link
Member

Choose a reason for hiding this comment

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

Most or all of our developers have long paths enabled (since it is/was documented as a requirement to build the repo) so I think if the test simply checks for it being enabled that makes sense to me and it would still run fairly often.

Copy link
Member

Choose a reason for hiding this comment

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

I've sent a proposal #46685

"TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFt" +
"IGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAABQRQAATAECAJyj7l8AAAAAAAAAAOAAAiELAQsAAAIAAAACAAAAAAAAfiEAAAAgAAAAQAAA" +
"AABAAAAgAAAAAgAABAAAAAAAAAAEAAAAAAAAAABgAAAAAgAAAAAAAAMAQIUAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAADAhAABLAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAEAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAACAAAAAAAAAAAAAAA" +
"CCAAAEgAAAAAAAAAAAAAAC50ZXh0AAAAhAEAAAAgAAAAAgAAAAIAAAAAAAAAAAAAAAAAACAAAGAucmVsb2MAAAwAAAAAQAAAAAIAAAAEAAAAAAAAAAAAAAAA" +
"AABAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgIQAAAAAAAEgAAAACAAUAUCAAAOAAAAABAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJTSkIBAAEAAAAAAAwAAAB2NC4wLjMwMzE5AAAAAAQAXAAAAFQA" +
"AAAjfgAAsAAAABgAAAAjU3RyaW5ncwAAAADIAAAACAAAACNVUwDQAAAAEAAAACNHVUlEAAAAAAAAAAIAAAEFAAAAAQAAAAD6JTMAFgAAAQAAAAEAAAABAAAA" +
"AAAKAAEAAAAAAAAAAAABAAAAAAABAAEAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAAAAADxNb2R1bGU+AFRlc3QuZGxsAFRlc3QAAAMgAAAAAAAQDeZIOzXcRaoJ" +
"Oh5rD9OCWCEAAAAAAAAAAAAAbiEAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAhAAAAAAAAAABfQ29yRGxsTWFpbgBtc2NvcmVlLmRsbAAAAAAA/yUAIEAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAMAAAAgDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAA"
);

string libraryDirectory = GetTestFilePath();
Directory.CreateDirectory(libraryDirectory);
string libraryPath = Path.Combine(libraryDirectory, new string('_', 250) + ".dll");
Assert.True(libraryPath.Length > 260);
File.WriteAllBytes(libraryPath, assemblyImage);

IntPtr library = NativeLibrary.Load(libraryPath);
Assert.True(library != IntPtr.Zero);
try
{
Assert.Contains(Process.GetCurrentProcess().Modules.Cast<ProcessModule>(), module => module.FileName == libraryPath);
}
finally
{
NativeLibrary.Free(library);
}
}

[Fact]
public void Modules_Get_ContainsHostFileName()
{
Expand Down