четверг, 30 июня 2011 г.

Что я могу делать с HINSTANCE, возвращаемыми функцией ShellExecute?

Это перевод What can I do with the HINSTANCE returned by the ShellExecute function? Автор: Реймонд Чен.

Как мы видели ранее, в 16-битных Windows HINSTANCE идентифицировало программу. Ядро Win32 было полностью перепроектировано по сравнению с 16-битным ядром, вводя такие новые концепции как "объекты ядра" (kernel objects) и "дескрипторы безопасности" (security descriptors). В частности, 16-битная Windows не имела "идентификаторов процессов" (PID - Process ID); вместо них был описатель экземпляра (instance handle). Вот почему WinExec и ShellExecute возвращают HINSTANCE. Но в современном 32-разрядном мире HINSTANCE более не идентифицируют запущенную программу, потому что это просто базовый адрес исполняемого файла. Поскольку каждая программа имеет своё собственное адресное пространство, то эти значения далеко не уникальны в системе.

Так что же можно делать с этим значением HINSTANCE, возвращаемым функцией ShellExecute? Ну, вы можете проверить, что оно больше 32, что указывает на успешный вызов. Если значение меньше 32-х, то это код ошибки. Точное значение HINSTANCE, если оно больше 32, не имеет значения.

Почему я говорю вам вещи, которые и так прекрасно описаны в MSDN? Потому что люди всё равно не могут сложить два плюс два. Я видел как люди берут HINSTANCE от вызова ShellExecute и перечисляют все окна в системе в поиске окна с равным GWLP_HINSTANCE (или GWL_HINSTANCE, если вы всё ещё живёте в непросвещённом мире "несовместимы-с-64-битами"). Это не будет работать по причинам, которые я уже указал выше. Во-первых, точное значение, которое вы получаете, вообще не имеет смысла. Даже, если бы оно было осмысленным, то не дало бы вам ничего, поскольку HINSTANCE не уникальны (фактически, HINSTANCE процесса почти всегда равно $00400000, поскольку это значение по умолчанию в большинстве компоновщиков).

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

Во-первых, как уже было замечено, получаемые от ShellExecute HINSTANCE бесполезны. Однако вы можете использовать новую функцию ShellExecuteEx и установить флаг SEE_MASK_NOCLOSEPROCESS в записи TShellExecuteInfo, что приведёт к записи описателя процесса в поле hProcess. Но это всё ещё не сработает.

Дело в том, что документ может быть открыт без создания нового процесса. Самый частый подобный случай (но далеко не единственный) - это когда обработчик файла регистрирует DDE взаимодействие. В этом случае существующая (уже запущенная) копия программы принимает ответственность за открытие документа. Тогда ожидание выхода из процесса не будет эквивалентно ожиданию закрытия документа пользователем, потому что закрытие документа в программе не закрывает саму программу.

Если пользователь закрывает документ - это ещё не означает, что процесс завершится. Большинство программ просто закроют документ и останутся работать, позволяя вам открыть или создать новый документ через меню "Файл". Когда открывается новый документ, пользователь может закрыть старый (это применимо даже к однодокументным программам, которые автоматически закроют текущий документ при создании/открытии нового). Более того, даже закрытие всех окон программы не всегда приводит к её закрытию. Некоторые программы работают в фоне даже после закрытия всех их окон - либо чтобы оказывать какие-то фоновые услуги, либо просто потому, что они ожидают, что пользователь скоро снова будет работать с программой, так что они откладывают выход на время - на случай, если они понадобятся в течение ближайших нескольких минут, так что они сэкономят время на запуск.

Но даже если процесс завершается - это ещё не означает, что документ закрыт. Некоторые программы (те, которые не используют DDE) обнаруживают свою первую запущенную копию и передают ей документ для открытия, после чего завершают работу. Другие программы являются просто заглушками, которые просто запускают другие программы для выполнения работы. В любом случае, только что созданный процесс завершается практически сразу же, но документ остаётся открытым, поскольку ответственность за него передаётся какому-то другому процессу.

Нет никакого единообразного способа обнаружить, что документ был закрыт. Каждая программа обрабатывает документы разными способами. Если вам повезёт, то программа предоставит вам свойства, которые позволят вам мониторить статус открытого документа. Как мы видели ранее, Проводник предоставляет свойства, позволяющие перечислить его открытые окна. Как я понимаю, Microsoft Office также предоставляет довольно подробный набор интерфейсов для автоматизации своих программных компонентов.

4 комментария:

  1. Всю статью можно заменить одним словом "ничего" =)

    ОтветитьУдалить
  2. опечатка - "в большинстве компоновщикОВ"

    ОтветитьУдалить
  3. опечатка - "так что они отКЛАДЫвают выход на время"

    ОтветитьУдалить
  4. Спасибо, исправил!

    Вернул на место Orphus после смены темы оформления блога. Так что теперь в блоге снова работает Ctrl + Enter.

    ОтветитьУдалить

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

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

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

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

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