Вы когда-либо пробовали собрать “правильное” приложение dlang для Windows? В этой статье пойдет речь о том, почему я в этом сильно сомневаюсь и причем тут манифест приложения.
Обнаружение проблемы
Недавно мне понадобилось в рантайме узнать версию моей Windows 10. Для этого я использовал функцию winapi GetVersionEx, можно взять и GetVersion, но это менее удобно. Код выглядит следующим образом:
import std.stdio; import core.sys.windows.winbase; import core.sys.windows.winnt; void main() { OSVERSIONINFO osvi; GetVersionEx(&osvi); writefln("Your Windows version is %d.%d.", osvi.dwMajorVersion, osvi.dwMinorVersion); }
Каково же было мое удивление, когда в результате исполнения этого кода на Windows 10 мне вывело:
Your Windows version is 6.2.
А должно было выдать 10.0, судя по документации.
В попытке разобраться в где же тут “зарыта собака” я вчитался в документацию, благо, нужный абзац оказался недалеко:
Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2). Once an application is manifested for a given operating system version, GetVersionEx will always return the version that the application is manifested for in future releases. To manifest your applications for Windows 8.1 or Windows 10, refer to Targeting your application for Windows.
В этот момент я и понял, что собираю свое приложение для Windows неправильно.
Ориентация вашего приложения на Windows
Конечно, я первым делом посетил указанную страницу документации Targeting your application for Windows.
Первое, что бросается в глаза, это подтверждение того, что я уже видел ранее:
В Windows 8.1 и Windows 10 функции GetVersion и GetVersionEx устарели. В Windows 10 функция VerifyVersionInfo также устарела. Хотя вы по-прежнему можете вызывать устаревшие функции, если ваше приложение специально не нацелено на Windows 8.1 или Windows 10, функции вернут версию Windows 8 (6.2).
Чтобы ваше приложение было ориентировано на Windows 8.1 или Windows 10, вам необходимо включить манифест приложения (в исполняемый файл) для исполняемого файла приложения. Затем в разделе манифеста <compatibility> вам нужно будет добавить элемент <supportedOS> для каждой версии Windows, которую вы хотите объявить поддерживающей ваше приложение.
Далее идет пример файла манифеста. Собственно, это вся “ориентация”.
Добавление манифеста в ваше приложение
Я сделал манифест для своего приложения, указал в нем поддержку всех версий Windows как в примере документации, но возник вопрос о том, как мне добавить его в свое приложение.
Самый простой способ, который мне удалось обнаружить, это в корень проекта приложить файл манифеста, назвав его следующим образом:
YouApp.exe.manifest
Любое другое название не поддерживается. Этот способ годится для сборки DMD или LDC2 компилятором приложения любой поддерживаемой разрядности.
Кроме того, мне удалось “нагуглить”, ряд параметров компоновщика MSVC, которые выглядели подходящими:
Параметры указываются в виде
lflags "/MANIFESTFILE:YouApp.exe.manifest" platform="windows"
Данный флаг аналогичен первому способу. Кастомное название не поддерживается.
Остальные флаги позволяют сгенерировать внедренный манифест из своих параметров. Для этого требуется флаг
lflags "/MANIFEST:EMBED,ID=1" platform="windows"
для exe-файлов и
lflags "/MANIFEST:EMBED,ID=2" platform="windows"
для dll-файлов.
В целом, так можно делать, но это работает только с линковщиком MSVC и надо прописывать все эти флаги. Поэтому я вспомнил про еще один вариант…
Однажды в нашем блоге мы публиковали интересную статью про включение ресурсных файлов (.res) в свое приложение.
Файлы с расширением .res представляют собой файлы ресурсов для использования в операционных системах Windows. Файл RES обычно содержит информацию о связанной компьютерной программе, такую как тип графики, включенной в программу, а также курсоры и значки, которые использует приложение. Файл RES может также включать информацию о версии программы и манифест.
Для генерации такого файла обычно используются специальные утилиты или приложения. Я решил воспользоваться бесплатной программой Resource Editor. Как оказалось, она умеет генерировать манифест и имеет еще много полезных возможностей.

Кроме манифеста я добавил в ресурсы еще иконку приложения и информацию о версии.
Обратите внимание на необходимость указания правильного ID ресурса, в программе есть выпадающий список, где можно увидеть те ID, которые “знает” Windows. Для манифеста это CREATEPROCESS_MANIFEST_RESOURCE_ID. Для своих ресурсов можно использовать любые не используемые системой ID.
Сохраняем наши ресурсы в файл с расширением .res. Осталось заставить линкер внедрить этот файл в наше приложение. Процесс “внедрения” мы уже описали в статье Entice Designer + Resource Builder. Единственное, что я могу добавить, это флаг линковщика для сборки через DUB:
lflags "you_path/you_resource.res" platform="windows"
Класть файл манифеста в корень проекта или добавлять специальные флаги линковщика не нужно.
К сожалению, этот способ не работает с линковщиком компилятора DMD в режиме сборки под x86. Я так и не смог понять почему и как правильно прикрепить файл для x86, если вы знаете в чем тут подвох, напишите мне. С LDC2 все работает без проблем.
Результат проделанной работы:
И вывод программы теперь верный:
Your Windows version is 10.0.
Я уверен, что это еще далеко не все, что можно раскопать по теме сборки приложений под Windows, я уже и не говорю про другие операционные системы. Буду продолжать опыты в данном направлении, а также приглашаю всех в наш Discord, где можно обсудить эту и другие темы.