使MAF AddInProcess.exe为“长路径感知”

时间:2019-10-25 17:44:30

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

我试图使Windows 10的Addin(使用MAF创建)AddInProcess.exe(由AddInProcess class创建)“长路径感知”。

困难源于我不拥有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的指导):

  • AppContextSwitch.System.IO.UseLegacyPathHandling的{​​{1}}开关设置为Switch.System.IO.BlockLongPaths。这使得 some .NET API开始接受长路径。但是,调用{em> native win32进行的调用仍然因false或其他异常而失败。

  • 我下一步要做的是 ,但不能做的是编辑应用程序的清单并在下面添加设置。但是,我没有一种(正确的)方法可以做到这一点。如果我可以做到这一点,它将将工作。为了证明它可以工作,我做了以下事情:

AddInProcess.exe

虽然上述骇客作品行之有效,但在最终用户的机器上这样做显然是不合理的,而且,它会改变 all <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的合法方法?

是否可以在运行时更改此清单设置?

注释

  • 我在组策略编辑器中启用了Win32长路径支持(请参见.NET 4.6.2 and long paths on Windows 10
  • 我很难让longPathAwareAppContextSwitchOverrides受到UseLegacyPathHandling的尊重。我原来是this problem,并且可以解决它。
  • 可以使用扩展路径(即以BlockLongPaths开头的路径)来获取无法开始工作的方法(因为这会导致路径长度请参阅Path Normalization博客条目。但是,这种方法的工作量更大/风险更大(尝试查找所有当前会抛出并修改路径的代码,并阻止将来的开发人员添加新用法)

其他参考

1 个答案:

答案 0 :(得分:0)

@RbMm 在评论中建议的无记录方法(将PEB的{​​{1}}位设置为 1 )有效(至少Windows 10的版本是:1903)。由于该方法是本机C / C ++方法,因此我只需要将其转换为C#可消耗的东西。

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

注意:

  • 此代码不进行版本检查,以查看其是否在支持IsLongPathAwareProcess的Windows 10版本上运行。
  • 有限的错误检查
  • 我只包含了IsLongPathAwareProcess结构的开始部分,因为访问我要修改的位就足够了。
  • 设置PEB位的方法可能比我使用的方法(IsLongPathAwareProcess)更为简洁
  • 包括一个测试,该测试在执行对Marshal.StructureToPtr的调用时有效,而在未调用时失败。
  • 请注意我已 删除 SetIsLongPathAwareProcess的测试代码(以便我可以在目录不存在的情况下多次重新运行测试) li>

测试代码:

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://docs.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://docs.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; } } } 的控制台应用程序中使用此代码,仍然可以通过在{{1}中手动将longPathAware设置为longPathAware来测试其是否正常运行}:

false