Проклятие текущего каталога

Spread the love

Это перевод The curse of the current directory. Автор: Реймонд Чен.

Текущий каталог — это и благо и проклятие. Это благо, потому что экономит вам набор текста (путём использования относительных путей). Это проклятие по всем остальным причинам.

Корнем многих зол является то, что в семейство операционных систем Windows NT держит открытым описатель текущего каталога каждого процесса (упреждающий комментарий от Yuhong Bao: семейство систем Windows 95, напротив, не держало описатель открытым, что порождало кучу других проблем, что, впрочем, никак не связано с темой этого поста).

Первое следствие: вы не можете удалить каталог, если этот каталог является текущим для какого-то запущенного процесса. Я неоднократно видел, как это вгоняет людей в ступор:

Я пытаюсь удалить каталог X, но при попытке его удалить, я получаю сообщение об ошибке: «Процесс не может получить доступ к файлу, поскольку он открыт в другой программе»… После долгих часов, я нашёл виновника: оказывается, каталог X держится программой someapp.exe. Какого чёрта ей вообще надо в моём каталоге? Как мне удалить каталог?

Конкретное приложение someapp.exe не имеет значения, оно каждый раз разное. Когда такое происходит, пользователи винят someapp.exe.

Но почти во всех случаях, someapp.exe — просто невинная жертва текущего каталога.

Во-первых, рассмотрим отдельно случай, когда someapp.exe — это explorer.exe. Почему текущий каталог Проводника может быть равен этому каталогу?

Ну, быть может потому, что это ещё одно проклятие текущего каталога, а именно: текущий каталог — один на весь процесс, это глобальная настройка. Если расширение Оболочки решило вызвать SetCurrentDirectory, то этим она поменяла текущий каталог для всего Проводника. И если это расширение не побеспокоилось о том, чтобы вызвать SetCurrentDirectory второй раз для возврата предыдущего текущего каталога, то теперь текущий каталог застрял на этом каталоге.

Заметьте, что даже если расширение оболочки попытается сделать всё правильно, то это может не сработать:

GetCurrentDirectory(Old) // возвращает C:Previous
SetCurrentDirectory(New) // меняет на C:Victim
.. что-то делаем ..
SetCurrentDirectory(Old) // меняем на C:Previous - неудачно?!

Второй вызов SetCurrentDirectory может завершиться неудачей, если, к примеру, каталог C:Previous был удалён или переименован, пока расширение оболочки делало свою работу. Расширение Оболочки не может вернуть текущий каталог, так что оно оставляет C:Victim текущим каталогом, и теперь вы не можете удалить C:Victim, потому что он стал текущим каталогом Проводника.

(Кстати, совсем правильное решение — это вообще не вызывать SetCurrentDirectory, а оперировать абсолютными полными путями. Поскольку текущий каталог — это глобальная настройка процесса, вы не можете гарантировать, что какой-то другой поток не вызовет SetCurrentDirectory в то время, когда вы работаете с относительными именами файлов).

Заметьте, что если сделать текущий каталог настройкой потока (перевод поста), а не процесса, то это также не решит проблему (полностью), потому что текущий каталог потока (если бы такая вещь существовала бы) всё равно держал бы открытым описатель текущего каталога. Но если бы текущий каталог был бы настройкой потока, то, по крайней мере, если бы этот поток был ассоциирован с окном, то вы могли бы закрыть окно и, таким образом, завершить поток — что, в свою очередь, освободило бы открытый каталог. Ну если только вы не вызвали Terminate­Thread — в последнем случае описатель текущего каталога просто утёк бы, и ваша «попытка» отпустить каталог только что прогарантировала, что этого никогда не случится (заметка для технологических ипохондриков: весь этот параграф был гипотетическим и поэтому совершенно не годится для решения вашей проблемы).

На этом история не закончена, но мне нужно отвлечься, чтобы подготовить поле для следующего этапа проклятия.

Бонус: Привет, люди. «История ещё не закончена». Воздержитесь от спекуляций в комментариях.

Читать на сайте автора.

0 ответы

Ответить

Хотите присоединиться к обсуждению?
Не стесняйтесь вносить свой вклад!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *