Добро пожаловать в нашу новую серию гостевых блогов!

Вы знаете его по нашей Прямой трансляции HACK + ALT + NCOMMANDER, участник DEFCON 201 NCommander будет вести серию гостевых блогов с эксклюзивными хаками, прежде чем они появятся популярный технический портал SoylentNews.

Наслаждаться!

Для тех, кто давно читает SoylentNews, не секрет, что у меня есть личный интерес к ретро-вычислениям и документированию истории и эволюции персональных компьютеров. Года три назад я публиковал серию статей о восстановлении Xenix 2.2.3c, и давно пора написать новую. Для тех, кто занимается программированием любого рода, вы также будете знакомы с Hello World, первой программой, которую большинство, если не все, программисты пишут в своей карьере.

Пример программы hello world может выглядеть следующим образом:

#include <stdio.h>
int main() {
 printf("Hello world\n");
 return 0;
}

Недавно я был вдохновлен исследовать оригинальный HELLO.C для Windows 1.0, бегемот из 125 строк, о котором говорили вполголоса. С этой целью я записал видео на YouTube, в котором рассказывается о мире программирования для Windows 1.0, а затем тестируется обратная совместимость Windows с Windows 10.

Для тех, кто менее склонен смотреть видео, мой отчет об этом опыте уже вышел за рамки, а аннотированная версия файла доступна на GitHub.

Выведи своих динозавров — DOS 3.3

Прежде чем мы перейдем к теме HELLO.C, нужно сказать немало об этих древних версиях Windows. Windows 1.0, как и все версии до 95 года, требовала предустановки DOS. Одна особенность этой конкретной версии Windows заключается в том, что она зависает при запуске на чем-либо более позднем, чем DOS 3.3. Частично это связано с внутренней проверкой версии, которую можно обойти с помощью SETVER. Однако, даже если эту проверку версии обойти, предположительно существуют известные проблемы с запуском COMMAND.COM. Чтобы уменьшить количество потенциальных головных болей, я решил просто установить PC-DOS 3.3 и дать Windows то, что она хочет.

Вы могли заметить, что я не сказал Microsoft DOS 3.3. Причина в том, что в то время DOS не существовало как отдельный продукт. Вместо этого сборщики систем будут лицензировать DOS OEM Adaptation Kit и создавать свои собственные DOS, такие как Compaq DOS 3.3. Учитывая, что PC-DOS создавалась для собственной линейки компьютеров IBM, ее обычно считают наиболее «универсальной» версией версий до DOS 5.0, и эта версия была выбрана для нашей базы. Однако из-за своего возраста у него есть некоторые особенности, которые исчезнут в более поздних и более распространенных версиях DOS.

PC DOS 3.3 отлично загрузилась в VirtualBox и — с единственной дискетой на 720 КиБ, которая была загрузочной — сразу же бросила меня в командную строку. Точно так же FDISK и FORMAT были доступны для разделения жесткого диска для установки. Однако размер каждого отдельного раздела ограничен 32 МБ. Даже в то время это было несколько ограничено, и Compaq DOS была первой (насколько мне известно), которая сняла это ограничение. Запуск FORMAT C: /S создал загрузочный диск, но часто забывают, что IBM на самом деле предоставила утилиту установки, известную как SELECT.

Неизвестность SELECT в первую очередь заключается в его неочевидном имени или использовании, а также в том факте, что он действительно необходим для установки DOS; достаточно просто скопировать файлы на жесткий диск. Однако SELECT создает CONFIG.SYS и AUTOEXEC.BAT, поэтому его удобно использовать. По сравнению с более поздней настройкой DOS, SELECT требует относительно загадочного вызова с целевой папкой установки, раскладкой клавиатуры и кодом страны, введенными в качестве аргументов, и просто выдает ошибки, если они неверны. После ввода правильных рун SELECT форматирует целевой диск, копирует DOS и завершает установку.

Без особой помпы первое препятствие было преодолено, и мы приступили к установке Windows.

Установка Windows 1.0/проблемы с мышью

С установленным DOS он был включен в Windows. По сравнению с минималистской командой SELECT, Windows 1.0 поставляется со специальной программой установки и простым текстовым интерфейсом. Эта доработка, вероятно, была связана с тем, что большинство пользователей должны были установить Windows самостоятельно, а не предварительно установить ее.

Еще одна интересная особенность заключалась в том, что Windows можно было установить на вторую дискету из-за редкости жестких дисков той эпохи, что мы увидим позже с Microsoft C 4.0. Установка прошла (в основном) гладко, хотя мне потребовалось две попытки, чтобы установить работающую установку из-за опечатки. Ввод WIN привел меня к довольно спартанскому интерфейсу Windows 1.0.

Несмотря на функциональность, чего не хватало, так это поддержки мыши. Из-за своего возраста Windows предшествует мыши как стандартному оборудованию и предшествует протоколу мыши PS/2; из коробки поддерживались только последовательные и шинные мыши. Есть два способа решить эту проблему:

Первый, который я использовал, включает копирование MOUSE.DRV из Windows 2.0 на установочный носитель Windows 1.0, а затем повторную установку, выбрав в меню пункт Мышь Microsoft. Требуется переустановка, т.к. WIN.COM статически связан как часть установки с включенными только необходимыми драйверами; нет возможности изменить настройки позже. В документации SDK подробно описан процесс статической компоновки и запуск Windows в медленном режиме для разработки драйверов, но конечный результат тот же. Если вы хотите перенастроить, вам нужно переустановить.

Второй вариант, о котором я не знал до тех пор, пока не снял видео, заключается в использовании версии Windows 1.0 для PS/2. Как и DOS той эпохи, Windows была лицензирована для OEM-производителей, которые могли адаптировать ее к своему индивидуальному оборудованию. IBM действительно сделала это для своей тогдашней новой линейки компьютеров PS/2, добавив в то время поддержку мыши PS/2. Несмотря на то, что эта версия Windows предназначена для линейки PS/2, известно, что она работает на AT-совместимых машинах.

Тем не менее, второе препятствие было пройдено, и у меня была рабочая мышь. Это значительно упростило изучение Windows 1.0.

Опыт Windows 1.0

Если вы заинтересованы в том, чтобы попробовать Windows 1.0, я бы порекомендовал зайти на PCjs.org и использовать их браузерный эмулятор, чтобы поиграть с ним, поскольку он уже поддерживает работающую мышь и не требует приобретения программного обеспечения 35-летней давности. . Точно так же об этой версии есть множество рецензий, но было бы упущением, если бы я не посвятил хотя бы немного времени разговору о ней, хотя бы с технического уровня.

По сравнению даже с немного более поздней Windows 2.0, Windows 1.0 намного ближе к DOSSHELL, чем к любой другой версии Windows, и, по сути, является графическим дополнением к DOS, хотя благодаря глубокой магии она способна к совместной многозадачности. Это было сделано полностью с помощью программного обмана, поскольку Windows предшествовала 80286 и работала на исходном 8086. COMMAND.COM можно было запускать как текстовое приложение, однако большинство приложений DOS запускали полноэкранный сеанс и взять под контроль пользовательский интерфейс.

Вероятно, поэтому у Windows 1.0 есть проблемы с более поздними версиями DOS, поскольку она, вероятно, берет под контроль внутренние структуры в DOS, чтобы выполнять пограничную магию на процессоре, у которого не было концепции защиты памяти.

Еще одна странность заключается в том, что в этой версии Windows на самом деле нет «окон». Вместо этого приложения располагаются плитками, и только диалоговые окна отображаются как свободно плавающие окна. Перекрывающиеся окна появятся в 2.0, но из API ясно, что они, по крайней мере, были запланированы в какой-то момент. Наиболее примечательно то, что вызов функции CreateWindow() имеет аргументы для координат x и y.

По моему мнению, Microsoft хотела избежать гнева Apple, которая встала на тропу юридической войны любой компании, которая слишком точно копировала пользовательский интерфейс тогдашнего нового Apple Macintosh. По сравнению с более поздними версиями также почти нет включенных приложений. Наиболее известные приложения, которые были включены: БЛОКНОТ, PAINT, WRITE и CARDFILE.

В то время как NOTEPAD практически не изменился по сравнению с его современной версией, Write лучше всего рассматривать как урезанную версию Word, и он останется опорой до Windows 95, где он был заменен Wordpad. CARDFILE также был цифровым Rolodex. CARDFILE оставался частью установки по умолчанию до Windows 3.1 и оставался на компакт-диске для 95, 98 и ME, прежде чем полностью исчезнуть.

PAINT, с другой стороны, полностью отличается от приложения Paintbrush, которое станет основой. В частности, он ограничен монохромной графикой, а файлы сохраняются в формате MSP. Частично это связано с ограничениями Windows API той эпохи: для рисования растровых изображений на экране Windows предоставила Display Independent Bitmaps или DIB. В них не было концепции палитры, и они были ограничены 8 цветами, которые Windows использует как часть палитры EGA. Поддержка цвета, по-видимому, была поздним дополнением к Windows и, по-видимому, не была полностью реализована до Windows 3.0.

Paintbrush (и более поздняя версия Paint со сбивающим с толку названием) на самом деле была сторонним приложением, созданным ZSoft, которое имело версии для DOS и Windows 1.0. ZSoft Paintbrush был очень похож на то, что поставлялось в Windows 3.0, и использовал некоторые технические хитрости, чтобы воспользоваться преимуществами полной палитры EGA.

Закончив этот беглый обзор, давайте вернемся к HELLO.C, что связано с установкой SDK.

Windows SDK и Microsoft C 4.0

Установка Windows SDK — это своего рода опыт. Большая часть документации Microsoft той эпохи утеряна, но в Музее OS/2 есть отсканированные копии некоторых эталонных подшивок, а на втором диске в SDK есть как файл README, так и установочный пакетный файл, который удалось большая часть необходимой информации.

В отличие от более поздних версий SDK, за предоставление компилятора отвечал программист. Официально Microsoft поддерживает следующие инструменты:

  • Ассемблер макросов Microsoft (MASM) 4
  • Microsoft C 4.0 (не путать с MSC++4 или Visual C++)
  • Microsoft Паскаль 3.3

Неофициально (и не подтверждено) существовали версии Borland C, которые также можно было использовать, хотя они не тестировались и, похоже, не были задокументированы, кроме некоторых заметок в USENET. Что еще более интересно, все вышеперечисленные инструменты были компиляторами для DOS и не имели какой-либо конкретной поддержки для Windows. Вместо этого в SDK был поставлен заменяющий компоновщик, который мог создавать новые исполняемые файлы Windows 1.0 «NE» — исполняемый формат, который также использовался в ранних версиях OS/2, прежде чем был заменен переносимыми (PE) и линейными исполняемыми файлами (LX) соответственно.

Для компиляции HELLO.C был установлен Microsoft C 4.0. Как и Windows, MSC можно было запустить с гибкого диска, хотя и с большим количеством операций подкачки дисков. Установщик не предоставляется, вместо этого в сохранившихся PDF-файлах есть несколько страниц команд COPY в сочетании с изменениями в AUTOEXEC.BAT и CONFIG.SYS для установки на жесткий диск. Также в этот момент я установил SLED, полноэкранный редактор, поскольку DOS 3.3 поставляется только с EDLIN. EDIT не появится до DOS 5.0

После долгой загрузки диска и устранения неполадок мне удалось скомпилировать быструю и грязную программу Hello World для DOS. Еще одна интересная особенность MSC 4.0 заключалась в том, что он не включал автономный ассемблер; В то время MASM был отдельным розничным продуктом. Разобравшись с компилятором, пришло время для SDK.

К счастью, имеется сценарий установки. Как и SELECT, он требовал перечисления множества папок, но в остальном был достаточно прост в использовании. По причинам, которые, вероятно, имели смысл только в 1985 году, и сценарий, и файл README находились на Диске 2, а не на Диске 1. Было подтверждено, что это не ошибка маркировки, поскольку сценарий немедленно запрашивает вставку Диска 1.

Сценарий установки копирует файлы с четырех из семи дисков перед возвратом в командную строку. Диск 5 содержит отладочную сборку Windows, которая примерно эквивалентна проверенным сборкам современной Windows. На дисках 6 и 7 есть примеры кода, включая HELLO.C.

После преодоления последнего препятствия было несложно получить скомпилированный файл HELLO.EXE.

Анализ HELLO.C

Я собираюсь пройтись по ним на высоком уровне, мой аннотированный hello.c содержит гораздо больше подробностей по всем этим пунктам.

Общие замечания

Теперь, когда мы можем его построить, пришло время взглянуть на то, что на самом деле составляет основу 16-битного приложения Windows. Первое существенное отличие, просто из-за возраста, заключается в том, что HELLO.C использует K&R C просто на основе предшествующей датировки функции ANSI C. Также ясно, что некоторые соглашения еще не были общепринятыми: например, в windows.h отсутствуют средства защиты включения.

указатели NEAR и FAR

long FAR PASCAL HelloWndProc(HWND, unsigned, WORD, LONG);

О боже, проклятие любого, кто кодирует в реальном режиме, ближний и дальний указатели — это «функция», о которой многие просто хотели бы забыть. Разница кажется простой: ближний указатель почти идентичен стандартному указателю в C, за исключением того, что он ссылается на память в пределах известного сегмента, а дальний указатель — это указатель, который включает в себя селектор сегмента. Ясно, верно?

Да я и не думал. Чтобы на самом деле понять, что это такое, нам нужно перейти к 20-битной карте памяти 8086. Внутренне 8086 был 16-битным процессором и, таким образом, мог напрямую адресовать 2¹⁶ бита памяти за раз, или всего 64 килобайта. Для преодоления 16-битного барьера памяти были предприняты различные уловки, такие как переключение банков или, в случае с 8086, сегментация.

Вместо прямого доступа ко всем 20-битным указателям памяти они делятся на селектор, формирующий основу данного указателя, и смещение от этой базы, позволяющее отображать полное адресное пространство. По сути, 8086 предоставил четыре независимых окна в системную память за счет использования сегмента кода (CS), сегмента данных (DS), сегмента стека (SS) и дополнительного сегмента (ES).

Таким образом, ближние указатели используются в тех случаях, когда данные или вызов функции находятся в одном и том же сегменте и содержат только смещение; они функционально идентичны обычным указателям C в данном сегменте. Дальние указатели включают в себя как сегмент, так и смещение, и у 8086 были специальные коды операций для их использования. Следует отметить дальний вызов, который автоматически выдвигает и извлекает сегмент кода для перехода между ячейками памяти. Это будет актуально позже.

HelloWndProc — это предварительное объявление для обратного вызова Hello Window, стандартной функции программирования Windows. Функции обратного вызова всегда должны были быть объявлены FAR, поскольку Windows должна была загружать правильный сегмент при переходе в код приложения из диспетчера задач. Отсюда далекое заявление. Кроме того, в Windows 1.0 и 2.0 были и другие правила, которые мы рассмотрим ниже.

Декларация WinMain:

int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
HANDLE hInstance, hPrevInstance;
LPSTR lpszCmdLine;
int cmdShow;

Соглашение о вызовах PASCAL

Все функции Windows API объявлены как соглашение о вызовах PASCAL, также известное как STDCALL в современной Windows. При нормальных обстоятельствах язык программирования C имеет номинальное соглашение о вызовах (известное как CDECL), которое в первую очередь относится к тому, как очищается стек после вызова функции. В объявленных CDECL функциях за очистку стека отвечает вызывающая функция. Это необходимо для работы функций vardiac (также называемых функциями, которые принимают переменное количество аргументов), поскольку вызываемый объект не будет знать, сколько из них было помещено в стек.

Недостатком CDECL является то, что он требует дополнительных инструкций пролога и эпилога для каждого вызова функции, тем самым замедляя скорость выполнения и увеличивая требования к дисковому пространству. И наоборот, соглашение о вызовах PASCAL оставляло очистку, выполняемую вызываемой функцией, и обычно требовался только один код операции для очистки стека в конце функции. Вероятно, из-за проблем с выполнением и дисковым пространством Windows стандартизировала это соглашение (и фактически до сих пор использует его в 32-разрядной Windows.

hPrevInstance

if (!hPrevInstance) {
/* Call initialization procedure if this is the first instance */
if (!HelloInit( hInstance ))
return FALSE;
} else {
/* Copy data from previous instance */
GetInstanceData( hPrevInstance, (PSTR)szAppName, 10 );
GetInstanceData( hPrevInstance, (PSTR)szAbout, 10 );
GetInstanceData( hPrevInstance, (PSTR)szMessage, 15 );
GetInstanceData( hPrevInstance, (PSTR)&MessageLength, sizeof(int) );
}

hPrevInstance десятилетиями был рудиментарным органом в современной Windows. Он устанавливается в NULL при запуске программы и не имеет смысла в Win32. Конечно, это не значит, что это всегда было бессмысленно. Приложения на 16-битной Windows существовали в общем супе из общего адресного пространства. Кроме того, Windows не сразу освобождала память, которая была помечена как неиспользуемая. Таким образом, части приложений могут оставаться резидентными по истечении срока службы приложения.

hPrevInstance был указателем на эти предыдущие экземпляры. Если ресурсы приложения по-прежнему зарегистрированы в диспетчере ресурсов Windows, оно может восстановить их, вместо того чтобы загружать их заново с диска. hPrevInstance был установлен в NULL, если предыдущий экземпляр не был загружен, тем самым предписывая приложению перезагрузить все, что ему нужно. Ресурсы регистрируются с помощью глобального ключа, поэтому попытка зарегистрировать один и тот же ресурс дважды приведет к ошибке инициализации.

У меня также сложилось впечатление, что ресурсы могут быть разделены между приложениями, хотя я не подтверждал это явно.

Распределение локальной/глобальной памяти

ПРИМЕЧАНИЕ. В основном взято из блога Рэймонда Чена, отличного чтения о том, почему Windows работает именно так, а не иначе.

pHelloClass = (PWNDCLASS)LocalAlloc( LPTR, sizeof(WNDCLASS) );
LocalFree( (HANDLE)pHelloClass );

Другая концепция, которая по существу исчезла, заключается в том, что выделение памяти классифицировалось либо как локальное для приложения, либо как глобальное. Из-за сегментированной архитектуры приложения имеют несколько куч: локальную кучу, которая инициализируется программой и существует в локальном сегменте данных, и глобальную кучу, для доступа к которой и из нее требуется дальний указатель.

У каждого исполняемого файла и библиотеки DLL были свои собственные локальные кучи, но глобальные кучи могли быть разделены между процессами и, насколько я могу судить, не освобождались автоматически после завершения процесса. HEAPWALK можно использовать, чтобы посмотреть, кто что выделил, и найти утечки в адресном пространстве. Его также можно было комбинировать с SHAKER, который переупорядочивал блоки воспоминаний, пытаясь избавиться от жуков. Это похоже на более современные инструменты, такие как valgrind в Linux или инструменты тестирования приложений Microsoft.

MakeProcInstance

lpprocAbout = MakeProcInstance( (FARPROC)About, hInstance );

О боже, это настоящая вонючка и совершенно ненужная. MakeProcInstance даже не появился в Windows 3.1, и все его существование связано с тем, что Microsoft забыла подробности своей операционной среды. Чтобы объяснить, нам нужно немного углубиться в программирование сегментированного режима.

Целью MakeProcInstance была регистрация функции, пригодной для использования в качестве обратного вызова. Только функции, помеченные MPI или объявленные как EXPORT в файле модуля, могут безопасно вызываться через границы процесса. Причина этого в том, что Windows необходимо зарегистрировать сегмент кода и сегмент данных в глобальном хранилище, чтобы безопасно выполнять вызовы функций. Помните, у каждого приложения была своя локальная куча, которая располагалась в собственном селекторе в DS.

В реальном режиме выполнение CALL FAR для перехода к дальнему указателю автоматически толкает и извлекает сегмент кода по мере необходимости, но сегмент данных остается неизменным. Таким образом, требовался механизм для хранения дополнительной информации, необходимой для поиска локальной кучи. Пока это звучит относительно разумно.

Проблема в том, что в 16-битной Windows это инвариант: DS = SS…

Если вы программист реального режима, это может прояснить, к чему я клоню. Селектор сегмента стека используется для обозначения того, где в памяти находится стек. SS также был помещен в стек во время вызова функции через границы процесса вместе с предыдущим SP. Вы можете начать понимать, почему MakeProcInstance становится совершенно ненужным.

Вместо того, чтобы нуждаться в глобальной системе регистрации для вызовов функций, приложение может просто посмотреть на базовый указатель стека (bp) и извлечь оттуда предыдущую SS. Поскольку SS = DS, предыдущий сегмент данных фактически был сохранен, и регистрация не требуется, просто изменение того, как Windows обрабатывает эпилоги и прологи функций. На самом деле это было обнаружено третьей стороной, и Майкл Гири выпустил инструмент FixDS, который переписал код функции, чтобы делать то, что я только что описал. Microsoft в конечном итоге внедрила его исправление непосредственно в Windows, и MakeProcInstance исчез как необходимость.

Другие странности

Из блога Рэймонда Чена и других источников один интересный аспект 16-разрядной Windows состоял в том, что она была фактически разработана с возможностью того, что приложения будут иметь свое собственное адресное пространство, и ходили разговоры о том, что Windows будет перенесена для работы поверх XENIX, Microsoft. Операционная система на базе UNIX. Неясно, имел ли OS/2 Presentation Manager общий код с 16-битной Windows, хотя некоторые аспекты дизайна и названия API были тесно связаны друг с другом.

Судя по дизайну 16-битной Windows и поэкспериментировав с ней, становится ясно, что на самом деле это был защитный режим на 80286, который иногда называют режимом сегментированной защиты. В защищенном режиме 286, несмотря на то, что процессор был 32-битным, адресное пространство памяти по-прежнему было сегментировано на 64-килобайтные окна. Основное отличие заключалось в том, что селекторы сегментов стали логическими, а не физическими адресами.

Если бы 80286 действительно преуспел, 32-разрядная Windows была бы практически идентична 16-разрядной Windows из-за того, как работал этот процессор. По правде говоря, отдельные адресные пространства должны были ждать появления 80386 и Windows NT, и эта потенциальная возможность так и не была использована. В 80386 было снято ограничение в 64 килобайта и введено плоское адресное пространство посредством подкачки, что сделало процессор x86 более совместимым с другими архитектурами.

Обратная совместимость в Windows 3.1

Хотя обратная совместимость Microsoft стала легендой, на самом деле она не существовала до Windows 3.1 и более поздних версий. Поскольку приложения Windows 1.0 и 2.0 работали в реальном режиме, они могли напрямую манипулировать оборудованием и выполнять операции, которые в защищенном режиме завершались бы сбоем.

Первоначально Microsoft выпустила Windows 286 и 386, чтобы добавить поддержку 80286 и 80386, функции, которые будут объединены в Windows 3.0 как стандартный режим и расширенный режим 386 вместе с устаревшей поддержкой «реального режима». Из-за того, что части операционной системы работают в защищенном режиме, многие трюки, которые могут выполнять приложения, могут привести к общему сбою защиты и простому сбою. Это не считалось проблемой, поскольку ранние версии Windows не пользовались популярностью, а Microsoft фактически прекратила поддержку приложений 1.x и 2.x в Windows 95.

Windows for Workgroups была установлена ​​на новую виртуальную машину, вместе с ней были скопированы HELLO.EXE и еще два примера приложений, CARDFILE и FONTTEST. При загрузке Windows не разочаровала, сразу же выдав предупреждение о совместимости.

Принятие предупреждения о том, что все три приложения работали нормально, хотя и с неправильным разрешением из-за передачи 0,0 в CreateWindow().

Тем не менее, здесь есть еще кое-что, что нужно изучить. Windows 3.1 SDK включает утилиту, известную как MARK. MARK использовался, как следует из названия, чтобы пометить устаревшие приложения как пригодные для работы в защищенном режиме. Это также может позволить использовать шрифты TrueType, функция, представленная еще в Windows 3.0.

Эффект ясен, HELLO.EXE теперь отображается в шрифтах TrueType. Причина, по которой шрифты TrueType не включаются сразу, можно увидеть в FONTTEST, где системный шрифт теперь перекрывает несколько полей диалога.

Теперь вопрос заключался в том, можем ли мы пойти дальше?

35 лет спустя…

Как отмечалось ранее, в Windows 95 прекращена поддержка двоичных файлов 1.x и 2.x. Однако этого нельзя сказать о Windows NT, на которой основаны современные версии Windows. Однако запуск 16-разрядных приложений осложняется тем, что NTVDM недоступен в 64-разрядных установках. Таким образом, была установлена ​​свежая копия 32-разрядной версии Windows 10.

Некоторую боль пришлось пережить, убедив Windows, что я не хочу использовать учетную запись Microsoft для входа в систему. Вставив ту же дискету, которая использовалась в предыдущем тесте, я дважды щелкнул HELLO, и появился установщик компонентов с просьбой установить NTVDM. После установки NTVDM вторая попытка показывает, что да, можно запускать приложения Windows 1.x в Windows 10.

FONTTEST также работал без проблем, хотя шрифты TrueType из Windows 3.1 исчезли. CARDFILE загрузился, но сразу умер с ошибкой инициализации. Я попытался отладить проблему и обнаружил, что WinDbg по крайней мере частично поддерживает работу с этими древними двоичными файлами, хотя история о том, почему умирает CARDFILE, должна подождать еще один день.

В заключение …

Надеюсь, вам понравился этот взгляд на древнюю Windows и HELLO.C. Я буду рад ответить на вопросы, и следующая тема, которую я, вероятно, затрону, — это более глубокий анализ различий между Windows 3.1 и Windows для рабочих групп в сочетании с демонстрацией работы сети в этих версиях.

Приветствуются любые отзывы о статье или видео, которые помогут мне улучшить мой контент в будущем.

До скорого,

73 NCommander

::КОНЕЦ ЛИНИИ::