AppCompat
Dec 8, 2009 · CommentsсовместимостьWindowsWow64
Словечко «AppCompat», появившееся в моем лексиконе за время работы над Wow64, обозначает множество вещей. Чаще всего - геморрой. Иногда - великий геморрой. Происходит оно от «application compatibility» - т.е. совместимость OS с приложениями, она же - «обратная совместимость».
Почему геморрой? Да потому, что эта совместимость, - она только для пользователей операционной системы полезна. Для разработчика OS эта совместимость хуже вреда. Скажем, исправляете вы ошибку в обработке некорректных параметров той или иной функции Win32. Как вы думаете, что случится после того, как исправление пройдёт все тесты и будет послано в репозиторий кода? Через пару недель-месяцев с вероятностью сильно отличной от нуля вы получите письмо от Gov Maharaj, что это исправление ломает то или иное приложение. Или хуже – ломает некую библиотеку, которой пользуется множество приложений. Да, это приложение сует непонятно что в параметры этой функции. Да, оно никогда не должно было работать. Но работало – значит будь добр, исправь. Бывает, конечно, и наоборот, OS делает непонятно что, и приложения работали просто чудом.
Я тут собрал небольшую коллекцию багов, найденных примерно вышеописанным образом в процессе работы над Windows 7:
-
Некий софт вызывал функцию RegEnableReflectionKey в ситуации, когда она не делала ничего, просто возвращая ERROR_SUCCESS. Собственно говоря, она и не могла ничего сделать, даже если бы попыталась. Софт, тем не менее, тщательно проверял код возврата и отказывался работать в случае, если функция возвращала ошибку.
-
Некий антивирус поломался, когда в один прекрасный момент изменился регистр букв в имени ключа «HKEY_LOCAL_MACHINE\Software\Wow6432Node». Пришлось вернуть прежнее начертание.
-
Оригинальная версия механизма Registry Value Redirection (замена «%ProgramFiles%» на «%ProgramFiles(x86)%») использовала чувствительное к регистру сравнение строк. Когда это было замечено и исправлено выяснилось, что несколько разных приложений используют строки другого регистра и, тем самым, обходят перенаправление. И как только перенаправление заработало «как положено» все очень сильно поломалось.
-
Некий софт указывал оба флага KEY_WOW64_32KEY и KEY_WOW64_64KEY при вызове RegCreateKeyEx. Зачем и почему – не понятно. Но программа очень обиделась, когда функция начала возвращать ошибку. К счастью, в этом случае было проще исправить само приложение.
-
В один не столь прекрасный момент, выяснилось, что функции RegCeateKeyEx и RegOpenKeyEx по разному реагируют на ведущий слеш в имени ключа. В зависимости от версии OS, разрядности приложения, комбинации флагов KEY_WOW64_XXX и ветки реестра, где создавался ключ, можно было получить разный результат. К сожалению, к моменту, когда это было обнаружено, было уже поздно что-либо менять в коде реестра.
-
Обнаружилось, что создатели некоторых программ изобретательно подошли к регистрации COM объектов в реестре во время инсталляции. COM объекты регистрируются в «HKEY_CLASSES_ROOT\CLSID». Этот ключ «перенаправляется» в Wow64, т.е. существуют две версии этого ключа 32-х и 64-х разрядная, которые синхронизируются между собой с помощью механизма Registry Reflection. Так вот, содержимое некоторых из ключей создавалось по кусочкам, скажем «LocalServer32» брался из .msi и клался в 32-х битный ключ, а «ProgID» дописывался позднее из Custom Action в 64-х разрядный ключ. Это худо-бедно работало, пока из Windows 7 с корнем не выкорчевали Registry Reflection.
-
Однажды мне пришел баг, который был вызван тем, что строка, передаваемая в другой компонент Windows в формате UNICODE_STRING, не завершалась нулем. Но позвольте, заметил я, UNICODE_STRING и не должен завершаться нулем! Но ведь раньше этот конкретный UNICODE_STRING завершался нулем, резонно возразили владельцы компонента. Пришлось вернуть ноль на место.
Самое неприятное свойство подобных ошибок заключается в том, что исправлять приходится как раз «правильный» код. «Неправильный» код либо невозможно исправить (код уже выпущен, приложение не наше, разумного способа обновить инсталляции на дисках пользователей нет), либо сложнее исправить (например, этот код имеет гораздо больше зависимостей, которые нельзя протестировать в разумные сроки).
Вот так и живем…