Папка Windows и другие системные пути в VB.NET

РазделVB.NET и WinFormsУровеньНовичок Чтение6 минОпубликовано ТегиVB.NETФайловая системаWindows

Путь C:\Windows нельзя зашивать в код: система может стоять на другом диске или в папке WINNT. В VB.NET правильный путь получают одной строкой, но вариантов несколько, и у каждого свои особенности.

Главное. Путь к папке Windows берите у системы, а не из константы: Environment.GetFolderPath(SpecialFolder.Windows) в .NET 4 и новее, SystemRoot или GetSystemWindowsDirectory в старых версиях. Помните о перенаправлении WOW64 для 32-битных сборок.

Почему нельзя писать C:\Windows

В старом коде часто встречается строка вроде «C:\Windows\System32\…». На машине разработчика она работает, а у клиента падает: Windows 2000 по умолчанию ставилась в папку WINNT, администраторы нередко ставят систему на диск D, а на серверах встречаются совсем нестандартные пути.

Правильный подход: спросить у системы, где она находится. В .NET для этого есть несколько способов, от переменной окружения до специальных перечислений. Разберём их по порядку и посмотрим, какой выбрать в зависимости от версии Framework.

Переменная окружения SystemRoot

Самый короткий способ: прочитать переменную окружения SystemRoot. Её устанавливает сама Windows, и она есть во всех версиях начиная с NT. Есть и переменная windir с тем же значением, она осталась со времён Windows 9x.

Минус подхода в том, что переменную окружения теоретически можно переопределить в процессе или в сеансе пользователя. Для служебных утилит это не проблема, но для кода, от которого зависит безопасность, лучше брать путь из API.

WindowsFolder.vbVB.NET
Module WindowsFolder
    Sub Main()
        ' Папка Windows из переменной окружения
        Dim winDir As String = Environment.GetEnvironmentVariable("SystemRoot")
        Console.WriteLine("Windows: " & winDir)

        ' Папка System32 (для 32-битного процесса на x64 см. ниже)
        Console.WriteLine("System: " & Environment.SystemDirectory)

        ' Корень системного диска, например C:\
        Console.WriteLine("Диск: " & IO.Path.GetPathRoot(winDir))
    End Sub
End Module

Environment.GetFolderPath и особые папки

Начиная с .NET Framework 4 в перечислении Environment.SpecialFolder появилось значение Windows, и путь можно получить без переменных окружения: Environment.GetFolderPath(Environment.SpecialFolder.Windows). Внутри метод обращается к функции SHGetFolderPath, то есть получает путь из той же системной службы, что и Проводник.

В .NET 2.0 и 3.5 такого значения нет. Если проект ещё не переведён на четвёртую версию, остаётся SystemRoot или вызов API GetWindowsDirectory. Свойство Environment.SystemDirectory существует во всех версиях и указывает на System32.

  • SpecialFolder.ProgramFiles: папка установки программ;
  • SpecialFolder.ApplicationData и LocalApplicationData: данные пользователя, роуминговые и локальные;
  • SpecialFolder.CommonApplicationData: общие данные всех пользователей, обычно C:\ProgramData;
  • SpecialFolder.MyDocuments и Desktop: документы и рабочий стол текущего пользователя.

Вызов GetWindowsDirectory через API

Если нужна абсолютная надёжность в .NET 2.0, можно вызвать функцию kernel32 напрямую. Она возвращает путь к общей папке Windows даже на терминальном сервере, где у каждого пользователя может быть свой каталог Windows. Для этого случая есть ещё функция GetSystemWindowsDirectory, она всегда отдаёт системный путь.

NativeFolders.vbVB.NET
Imports System.Runtime.InteropServices
Imports System.Text

Public Module NativeFolders
    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>
    Private Function GetSystemWindowsDirectory(buffer As StringBuilder,
                                               size As Integer) As Integer
    End Function

    ' Путь к папке Windows без переменных окружения
    Public Function GetWindowsFolder() As String
        Dim sb As New StringBuilder(260)
        Dim n = GetSystemWindowsDirectory(sb, sb.Capacity)
        If n = 0 Then Throw New ComponentModel.Win32Exception()
        Return sb.ToString()
    End Function
End Module

Ловушка 64-битной Windows

Если программа скомпилирована под x86 и запущена на 64-битной системе, работает механизм перенаправления WOW64. Обращение к папке System32 молча уходит в SysWOW64, где лежат 32-битные библиотеки. Environment.SystemDirectory при этом всё равно вернёт строку с System32, но реальные файлы будут другими.

Чаще всего это всплывает, когда утилита ищет системный exe или драйвер и «не видит» его. Для доступа к настоящей 64-битной System32 из 32-битного процесса используют виртуальную папку Sysnative: путь SystemRoot плюс «Sysnative». Она видна только 32-битным процессам, поэтому проверяйте Environment.Is64BitProcess перед её использованием.

Самое простое решение: собирать утилиту под Any CPU без флага Prefer 32-bit. Тогда на x64 она запустится 64-битной, и перенаправления не будет вовсе.

Не пишите в папку Windows

Узнать путь к папке Windows полезно для чтения: найти шрифт, системную утилиту, проверить наличие компонента. Записывать туда свои файлы не стоит. Обычный пользователь не имеет на это прав, а программы, которые раньше клали INI-файлы в C:\Windows, на новых системах получают виртуализацию и путаницу с настройками.

Собственные данные приложения храните в ApplicationData или CommonApplicationData, а пути собирайте через Path.Combine, а не склейкой строк: так не придётся думать, есть ли на конце обратная косая черта.

Как это делают сегодня

В современных .NET все системные каталоги доступны через Environment.GetFolderPath с перечислением SpecialFolder, включая Windows, System и SystemX86. Для папок, которых нет в перечислении (например, «Загрузки»), вызывают SHGetKnownFolderPath с нужным KNOWNFOLDERID. Пути собирают через Path.Combine или Path.Join, а для кроссплатформенного кода проверяют OperatingSystem.IsWindows().

Вопросы и ответы

Чем отличаются переменные SystemRoot и windir?

На современных Windows они содержат один и тот же путь. windir осталась для совместимости с Windows 9x, SystemRoot появилась в линейке NT. Для нового кода лучше вообще использовать Environment.GetFolderPath.

Почему в 32-битной программе нет файла, который есть в System32?

Сработало перенаправление WOW64: обращение ушло в SysWOW64. Соберите программу под Any CPU или используйте путь через виртуальную папку Sysnative.

Как получить путь к Program Files (x86)?

Используйте Environment.SpecialFolder.ProgramFilesX86 в .NET 4 и новее. На 32-битной системе этот вызов вернёт обычную папку Program Files.

Читайте также