[Из песочницы] Сказ о том как .NET 4.5 на ReactOS ставился

Это могла бы быть рождественская история со счастливым концом, но он не такой.

В канун рождества 2018 года заметил в списке проверенных приложений для ReactOS установщик .NET 4.0 и был приятно удивлён, что он успешно устанавливается и приложения запускаются. Но так как 4.0 давно не актуален меня посетила безумная идея —, а что будет если попытаться установить версию 4.5?
Установщик сразу заявил что версия ОС не подходит. Запуск в режиме совместимости так же определялся им и устанавливаться отказался. Дела, всё закончилось не начавшись…

Не совместимость с ОС

Да сейчас версию поменяю и все заработает!


ReactOS разрабатывается как аналог Windows 2003 и версию он сообщает как 5.2. Для установки требуется как минимум 6.0, но тогда я решил что целиться лучше сразу на Windows 7 и стал искать как менять версию на 6.1. В коде по словам version/MajorVersion/MinorVersion нашлось несколько мест по всей системе, даже там где не ожидаешь. Заменив все пары 5.2 на 6.1 результат был нулевой — не стартовал даже установщик ОС. Дальнейший поиск вывел на freeldr и ntldr в boot — в 4 местах надо было заменить _WIN32_WINNT_WS03 на _WIN32_WINNT_WIN7. ОС установилась и сообщает нам что это 6.1. Отлично?! Нет. Почему-то с такой заменой ничего толком не устанавливается — ни VirtualBox Guest Additions, ни Firefox, ни .NET 4.0 и даже проводник падает через раз. Окей, значит надо создать ISO со всем необходимым и подключить его к виртуалке. Слава байтам, привод ещё работает и снова стартуем установщик с него. Ура! Он хотя бы стартует, но жалуется на отсутствие неких компонентов ОС:

Предупреждения установщика

Первый это сервис автоматических обновлений, второй — Trusted Installer. Ничего подобного у нас нет — действительно, зачем в ReactOS обновления Windows, а Trusted Installer не может быть по определению ибо он появился вместе с Windows 7. Так же в логе нашлась строка, что отсутствует некий wusa.exe. Ок, создаём заглушки для wusa.exe и wuauserv.dll для сервиса обновлений PR 355. Trusted Installer просто скопировал из Windows 7 32bit вместе с ключами реестра. Теперь установщик определяет наличие всего необходимого и остаётся лишь скрепить договор капелькой крови.

Процесс верификации файлов проходит успешно и стартует установка. Сразу получаем сообщение, что не хватает функции LCMapStringEx в модуле kernel32.

LCMapStringEx

Ок, ищу функцию в коде и, вы удивитесь, она есть, но почему-то не добавлена в экспорт-лист (spec-файлы рядом с CMakeLists.txt в корне каждой DLL). Сборка/Установка/Запуск и опять подобная ошибка. Что же, сценарий известен. После повторения этой процедуры 5–10 раз от прощёлкивания Enter-ом установщика нагугливается, что у ОС есть режим unattend-установки. Для этого нужно в файле boot\bootdata\bootcd\unattend.inf включить UnattendSetupEnabled = yes. Супер, сани едут сами! Едут так быстро, что за чаем не успеваешь сходить — минуты две вся установка.

Настройки в unattend.inf

Там же можно поменять папку установки с ReactOS на Windows или Bolgenos (а может и GreenteaOS? :)), разрешение графического режима, включить установку темы и ещё пару моментов


Так как ReactOS пишется как 2003, то код для 6.0+ никто особо не поддерживает. Такие места закрыты условиями #if _WIN32_WINNT >= 0×600, а то и вовсе #if 0 и их приходится приводить в рабочий вид. Недостающую функцию (хотя код её есть @$&^%!) добавить в экспорт (SleepConditionVariableCS требует RtlSleepConditionVariableCS) или где-то в SDK-заголовках разблокировать структуры/поля. Ещё более странный костыль с ntdll, kernel32 и advapi32 — для них зачем-то созданы дополнения в виде ntdll_vista, kernel32_vista и advapi32_vista в каждой из которых максимум 10–15 процедур, при этом в kernel32 есть целых два файла vista.c. Лебедь, рак и щука, не иначе, принимали такие решения. Сейчас определённости так же нет — при выкладке ПРа на гитхабе один просит вынести код в *_vista-либу, второй пишет что достаточно закрыть экспорт условием -version=0×600+ в spec-файле. Больше всего тут удивляет то, что все такие функции это новое API и вполне могут мирно сосуществовать с основным кодом, непонятно зачем городить такой огород.

Продолжались эти попытки на протяжении пары месяцев, но в итоге всё остановилось на том, что установщик зависал в середине процесса ни ругаясь, ни падая.

Быть, а не казаться


Летом решил вернуться к снаряду. Всё-таки столько времени потрачено, а результат нулевой. На этот раз решил делать всё иначе — раз при подмене сообщаемой версии с 5.2 на 6.1 всё работает вкривь и вкось, то надо менять версию совсем с другой стороны — попытаться собрать ReactOS полностью в режиме NT6.

Для этого надо в корневом CMakeLists.txt заменить следующие условия 0×502 на 0×600. Да, тут уже не до жиру, хотя бы 6.0 получить на выходе.

    # Version Options
    add_definitions(-DWINVER=0x502
                    -D_WIN32_IE=0x600
                    -D_WIN32_WINNT=0x502
                    -D_WIN32_WINDOWS=0x502
                    -D_SETUPAPI_VER=0x502)

Ожидания успеха от этой затеи изначально были так себе, хотя идея в целом верная.
Суть проблемы в том, что в ядре NT6 многие функции изменили сигнатуры, структуры данных отличаются составом и порядком полей.

#if (_WIN32_WINNT >= 0x600)
NTSTATUS
RxConstructSrvCall(
    _In_ PRX_CONTEXT RxContext,
    _In_ PIRP Irp,
    _In_ PSRV_CALL SrvCall,
    _Out_ PLOCK_HOLDING_STATE LockHoldingState);
#else
NTSTATUS
RxConstructSrvCall(
    _In_ PRX_CONTEXT RxContext,
    _In_ PSRV_CALL SrvCall,
    _Out_ PLOCK_HOLDING_STATE LockHoldingState);
#endif         


Ошибки сборки сыпались как из рога изобилия, самые простые из которых были в том что в CMake-файлах некоторых dll WINVER и _WIN32_WINNT явно переназначались на иные значения, например 0×602. При этом в ReactOS полно dll, у которых и сейчас WINVER переопределён с 0×502 на 0×600. Нашлось и несколько настоящих багов #356 #359 #747 #814 #815.

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

Логика не железная


Грядёт новое рождество, а .NET 4.5 не даёт покоя. Вновь возвращаюсь к первому варианту, повторяются все правки, но с некоторыми изменениями. Если раньше вместо недостающих функций (для которых нет кода в ОС) делал просто заглушки, то теперь решил поискать их в коде Wine и, о чудо, там они были. Перенести, адаптировать под ReactOS, к следующей #1045. Где-то вместо заглушки можно и настоящего кода написать :) #1046. С такими правками установщик работал уже по-бодрее и даже завершался «успешно», но в отличие установщика .NET 4.0 не предлагал выполнить перезагрузку после установки — я списал на то, что все таки софт для нового поколения ОС и нет нужды перезагружаться каждый раз (ха ха, святая наивность). При попытке запустить helloWorld-приложение на экране не происходило ничего, в task manager тоже не успевал заметить какую-то активность.

Небольшое отступление по установщику

в самом начале пути, что бы не ждать каждый раз старт установщика, я распаковал его в папку и щёлкал setup.exe вручную. Запускать нужно с аргументом /x86 для чего создал к нему ярлык


Немного позже решил покопаться в ресурсах установщика, где нашел текст сообщений об ошибках и ВОТ СЮРПРИЗ — все ограничения указаны в файле ParameterInfo.xml! Достаточно было закомментировать StopBlockers-условие IsInOSCompatibilityMode и всё стартует без проблем. Убираю правки, которые меняют версию ОС с 5.2 на 6.0, включаю в ярлыке к setup-у режим совместимости с Vista (так он не ожидает наличие Trusted Installer) и добавив ещё немного функций установщик завершался так же «успешно». Однако если сперва установить 4.0 и потом запустить установщик 4.5, то процесс завершился уже с просьбой перезагрузиться! Победа?! Нет. Я же говорил что это сказ без хэппи-энда. При попытке запустить HelloWorld результат немного отличается, но не сильно — процесс занимает 11–12МБ памяти и, повисев секунд 20, завершается. Запуск в режиме совместимости не помогает (всё-таки CLR-runtime запускается в каждом процессе отдельно, а не одна общесистемная среда, которая стартует вместе с ОС с версией ОС 5.2).
В логе видим вызовы на определение версии:

Проверка версии

Добавил в RtlVerifyVersionInfo хак, что если запрашивается версия 6.*, то подменить версию ОС на 6.0. Отмеченные стрелкой строки ушли, но результат тот же.

Не хэппи-энд.

Заключение


Возможно до успеха осталось совсем немного и вы, вдохновились этим рассказом, готовы подхватить флаг и закончить начатое скачав код ReactOS и собрав её? Не так быстро. Почти по всем ПР на сделанные изменения при их выкладке сперва идёт какое-то обсуждение, но потом они висят без внимания основных разрабов ОС.

Для того что бы повторить описанное надо на мастер-ветку накатить ПРы по ссылкам в тексте, разблокировать пачку функций в spec-файлах (заменить версию в условии -version на 0×500+) и несколько 0×600-условий в заголовках

Список изменений для .NET 4.5
Помимо ПРов надо поправить немного winbase.h wincon.h и открыть функции из списка ниже
advapi32
  • EventWrite (stub)
  • EventRegister (stub)
  • EventUnregister (stub)
  • RegLoadMUIStringA
  • RegLoadMUIStringW

msvcrt

  • _except_handler4_common

kernel32

  • AcquireSRWLockExclusive
  • AcquireSRWLockShared
  • CloseThreadpool
  • CloseThreadpoolCleanupGroup
  • CloseThreadpoolCleanupGroupMembers
  • CloseThreadpoolIo
  • CloseThreadpoolTimer
  • CloseThreadpoolWait
  • CloseThreadpoolWork
  • SetThreadpoolTimer
  • SetThreadpoolWait
  • CompareStringEx
  • CreateSemaphoreExA (stub)
  • CreateSemaphoreExW (stub)
  • CreateThreadpool
  • CreateThreadpoolCleanupGroup
  • CreateThreadpoolIo
  • CreateThreadpoolTimer
  • CreateThreadpoolWait
  • CreateThreadpoolWork
  • EnumCalendarInfoExEx
  • EnumDateFormatsExEx
  • EnumSystemLocalesEx
  • EnumTimeFormatsExEx
  • FlushProcessWriteBuffers (stub)
  • GetCalendarInfoEx
  • GetDateFormatEx
  • GetLocaleInfoEx
  • IsValidLocaleName (stub)
  • GetNLSVersionEx (stub)
  • GetNumberFormatEx
  • GetTickCount64
  • GetTimeFormatEx
  • GetUserDefaultLocaleName
  • LCMapStringEx
  • InitOnceExecuteOnce
  • InitializeCriticalSectionEx
  • InitializeSRWLock
  • ReleaseSRWLockExclusive
  • ReleaseSRWLockShared
  • WerSetFlags (stub)


Некогда собирать, давай сюда сборку!


Если вы следите на развитием ReactOS и пробуете установить свежее приложение, то наверняка сталкивались с тем, что не хватает каких-то функций либо интересный/полезный ПР висит бесконечно. Мне это тоже знакомо и потому решил регулярно составлять сборку ОС с недостающими кусками и ПРами (хочу держать график еженедельно или раз в две недели). Попробуйте эту сборку, возможно она будет полезна! Пишите если столкнётесь с очередной отсутствующей функцией — высока вероятность что она уже лежит в коде ReactOS или есть в Wine. USB-драйвера в ней пока нет.

Скачать можно здесь

С новым годом и стабильного ReactOS-а!

© Habrahabr.ru