Отгадка.
В комментариях к загадке ответ был дан почти сразу. Действительно, обработчик отложенной загрузки DLL попытается загрузить «version.dll» по имени, а не по полному пути. В результате загрузчик будет искать библиотеку, пользуясь правилами по умолчанию. Номер два в этом списке поиск DLL в system32, что при отключенном перенаправлении файловой системы, равноценно попытке загрузить 64-х битную DLL в 32-х битный процесс. Бум.
Но проблема на самом деле не так уж и проста, как может показаться с самого начала. Первая сложность здесь в том, что загрузка DLL происходит неявно для программиста. Из кода никак не понять, что библиотека выбрана для отложенной загрузки. В достаточном большом проекте это означает, что «любой залетный программист» из соседней группы может запретить перенаправление, исправляя какой-то свой баг. Причем какое-то время всё может работать, так как нужная библиотека могла загрузиться ранее. А потом, по закону подлости, это вылезет во время презентации продукта заказчику.
Причем это не единственный способ скрытой загрузки DLL. Таких способов масса: создание COM объекта, вызов CreateProcess или ShellExecute, установка хука на оконные сообщения и т.д. и т.п.
Вторая проблема заключается в том, что реализовать работающее/альтернативное решение либо сложно, либо неочевидно. Показательно, что из двух предложенных решений проблемы из предыдущего топика только одно решало проблему полностью, а второе – только маскировало проблему. С другой стороны, даже правильное решение не работает однообразно для 32-х разрядного и 64-х разрядного кода. 32-х битное приложение должно использовать «sysnative» для доступа к настоящему каталогу «system32». 64-х разрядные приложения ничего не знают о «sysnative» и подобное перенаправление для них не работает. Да, я знаю, что Microsoft – зло. В данном конкретном случае я полностью согласен.
Ещё один пример, на форуме разработчиков Far Manager обсуждался плагин, который должен был запрещать перенаправление файловой системы раз и навсегда при старте приложения. Именно с целью попасть в настоящий «system32». Не знаю, чем это закончилась, но помню аргументацию в стиле «попробовал – работает». Действительно работает, пока не сломается.
Третья сложность заключается в неочевидных взаимоотношениях LoadLibrary и перенаправления файловой системы. Если запустить 32-х разрядную версию fileverison.exe, чтобы получить версию ядра (файл «%windir%\system32\ntoskrnl.exe»), то случится странное. Не смотря на перенаправление файловой системы, 32-х разрядный процесс успешно загрузит ресурсы из 64-х битного «ntoskrnl.exe». Хотя никакого «ntoskrnl.exe» в «%windir%\syswow64», куда перенаправляется процесс, нет.
Как так получается? Очень просто. GetFileVersionInfo вызывает LoadLibraryEx с флагом LOAD_LIBRARY_AS_IMAGE_RESOURCE. Этот флаг указывает, что файл загружается только для извлечения ресурсов. Если указан этот флаг и первая попытка найти файл с заданным именем окончилась неудачей, LoadLibraryEx попробует отключить перенаправление файловой системы и повторит попытку. В приеме выше, 64-х битный «ntoskrnl.exe» будет загружен во время второй попытки. Добавлю, что этот механизм работает только для модулей «system32» и своему появлению он обязан проблемам с совместимостью к каким-то из приложений.
Вот такие вот ужасы.
Нда, жестокая весчь. Совмещать 64 битную операционку с 32 битным унаследованым кодом.
Интересно а ктонить считал что больше + или – бизнес поимел от такой политики?
Это только с точки зрения “правильности” дизайна выглядит ужастно. А с практической точки зрения это очень даже неплохое решение. Портирование приложений (и самой OS) на 64 бита значительно упростилось из-за таких вот вывертов. C’est la vie.
> Не знаю, чем это закончилась, но помню аргументацию в стиле «попробовал – работает».
Закончилось это встраиванием в 32-битную версию far хука, включающего перенаправление только на время запуска процессов и загрузки dll.
Ну работать это будет до первого дятла, т.е. до первой внедренной DLL, которая захочет чего-то найти в system32.