Это могла бы быть рождественская история со счастливым концом, но он не совсем такой.
В канун рождества 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.

Ок, ищу функцию в коде и, вы удивитесь, она есть но почему-то не добавлена в экспорт-лист (spec-файлы рядом с CMakeLists.txt в корне каждой DLL). Сборка/Установка/Запуск и опять подобная ошибка. Что же, сценарий известен. После повторения этой процедуры 5-10 раз от прощёлкивания Enter-ом установщика нагугливается, что у ОС есть режим unattend-установки. Для этого нужно в файле boot\bootdata\bootcd\unattend.inf включить UnattendSetupEnabled = yes. Супер, сани едут сами! Едут так быстро, что за чаем не успеваешь сходить — минуты две вся установка.
Так как ReactOS пишется как 2003, то код для 6.0+ никто особо не поддерживает. Такие места закрыты условиями #if _WIN32_WINNT >= 0x600, а то и вовсе #if 0 и их приходится приводить в рабочий вид. Недостающую функцию (хотя код её есть @$&^%!) добавить в экспорт (SleepConditionVariableCS требует RtlSleepConditionVariableCS) или где-то в SDK-заголовках разблокировать структуры/поля. Ещё более странный костыль с ntdll, kernel32 и advapi32 — для них зачем-то созданы дополнения в виде ntdll_vista, kernel32_vista и advapi32_vista в каждой из которых максимум 10-15 процедур, при этом в kernel32 есть целых два файла vista.c. Лебедь, рак и щука, не иначе, принимали такие решения. Сейчас определённости так же нет — при выкладке ПРа на гитхабе один просит вынести код в *_vista-либу, второй пишет что достаточно закрыть экспорт условием -version=0x600+ в spec-файле. Больше всего тут удивляет то, что все такие функции это новое API и вполне могут мирно сосуществовать с основным кодом, непонятно зачем городить такой огород.
Продолжались эти попытки на протяжении пары месяцев, но в итоге всё остановилось на том, что установщик зависал в середине процесса ни ругаясь, ни падая.
Летом решил вернуться к снаряду. Всё-таки столько времени потрачено, а результат нулевой. На этот раз решил делать всё иначе — раз при подмене сообщаемой версии с 5.2 на 6.1 всё работает вкривь и вкось, то надо менять версию совсем с другой стороны — попытаться собрать ReactOS полностью в режиме NT6.
Для этого надо в корневом CMakeLists.txt заменить следующие условия 0x502 на 0x600. Да, тут уже не до жиру, хотя бы 6.0 получить на выходе.
Ожидания успеха от этой затеи изначально были так себе, хотя идея в целом верная.
Суть проблемы в том, что в ядре NT6 многие функции изменили сигнатуры, структуры данных отличаются составом и порядком полей.
Ошибки сборки сыпались как из рога изобилия, самые простые из которых были в том что в CMake-файлах некоторых dll WINVER и _WIN32_WINNT явно переназначались на иные значения, например 0x602. При этом в 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 тоже не успевал заметить какую-то активность.
Немного позже решил покопаться в ресурсах установщика, где нашел текст сообщений об ошибках и ВОТ СЮРПРИЗ — все ограничения указаны в файле 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 на 0x500+) и несколько 0x600-условий в заголовках
Если вы следите на развитием ReactOS и пробуете установить свежее приложение, то наверняка сталкивались с тем, что не хватает каких-то функций либо интересный/полезный ПР висит бесконечно. Мне это тоже знакомо и потому решил регулярно составлять сборку ОС с недостающими кусками и ПРами (хочу держать график еженедельно или раз в две недели). Попробуйте эту сборку, возможно она будет полезна! Пишите если столкнётесь с очередной отсутствующей функцией — высока вероятность что она уже лежит в коде ReactOS или есть в Wine. USB-драйвера в ней пока нет.
Скачать можно здесь
С новым годом и стабильного ReactOS-а!
P.S. Важное обновление! Простое консольное приложение собранное под 4.0 или 4.5 перечисляющее папки/файлы на диске работает нормально. Значит косяк где-то с WinForms и WPF(висит как ВинФормс-пример), а не всем CLR.
Во время установки 4.5 замечал процессы ngen.exe c аргументом remove System.Windows.Forms или System.Dynamic и т.д. Похоже удалить удалил, а новые сборки не зарегистрировал нормально
P.P.S. СУПЕР важное обновление! Установщик 4.5 в процессе работы удаляет некоторые сборки из GAC, но где-то падает и новые скопировать не успевает (\Microsoft.NET\assembly\GAC_MSIL). В итоге из 115 сборок остаётся 73. Если скопировать туда сборки System, System.Drawing, System.Windows.Forms и Accessibility то простое WinForms-приложение стартует!
P.P.P.S. Выяснился прекрасный момент — если в system32 скопировать mscoree.dll от 4.0, то установщик 4.5 хоть и не отрабатывает по полной, но после перезагрузки консольные и вин-формс приложения стартуют.
В канун рождества 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.

Ок, ищу функцию в коде и, вы удивитесь, она есть но почему-то не добавлена в экспорт-лист (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 >= 0x600, а то и вовсе #if 0 и их приходится приводить в рабочий вид. Недостающую функцию (хотя код её есть @$&^%!) добавить в экспорт (SleepConditionVariableCS требует RtlSleepConditionVariableCS) или где-то в SDK-заголовках разблокировать структуры/поля. Ещё более странный костыль с ntdll, kernel32 и advapi32 — для них зачем-то созданы дополнения в виде ntdll_vista, kernel32_vista и advapi32_vista в каждой из которых максимум 10-15 процедур, при этом в kernel32 есть целых два файла vista.c. Лебедь, рак и щука, не иначе, принимали такие решения. Сейчас определённости так же нет — при выкладке ПРа на гитхабе один просит вынести код в *_vista-либу, второй пишет что достаточно закрыть экспорт условием -version=0x600+ в spec-файле. Больше всего тут удивляет то, что все такие функции это новое API и вполне могут мирно сосуществовать с основным кодом, непонятно зачем городить такой огород.
Продолжались эти попытки на протяжении пары месяцев, но в итоге всё остановилось на том, что установщик зависал в середине процесса ни ругаясь, ни падая.
Быть, а не казаться
Летом решил вернуться к снаряду. Всё-таки столько времени потрачено, а результат нулевой. На этот раз решил делать всё иначе — раз при подмене сообщаемой версии с 5.2 на 6.1 всё работает вкривь и вкось, то надо менять версию совсем с другой стороны — попытаться собрать ReactOS полностью в режиме NT6.
Для этого надо в корневом CMakeLists.txt заменить следующие условия 0x502 на 0x600. Да, тут уже не до жиру, хотя бы 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 явно переназначались на иные значения, например 0x602. При этом в 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 на 0x500+) и несколько 0x600-условий в заголовках
Список изменений для .NET 4.5
Помимо ПРов надо поправить немного winbase.h wincon.h и открыть функции из списка ниже
advapi32
msvcrt
kernel32
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-а!
P.S. Важное обновление! Простое консольное приложение собранное под 4.0 или 4.5 перечисляющее папки/файлы на диске работает нормально. Значит косяк где-то с WinForms и WPF(висит как ВинФормс-пример), а не всем CLR.
Во время установки 4.5 замечал процессы ngen.exe c аргументом remove System.Windows.Forms или System.Dynamic и т.д. Похоже удалить удалил, а новые сборки не зарегистрировал нормально
P.P.S. СУПЕР важное обновление! Установщик 4.5 в процессе работы удаляет некоторые сборки из GAC, но где-то падает и новые скопировать не успевает (\Microsoft.NET\assembly\GAC_MSIL). В итоге из 115 сборок остаётся 73. Если скопировать туда сборки System, System.Drawing, System.Windows.Forms и Accessibility то простое WinForms-приложение стартует!
P.P.P.S. Выяснился прекрасный момент — если в system32 скопировать mscoree.dll от 4.0, то установщик 4.5 хоть и не отрабатывает по полной, но после перезагрузки консольные и вин-формс приложения стартуют.