Зачем Win32-приложению манифест?

Dragokas

Angry & Scary Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
7,809
Реакции
6,574
Зачем Win32-приложению манифест?

Автор: Torbins (habrahabr.ru)

Недавно на руборде в разделе Программирование был задан вопрос: «Зачем Win32-приложению манифест? На что он влияет?». Первое, что сразу приходит в голову большинству программистов — это темы оформления. Но на самом деле в современных виндах манифест нужен не только для этого. Я подумал и написал пять ключевых аспектов, на которые влияет манифест или его отсутствие. После этого сразу несколько человек попросили оформить этот пост в виде более развернутой статьи.

DLL hell. Возник он из-за того, что Windows задумывалась как идеальная система. Все приложения в ней всегда должны были писаться с использованием самых свежих версий системных библиотек. А различные версии самих библиотек должны были быть взаимозаменяемыми. Реальность быстро доказала всем, что для популярной операционки это несбыточная мечта, так и возник ад. Каждое второе приложение во время инсталляции перезаписывало системные библиотеки нужными ему версиями. В результате после установки приложения X установленное ранее приложение Y начинало глючить. А после переустановки приложения Y глючить начинало приложение X. В общем юзерам жилось весело.

Для решения этой проблемы в Windows XP появилась технология Side-by-side Assembly (SxS). Суть ее заключалась в том, что приложение могло явно указать, с какой версией библиотеки оно желает работать. Эта информация могла указываться либо в специальном файле .manifest, либо в ресурсах приложения в разделе Manifest. В самой ХР на основе новой технологии реализовали одну из самых заметных новых фишек системы — темы оформления.

Типичный манифест:

INI:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="*"
    name="CompanyName.ProductName.YourApplication"
    type="win32"
/>
<description>Your application description here.</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="*"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>

Всем был хорош SxS кроме одного — он был ужасно неудобен для программиста. В 99% случаев манифест применялся только для включения тех самых тем оформления, и ни для чего больше. Разработчикам винды стало ясно, что нужен новый, более простой в использовании способ указать поддерживаемые версии системных библиотек. Тогда они придумали простое правило: в пределах одной версии системы интерфейс и поведение этих библиотек не изменяется. Нужно было только каким то образом научится определять какая их версия требуется конкретному приложению. Так в Windows 7 в манифесте появилась секция Compatibility, где можно указать с какими версиями ОС тестировалось приложение.
Также в манифесте начиная с Windows Vista появилось еще несколько секций, обо всех о них ниже.

Функции манифеста

  1. Активация тем оформления (Visual Styles)
    Эта технология появилась в ХР и полностью основана на SxS. Работает просто: вы запрашиваете у системы ComCtl32.dll шестой версии, и — вуаля — все стандартные контролы рисуются согласно с активированной в системе темой. Если же вы не упомянете в манифесте ComCtl32.dll, или манифест вообще будет отсутствовать в приложении, то система по умолчанию загрузит ComCtl32.dll пятой версии, которая отрисовывает контролы в стиле Win95. Кроме поддержки тем оформления, шестая версия ComCtl32.dll содержит кое-какой функционал, которого нету в пятой версии. Поэтому если отключить темы оформления, некоторые приложения будут работать и выглядеть не так, как задумывали их авторы.

    Справа тоже самое приложение без манифеста:

    8b3d193be574153b92b2b82f071d17bd.png

  2. Взаимодействие с UAC имеет несколько аспектов:
    • Ключ requestedExecutionLevel.level позволяет указать требуемый приложению уровень прав пользователя. К примеру если указать requireAdministrator, то приложению будут предоставлены права администратора (если пользователь разрешит).

      Запрос разрешения пользователя:

      77196727f806b2ff223388cbad1b349f.png

    • Если вся секция по взаимодействию с UAC отсутствует в манифесте, к приложению будет применена виртуализацияфайловой системы и реестра. Если такое приложение попытается записать что то в защищенные папки типа «Program files», оно будет перенаправлено в папку “%userprofile%\AppData\Local\VirtualStore\Program files". Аналогично, попытки записи в раздел реестра HKEY_LOCAL_MACHINE будут перенаправлены в «HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE». Естественно, сделанные таким образом изменения будут видны только приложениям, запущенным в режиме виртуализации.

      Виртуализация файловой системы в деле:

      2aa62e5a5c34966724146b16ea12edfc.png

    • Ключ requestedExecutionLevel.uiAccess позволяет приложениям, запущенным без прав администратора, взаимодействовать с окнами приложений, запущенных с правами администратора. Это может потребоваться экранным клавиатурам, приложениям типа AutoIt, экранным читалкам, тестовым и отладочным тулзам. Чтобы этот ключ сработал, приложение должно быть подписано сертификатом Microsoft Authenticode.


  3. Управление DPI-масштабированием

    С древнейших времен винда имеет механизм масштабирования интерфейса в зависимости от DPI монитора (тут говорится, что все началось в XP, но мне кажется, что раньше). В те времена эта настройка выставлялась только вручную, наверное из-за отсутствияEDID. Популярностью она не пользовалась, возможно потому, что запрятана была далеко, а возможно потому, что мониторов с большим разрешением было очень-очень мало. Кроме того, большая часть работы, необходимой для поддержки масштабирования, была отдана на откуп самим приложениям и их авторам. А программисты, как известно, люди весьма ленивые, поэтому куча софта для винды писалась в предположении, что DPI всегда равен стандартному значению 96. Очень часто встречалась ситуация, когда в приложении использовались библиотеки поддерживающие нестандартный DPI, в то время как код самого приложения его не поддерживал. Это приводило к появлению ужасных артефактов в интерфейсе приложения, стоило пользователю выставить DPI, к примеру, в значение 120 (масштабирование 125%):

    3788e59447fec40d477460bb1dba592e.png

    Разработчики Висты не стерпели подобного безобразия, и заложили в DWM возможность производить масштабирование самостоятельно, а приложениям врать, что DPI по прежнему равен 96. Причем зависящие от него системные настройки, разрешение монитора и даже положение мыши, также пересчитываются. К сожалению разработчики Висты небыли волшебниками, поэтому масштабирование DWM производит с помощью простых алгоритмов растягивания изображений. И если интерфейс приложения нужно увеличить, то происходит замыливание картинки. Представьте что было бы, если бы разработчики Фотошопа не могли это отключить. Таких бунтов на корабле никто не хотел, поэтому появилась возможность указать в манифесте, что ваше приложение таки умеет нормально масштабировать свой интерфейс, и помощь DWM ему не нужна. За это отвечает параметр dpiAware. Тут правда следует отметить, что по умолчанию масштабирование силами DWM включается при увеличении 150% и выше. Видимо в Microsoft посчитали, что при масштабировании 125% артефакты как на скриншоте выше вполне терпимы.

    Слева масштабирование силами DWM, а справа — самого приложения:

    407c59debee1be2881c80bcd429ef2e8.png

    В Windows 8.1 появилась возможность указывать разный масштаб разным мониторам, если подключено сразу несколько. Соответственно у ключа dpiAware появилось новое значение «True/PM». Оно означает, что приложение умеет динамически изменять масштаб своего интерфейса при переносе окон с одного монитора на другой.

  4. Декларирование совместимости
    Работает очень просто: программист тестирует свое приложение в определенной версии винды, и если все работает как надо, добавляет GUID этой версии в манифест.

    Пример:

    INI:
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
          <application>
            <!--This Id value indicates the application supports Windows Vista functionality -->
              <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
            <!--This Id value indicates the application supports Windows 7 functionality-->
              <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
            <!--This Id value indicates the application supports Windows 8 functionality-->
              <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
            <!--This Id value indicates the application supports Windows 8.1 functionality-->
              <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
          </application>
      </compatibility>


    Наиболее интересен вопрос: «На что влияют эти GUID-ы?» Пока что список различий в поведении системных библиотек невелик. Наиболее интересно упоминание об оптимизации RPC. Получается что приложения, задекларировавшие совместимость с семеркой, будут работать быстрее.
    В будущем этот раздел манифеста наверняка будет играть большую роль чем сейчас. Ведь в винде полно разных хаковпризванных обеспечивать совместимость. И теперь есть возможность оградить от них нормальные приложения.

    Если GUID-ы полностью отсутствуют в манифесте, то к приложению применяются правила как к совместимому с Вистой:

    2ac3e7be97f77a379e059ca4fdecbf3e.png

  5. Управление прочими функциями
    Выше я описал только те функции, с которыми приходилось иметь дело лично. Но это далеко не все, чем можно управлять из манифеста: msdn.microsoft.com/en-us/library/aa374191.aspx К сожалению, документация по большей части остальных функций недоступна. Возможно ее опубликуют позже, как в свое время произошло с Magnification API.

Эпилог

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

1. Активация тем оформления

Итак мы, указали в манифесте ссылку на новую версию Comctl32.dll (6.0.):
Код:
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                type="win32"
                name="Microsoft.Windows.Common-Controls"
                version="6.0.0.0"
                processorArchitecture="X86"
                publicKeyToken="6595b64144ccf1df"
                language="*"
             />
        </dependentAssembly>
    </dependency>
Но этого недостаточно. Необходимо еще и инициализировать визуальные стили в самом приложении
с помощью функции InitCommonControls или InitCommonControlsEx, если рассчитываете на поддержку XP.

2. Ключ requestedExecutionLevel.level позволяет указать требуемый приложению уровень прав пользователя.
Код:
<requestedExecutionLevellevel="asInvoker"uiAccess="false"/>
Можно задать один из 3 уровней прав:
  • asInvoker - запуск приложения с текущими правами
  • highestAvailable - даст запрос на повышение привилегий, если текущий пользователь может обладать соответствующими правами, иначе запустит с текущими правами.
  • requireAdministrator - даст запрос на повышение привилегий в любом случае. Если текущий пользователь не обладает соответствующими правами, то будет предложено ввести пароль и имя пользователя, входящего в группу "Администраторы".
3. Ключ requestedExecutionLevel.uiAccess позволяет приложениям, запущенным без прав администратора, взаимодействовать с окнами приложений, запущенных с правами администратора.

Здесь можно привести пример, если у Вас Windows Vista и выше с включенным UAC.
Скачаем программу лечения ярлыков ClearLNK. Запустим.
Попытаемся перетащить файл ярлыка в главное окно.

UI_denied.png


Причина отказа в том, что приложение имеет высокий уровень целостности, а Explorer - средний.
Проблему можно обойти указанием uiAccess="true" в манифесте.
К сожалению, для работы этой функции обязательно требуется легитимная цифровая подпись.

4. Декларирование совместимости
Кроме сказанного тов. Torbins, отсутствие декларации о совместимости будет влиять, например, на:

1) результат получения информации о версии системы API-функцией GetVersion.
Так, ОС Windows 8.1 и Windows Server 2012 R2 будут сообщать о том, что она - Windows 8 (Major.Minor = 6.2).

2) будет вызываться "Помощник по совместимости программ";

3) особенности отрисовки графики, работу с памятью и прочее. Подробнее в этой статье MSDN.


Кодировка.
Кодировка файла манифеста должна быть UTF-8.

Распаковка.
Вы можете самостоятельно изучить, как составлены манифесты других программ,
распаковав их из ресурсов с помощью бесплатного Resouce Hacker.

rhacker.png



Подключение манифеста к приложению.
Есть 2 способа:
1) Как внешний файл - путем указания имени "application.exe.manifest", где "application.exe" имя файла нашей программы.
Для .NET приложений требуется указать этот файл в свойствах сборки:

manifest - копия.png


2) Как ресурс приложения. Тип ресурса должен быть 24.
Для других типов приложений можете почитать статью Как сделать XP-интерфейс в программе.

Быстрое подключение и модификация манифеста приложения.
Вы можете использовать готовую программу Manifested от Кривоуса Анатолия (в конце поста ver. 1.2 unoff by Dragokas).

Patcher.png


Она также поддерживает интерфейс командной строки и может работать скрыто в пакетном режиме. Пример:
Код:
manifested.exe application.exe manifest.txt -silent
где manifest.txt - текстовый файл манифеста в кодировке ANSI.
application.exe - путь и имя Вашего приложения.
 

Вложения

  • ManifestByTheTrick.rar
    17.1 KB · Просмотры: 6
Последнее редактирование:
добавляет GUID этой версии в манифест.
А я правильно понимаю, что GUID может быть любой зарегистрированный в системе?
Каким образом приложение,наткнувшись на GUID умеет его распознать (в смысле понять что это именно)?
 
Koza Nozdri, нет. Эти идентификаторы строго предопределены и не имеют ничего общего с регистрацией классов.
По Windows 10 GUID был опубликован Chuck Walbourn, Software Design Engineer (Microsoft Corporation).
Если интересно, по информации со stackowerflow этот же GUID есть в манифесте файла wscript.exe. Можешь попробовать глянуть Resource Hacker-ом.
Как реализован сам механизм обработки этих GUID я сказать не могу. Да и зачем так глубоко это знать.
 
Назад
Сверху Снизу