Сделать MAF AddInProcess.exe "Long Path Aware" - PullRequest
6 голосов
/ 25 октября 2019

Я пытаюсь сделать свой Addin's (используя MAF ) AddInProcess.exe (созданный AddInProcess классом ) "осведомленным о длинном пути" для Windows 10.

Сложность связана с тем, что у меня нет файла AddInProcess.exe. Он является частью .NET Framework и живет в C:\Windows\Microsoft.NET\Framework64\v4.0.30319\AddInProcess.exe

Вот что я сделал (основываясь на рекомендациях Блог Джереми Куна запись .NET 4.6.2 и длинные пути в Windows 10 ):

  • Установите переключатели AppContext Switch.System.IO.UseLegacyPathHandling и Switch.System.IO.BlockLongPaths в false. Это заставило некоторые .NET API начать принимать длинные пути. Тем не менее, вызовы, которые из-под колпака звонили в native win32, по-прежнему не выполнялись с System.IO.DirectoryNotFoundException или другими исключениями.

  • Следующее, что я должен сделать, но не могу, это отредактировать манифест приложения и добавить настройки ниже. Однако у меня нет (правильного) способа сделать это. Если бы я мог сделать это, он работал бы . Чтобы доказать, что это будет работать, я сделал следующее:

<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?

Есть ли способ изменить этот параметр манифеста во время выполнения?

Примечания :

  • Я включил поддержку длинных путей Win32 в редакторе групповой политики (см. .NET 4.6.2 и длинные пути в Windows 10). )
  • У меня были некоторые трудности с получением AppContextSwitchOverrides для UseLegacyPathHandling и BlockLongPaths, которые действительно соблюдаются. У меня оказалась эта проблема , и я смог ее обойти.
  • I может получить методы, которые не работают, чтобы начать работать с помощью расширенные пути (т. е. пути, начинающиеся с \\?\ (поскольку это приводит к пропуску проверок длины пути). См. Нормализация пути запись в блоге. Однако такой подход был бы гораздо более масштабным / рискованным(пытается найти все текущие вхождения кода, которые генерируют и изменяют путь и мешают будущим разработчикам добавлять новые использования)

Дополнительные ссылки :

1 Ответ

0 голосов
/ 06 ноября 2019

Недокументированный подход, предложенный @ RbMm в комментариях (установка PEB s IsLongPathAwareProcess бита на 1 ), сработал (по крайней мере, в версии Windows 10)Я был на: 1903). Так как этот подход является родным подходом C / C ++, мне просто нужно было перевести его в нечто, потребляемое C #.

Существует несколько подходов, которые можно использовать (C ++ / CLI, pinvoke). Так как я просто делал «проверку концепции», я решил просто использовать pinvoke и немного ленив в качестве кода.

Примечания:

  • Этот код не проверяет версию, чтобы убедиться, что он работает в версии Windows 10, которая поддерживает IsLongPathAwareProcess.
  • Ограниченная проверка ошибок
  • Я включил только начальную часть структуры 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://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;
            }
        }
    }

Если вы используете этот код в консольном приложении, которое, естественно, longPathAware, вы все равно можете проверить, что оно работает правильно, вручную установив longPathAware в false в app.manifest:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">false</longPathAware>
  </windowsSettings>
</application>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...