среда, 2 февраля 2011 г.

Неудачное взаимодействие между LOAD_LIBRARY_AS_DATAFILE и функцией DialogBox

Это перевод The unfortunate interaction between LOAD_LIBRARY_AS_DATAFILE and DialogBox. Автор: Реймонд Чен.

Некоторые люди заметили, что если вы загружаете DLL с флагом LOAD_LIBRARY_AS_DATAFILE, то иногда вы получаете странное поведение, если передаёте этот HINSTANCE в функцию DialogBox.

Проблема тут в том, что поскольку младшие 16 бит корректно загруженной DLL всегда равны 0 (прим.пер.: иными словами, корректный HINSTANCE кратен 64 Кб), то различные компоненты "заимствуют" эти биты для различных целей. Ядро использует младший бит, чтобы отличить модуль, загруженный проецированием в память секциями (т.е. загруженный обычным способом) от тех, кто были спроецированы одним гигантским блоком (загружены как файл с данными). Это необходимо, чтобы различные функции управления ресурсами вроде FindResource знали, как интерпретировать данные модуля. Хотя сегодня все знают, что HINSTANCE - это базовый адрес модуля, но в общем случае это "непрозрачное значение" (а в 16-ти битном мире оно было таким всегда).

А у оконного менеджера тоже есть свои проблемы. Чтобы поддерживать 16-ти битные приложения, работающие бесшовно на одном рабочем столе (а не в виртуальной машине, как обсуждалось ранее), а равно как и переходники между 16-ти битным и 32-х битных кодом, менеджеру окон нужно принимать и 32-х битные HINSTANCE и 16-ти битные HINSTANCE. Поскольку гранулярность памяти 64 Кб, то оконный менеджер знает, что корректные 32-х битные HINSTANCE имеют нули в младших 16-ти битах, а 16-ти битные HINSTANCE имеют там не нули.

Возможно, вы уже видите конфликт.

Если вы передаёте описатель DLL, загруженной как файл данных, ядро установит младший бит, чтобы указать самому себе, что ресурсы искать надо в манере плоского файла, а не спроецированной DLL. Но оконный менеджер смотрит на эти младшие 16 бит, которые не равны нулю и предполагает, что ему дали 16-ти битный HINSTANCE.

Удивительно, но обычно это не вызывает проблем, потому что есть не так много вещей, которые обрабатываются по-разному между 16-ти и 32-х битными HINSTANCE. Одной из вещей, которая доставит вам неприятности, является сегмент данных.

В 16-ти битных Windows диалоговое окно имело свой собственный сегмент данных, который использовался как локальный сегмент данных для элементов управления в диалоговом окне. Большинство элементов управления не нуждается в куче места, так что источник сегмента данных для них не важен. Однако элементы управления текстового ввода являются заметным исключением, поскольку они могут хранить много килобайт текста - а дюжина килобайт не всегда может влезть в один сегмент данных приложения. Поэтому создание нового сегмента данных даёт элементам текстового ввода возможность оперировать большим объёмом данных. Ожидалось, что программы будут получать доступ к этим данным через функции вроде GetWindowText и хранить данные где-то ещё (вне замусоренного локального сегмента данных).

Чтобы сохранить совместимость с 16-ти битными программами, которые ожидали это поведение, оконный менеджер, видя 16-ти битный HINSTANCE, покорно создавал 16-ти битный сегмент данных, в котором могли хранить данные элементы управления, используя вспомогательные функции, предоставляемые слоем эмуляции 16-ти битных программ. Но если у вас была не 16-ти битная программа, то этот слой был не активен, и, соответственно, не мог сказать менеджеру окон, как нужно создавать этот сегмент. Результат: вылет (crash).

Решение заключается в добавлении стиля DS_LOCALEDIT в ваше диалоговое окно. Этот флаг означает "Не создавать сегмент данных для диалогового окна; просто используй сегмент данных вызывающего". В результате, если ваш HINSTANCE с LOAD_LIBRARY_AS_DATAFILE будет ошибочно принят за 16-ти битный диалоговый шаблон, то менеджер диалогов не будет пытаться создать 16-ти битный сегмент данных и не будет вызывать функцию, которая не существует.

Мне кажется, что эта проблема была исправлена в Windows XP SP 2. Оконный менеджер использует другой механизм, чтобы определить разрядность, и поэтому его больше не обмануть HINSTANCE от функции LoadLibraryEx.

Комментариев нет:

Отправить комментарий

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.