
Папка Windows и другие системные пути в VB.NET
Путь C:\Windows нельзя зашивать в код: система может стоять на другом диске или в папке WINNT. В VB.NET правильный путь получают одной строкой, но вариантов несколько, и у каждого свои особенности.
Почему нельзя писать C:\Windows
В старом коде часто встречается строка вроде «C:\Windows\System32\…». На машине разработчика она работает, а у клиента падает: Windows 2000 по умолчанию ставилась в папку WINNT, администраторы нередко ставят систему на диск D, а на серверах встречаются совсем нестандартные пути.
Правильный подход: спросить у системы, где она находится. В .NET для этого есть несколько способов, от переменной окружения до специальных перечислений. Разберём их по порядку и посмотрим, какой выбрать в зависимости от версии Framework.
Переменная окружения SystemRoot
Самый короткий способ: прочитать переменную окружения SystemRoot. Её устанавливает сама Windows, и она есть во всех версиях начиная с NT. Есть и переменная windir с тем же значением, она осталась со времён Windows 9x.
Минус подхода в том, что переменную окружения теоретически можно переопределить в процессе или в сеансе пользователя. Для служебных утилит это не проблема, но для кода, от которого зависит безопасность, лучше брать путь из API.
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 ModuleEnvironment.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, она всегда отдаёт системный путь.
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 перед её использованием.
Не пишите в папку Windows
Узнать путь к папке Windows полезно для чтения: найти шрифт, системную утилиту, проверить наличие компонента. Записывать туда свои файлы не стоит. Обычный пользователь не имеет на это прав, а программы, которые раньше клали INI-файлы в C:\Windows, на новых системах получают виртуализацию и путаницу с настройками.
Собственные данные приложения храните в ApplicationData или CommonApplicationData, а пути собирайте через Path.Combine, а не склейкой строк: так не придётся думать, есть ли на конце обратная косая черта.
Как это делают сегодня
Вопросы и ответы
Чем отличаются переменные 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.