c# - 使MAF AddInProcess.exe "Long Path Aware"

标签 c# .net windows manifest system.io.file

我正在尝试使我的插件(使用 MAF )AddInProcess.exe(由 AddInProcess class 创建)在 Windows 10 上“长路径感知”。

困难源于我不拥有 AddInProcess.exe。它是 .NET Framework 的一部分,位于 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\AddInProcess.exe

这是我所做的(基于遵循 Jeremy Kuhne 的博客条目 .NET 4.6.2 and long paths on Windows 10 的指导):

  • 设置AppContext开关Switch.System.IO.UseLegacyPathHandlingSwitch.System.IO.BlockLongPathsfalse 。这使得一些 .NET API 开始接受长路径。然而,在后台调用 native win32 的调用仍然失败,并显示 System.IO.DirectoryNotFoundException或其他异常情况。

  • 应该要做但不能做的下一件事是编辑应用程序的 list 并添加以下设置。但是,我没有(正确的)方法来做到这一点。如果我做到这一点,它就会起作用。为了证明它可以工作,我做了以下事情:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
  </windowsSettings>
</application>

虽然上述黑客行为有效,但在最终用户的计算机上执行此操作显然不合理,而且,它会改变所有 AddInProcess.exe 的行为,不仅仅是我创建的。

问题:是否有合法的方法可以使您无法控制的进程 ( AddInProcess.exe ) 成为 longPathAware

有没有办法在运行时更改此 list 设置?

注释:

  • 我在组策略编辑器中启用了 Win32 长路径支持(请参阅 .NET 4.6.2 and long paths on Windows 10)
  • 我在获取 AppContextSwitchOverrides 时遇到了一些困难对于 UseLegacyPathHandlingBlockLongPaths才能真正得到尊重。我原来是this problem我能够解决这个问题。
  • 可以通过使用扩展路径(即以 \\?\ 开头的路径)来开始工作(因为这会导致路径长度检查被跳过)。请参阅 Path Normalization 博客文章。但是,这种方法将是一个更大/风险更高的工作(尝试查找所有当前出现的代码,这些代码会抛出并修改路径并阻止 future 的开发人员添加新用法)

其他引用:

最佳答案

@RbMm 在评论中建议的未记录方法(将 PEBIsLongPathAwareProcess 位设置为 1)有效(至少在我使用的 Windows 10:1903)。由于该方法是 native C/C++ 方法,因此我只需将其转换为 C# 可以使用的东西。

可以使用多种方法(C++/CLI、pinvoke)。由于我只是在做“概念验证”,所以我决定只使用 pinvoke,并且在代码质量方面有点懒。

注释:

  • 此代码不会进行版本检查来确定它是否在支持 IsLongPathAwareProcess 的 Windows 10 版本上运行。 .
  • 有限的错误检查
  • 我只包含 PEB 的开头部分结构,因为这就是访问我想要修改的位所需的全部内容。
  • 很可能有一种更简洁的方法来设置 IsLongPathAwareProcess比我使用的方法( Marshal.StructureToPtr )
  • 包括一个在调用 SetIsLongPathAwareProcess 时有效的测试未调用时执行并失败
  • 请注意,我在测试代码中删除 C:\Test (这样我就可以在目录不存在的情况下多次重新运行测试)

测试代码:

    class Program
    {
        static void Main(string[] args)
        {
            SetIsLongPathAwareProcess();

            string reallyLongDirectory = @"C:\Test\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            reallyLongDirectory = reallyLongDirectory + @"\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            reallyLongDirectory = reallyLongDirectory + @"\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            CreateDirTest(reallyLongDirectory);
            Directory.Delete(@"C:\Test", recursive: true);
        }

        private static void CreateDirTest(string name)
        {
            Console.WriteLine($"Creating a directory that is {name.Length} characters long");

            // Test managed CreateDirectory
            Directory.CreateDirectory(name);

            // Test Native CreateDirectory.  Note this method will only create the "last part" of the directory (not the full path).
            // Also note that it fails if the directory already exists.
            // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectorya
            string nativeName = Path.Combine(name, "TestEnding");
            var r = Native.CreateDirectory(nativeName, null);
            if (!r)
            {
                int currentError = Marshal.GetLastWin32Error();
                Debug.Fail($"Native.CreateDirectory failed: {currentError}");
            }
        }


        // Adapted from https://www.pinvoke.net/default.aspx/ntdll.ntqueryinformationprocess
        private static void SetIsLongPathAwareProcess()
        {
            var currentProcess = Process.GetCurrentProcess();
            IntPtr hProc = currentProcess.Handle;

            IntPtr pPbi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Native.PROCESS_BASIC_INFORMATION)));
            IntPtr outLong = Marshal.AllocHGlobal(sizeof(long));

            int status = Native.NtQueryInformationProcess(hProc, 0, pPbi, (uint)Marshal.SizeOf(typeof(Native.PROCESS_BASIC_INFORMATION)), outLong);

            Marshal.FreeHGlobal(outLong);

            //STATUS_SUCCESS = 0
            if (status == 0)
            {
                var pbi = Marshal.PtrToStructure<Native.PROCESS_BASIC_INFORMATION>(pPbi);
                var pPeb = pbi.PebBaseAddress;

                var peb = Marshal.PtrToStructure<Native.PEB_Beginning>(pPeb);

                var bitField1 = peb.BitField1;
                peb.BitField1 = bitField1 | Native.PEBBitField1.IsLongPathAwareProcess;
                Marshal.StructureToPtr<Native.PEB_Beginning>(peb, pPeb, false);
            }

            //Free allocated space
            Marshal.FreeHGlobal(pPbi);
        }

        static class Native
        {
            // http://pinvoke.net/default.aspx/Structures/PROCESS_BASIC_INFORMATION.html
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            internal struct PROCESS_BASIC_INFORMATION
            {
                public IntPtr ExitStatus;
                public IntPtr PebBaseAddress;
                public IntPtr AffinityMask;
                public IntPtr BasePriority;
                public UIntPtr UniqueProcessId;
                public IntPtr InheritedFromUniqueProcessId;

                public int Size
                {
                    get { return (int)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
                }
            }

            [Flags]
            internal enum PEBBitField1 : byte
            {
                None = 0,
                ImageUsesLargePages = 1 << 0,
                IsProtectedProcess = 1 << 1,
                IsImageDynamicallyRelocated = 1 << 2,
                SkipPatchingUser32Forwarders = 1 << 3,
                IsPackagedProcess = 1 << 4,
                IsAppContainer = 1 << 5,
                IsProtectedProcessLight = 1 << 6,
                IsLongPathAwareProcess = 1 << 7
            }

            // Note: this only contains the "beginning" of the PEB structure
            // but that is all we need for access to the IsLongPathAwareProcess bit
            // Used as a guide: https://github.com/processhacker/processhacker/blob/master/phnt/include/ntpebteb.h#L75
            // Related: https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb
            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            internal struct PEB_Beginning
            {
                Byte InheritedAddressSpace;
                Byte ReadImageFileExecOptions;
                Byte BeingDebugged;
                public PEBBitField1 BitField1;
            };

            // https://www.pinvoke.net/default.aspx/ntdll.ntqueryinformationprocess
            [DllImport("ntdll.dll", SetLastError = true)]
            internal static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, IntPtr processInformation, uint processInformationLength, IntPtr returnLength);

            // The below is for test purposes only:
            // Include CreateDirectory for testing if the long path aware changes work or not.
            // Requires SECURITY_ATTRIBUTES
            // http://pinvoke.net/default.aspx/kernel32/CreateDirectory.html?diff=y
            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)]
            internal static extern bool CreateDirectory(String path, SECURITY_ATTRIBUTES lpSecurityAttributes);


            // https://www.pinvoke.net/default.aspx/Structures/SECURITY_ATTRIBUTES.html
            [StructLayout(LayoutKind.Sequential)]
            internal class SECURITY_ATTRIBUTES
            {
                internal int nLength = 0;
                // don't remove null, or this field will disappear in bcl.small
                internal unsafe byte* pSecurityDescriptor = null;
                internal int bInheritHandle = 0;
            }
        }
    }

如果您在控制台应用程序中使用此代码,那自然是 longPathAware ,您仍然可以通过手动设置 longPathAware 来测试它是否正常工作。至falseapp.manifest :

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">false</longPathAware>
  </windowsSettings>
</application>

关于c# - 使MAF AddInProcess.exe "Long Path Aware",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58563335/

相关文章:

mysql - 如何在 Windows 上引导 MySQL?

python - 将TBB与OpenCV和Python(Eclipse)结合使用

c# - 未分配的变量和 "Not all code paths return a value"解决方法?

c# - 数据不显示在 Text={Binding} 上?

c# - 检查数组是否包含 false?

c# - 显式设置 WPF 绑定(bind)数据类型

windows - 删除 node_modules 文件夹

c# - RaisePropertyChanged 在另一个 View 模型中

c# - 在 C# 中使用 UPDATE MySQL 命令

c# - WCF 和传递 Windows 凭据