Данный файл содержит базовое описание пакета DOSWIN32. Применение и распространение данного пакета, возможно ТОЛЬКО на основании лицензионного соглашения (см. license.txt в дистрибутиве пакета). Пакет может распространяться в виде одного/двух дистрибутивных архивов - DW32CORE.ZIP (end-user файлы) и (не обязательно) DOSWIN32.ZIP (development). В случаях оговорённых в лицензионном соглашении, файлы из DW32CORE.ZIP могут распространяться в составе других программных продуктов. ---------------------------------------------------------------------------- Общая концепция пакета DosWin32 та же что была у Borland Power Pack,- не столько поддержка dpmi аппликаций (хотя, в какой-то мере, присутствует и она), сколько возможность исполнения win32 console application в "голом" ДОСе. "Ядро" поддержки (то, что в BPP был 32rtm.exe) файл DOSWIN32.RTM. Он включает в себя так же и драйвера (для отладки под win9x и winnt), и dpmi-хост (для работы в dos'е). I. Основные ограничения и особенности (end-user information) ----- 1. Необходимая аппаратно-программная конфигурация: 1.1. Минимально необходимая аппаратная конфигурация - x486 (не SX) и не менее 16MB ОЗУ. 1.2. В качестве "базовой" ОС может использоваться MS-DOS v7 (тот, который поставляется совместно с Windows9x) и (в отладочном варианте) Dos-Box Win9x и Dos-Box WinNT (NT4SP3 или старше). 1.3 DosWin32 работает так же с MS-DOS v5.x, v6.x со следующими ограничениями: Работа идёт без использования "длинных" имён файлов (LFN). Не поддерживается FAT32 (поскольку DOS о них не знает). Особенность при работе под MS-DOS v5: При загрузке "в резидент" вместо сообщения 'DosWin32 resident' выводится сообщение 'load DosWin32 resident' после которого выводится обычный заголовок command.com или (при неудачном запуске) причина отказа от загрузки. 1.4 Известные "несовместимости": Старые версии SBLive! (драйвер pci-audio карт от Creative Inc.) ----- 2. Для запуска аппликаций со stub'ом "автозагрузки" эмулятора (см. описание UniLink) файл DOSWIN32.RTM должен быть где-то в "пути". ----- 3. Каталог в котором находится DOSWIN32.RTM рассматривается как SystemDirectory (и WindowsDirectory) для эмулируемой win32-подсистемы. ----- 4. Используется не больше 1Гб :) физической памяти, однако задаче предоставляется почти 3Гб виртуального АП. Это - под DOS'ом. Ну а под win9x/winnt - сколько отдадут :). Работа в DOS'е возможна как в "голом" DOS'е, так и при загруженном HIMEM и при HIMEM+EMM386. Последний вариант самый медленный, но разница крайне невелика. ----- 5. Если физической памяти (в ДОСе) недостаточно, то можно использовать свопинг. При необходимости его использования, достаточно просто создать файл потребного размера. Наличие этого файла при загрузке ядра автоматически интерпретируется как необходимость инициализации подсистемы свопинга. Нижняя граница размера файла 0.5Mb (иначе не оправдываются накладные расходы), верхняя определяется ДОСом,- 2ГБ. По умолчанию ищется файл DOSWIN32.SWP в ТЕКУЩЕЙ директории, однако можно указать и любой другой. Для этого достаточно задать environment переменную DW32SWP. ВНИМАНИЕ: В environment переменной должен быть указано ПОЛНОЕ (с путём) имя файла. ПРИМЕЧАНИЕ: Свопингом следует пользоваться только когда другого выхода нет (он в ДОСе очень медленный). Для того, что бы получить минимально возможное "торможение" файл свопинга желательно размещать на диске с размером кластера 4kB (8 секторов). ----- 6. CAD (Ctrl-Alt-Del) "заблокирован" всё время пока загружен эмулятор. Так же заблокирована работа с "фантомным" флоппи-приводом (если он есть в системе). ----- 7. Не забывайте что мышиный драйвер (если он нужен), кирилизатор (или другой "хукер" клавиатуры и smartdrv (если планируется большая работа с файлами) надо грузить ДО эмулятора. ----- 8. Запуск программ под эмулятором осуществляется либо автоматически (при компоновке их UniLink с ключом -ax[y]), либо "в ручную" (run32), либо (при загруженном в резидент DosWin32) автоматически для всех PE-аппликаций, ----- 9. Совместно с DOSWIN32.RTM распространяются 2 файла RUN32.EXE и RD32.EXE. Второй это "запускалка" td32 (исторически осталась :), а первый имеет два назначения. Его можно использовать для запуска отдельной PE-аппликации (без stub'а) - указанием имени файла аппликации в качестве первого параметра командной строки (остальные параметры будут переданы аппликации). А можно (что важнее) использовать для загрузки эмулятора в "резидентном" режиме (run32 без параметров). При загруженном эмуляторе в резидент ни run32 ни стабы уже не нужны - все запускаемые задачи анализируются и PE'шные файлы автоматически запускаются в win32 режиме). На самом деле это самый выгодный вариант, т.к. иначе для запуска каждой задачи требуется грузить/выгружать эмулятор. С другой стороны такой режим чуть-чуть замедляет обычные 16битовые задачи. Выгрузка из резидента осуществляется просто командой 'exit' :) ПРИМЕЧАНИЕ: разумеется, если запускаемая задача требует "внешние" dll's они должны быть где-то в пути. ----- 10. Программы требующие для своей работы registry используют файл DOSWIN32.RGD располагающийся в той же директории где и DOSWIN32.RTM. В случае, когда его нет, он создаётся автоматически при первом обращении. ВНИМАНИЕ: Нарушение структуры данного файла является фатальной ошибкой (ядро не будет грузиться). Справиться с этим (пока?) можно только одним способом - удалить файл. Так что, если он Вам нужен, не забывайте его бэкапировать :). ----- 11. Программы использующие "системный" ini-файл (win.ini) работают с файлом DOSWIN32.INI располагающийся в той же директории где и DOSWIN32.RTM. ----- 12. При загруженном эмуляторе (в резиденте или при работе "дочерних" задач) даже dos-задачам доступна работа с clipboard. ----- 13. Если задача "завершается" по exception, то информация о нём не только выводится на экран, но и МОЖЕТ сохраняться в файле. Для того, что бы это происходило должна быть определена environment переменная DW32LOG с ПОЛНЫМ именем файла сохранения. ПРИМЕЧАНИЕ: Если в качестве имени файла указана "*", то используется файл DOSWIN32.LOG в той директории, откуда был запущен эмулятор. Если же в качестве имени указана ".". то файл с таким именем создаётся в текущей (на момент запуска эмулятора) директории. ВНИМАНИЕ: Если перед именем введён '+' то он интерпретируется не как часть имени, а как признак того, что ВСЕ сообщения связанные с невозможностью загрузки задачи/библиотеки, так же помещаются в этот файл. Причём это происходит не только для "первичного" процесса, но и для всех последующих LoadLibrary/CreateProcess/etc. ----- 14. Если при загрузке программы обнаружены неотрезолвленные импорты, то на экран будет задан вопрос продолжать ли загрузку. Если ответ Y[es], а, в последующем, один из этих неотрезолвленных импортов будет вызван, то на экран (и в log, если он назначен) будет выдано сообщение информирующее о том, откуда и какой импорт был вызван. ----- 15. DosWin32 реализует поддержку работы с длинными именами (LFN) однако поскольку сам DOS не поддерживает работы с LFN, то их применение приводит к ухудшению буферизации и некоторому замедлению работы. С другой стороны, длинные имена сохраняются даже при работе старых 16'ти битовых программ :). Для полноценной работы с LFN должна быть ПРАВИЛЬНО определена текущая CodePage (для всех стран, кроме США - загружен country.sys). ВНИМАНИЕ: Не поддерживаются CodePage (cтраны) в которых используется DBCS. Если на компьютере используется именно такая CodePage, то работа с LFN обязательно ДОЛЖНА быть запрещена - иначе эмулятор просто не загрузится. Для отключения поддержки LFN следует установить environment переременную DW32LFN=- ----- 16. При работе с ресурсами текущая локализация "вычисляется" из кода страны полученного от DOS'а (country.sys). Однако при необходимости (например, для стран использующих более одного языка) можно изменить его задав environment переменную DW32RLC=nn, где nn младшие две цифры LANGID. В принципе, это аналогично заданию ResourceLocale в registry win32, только если там для (например) испанского будет значение 0000040A, то здесь следует указывать 0A. Если же указать значение 00, то язык "вычисленный" из кода страны игнорируется и ResourceLocale остаётся в состояние NEUTRAL. ВНИМАНИЕ: Заданный таким образом язык может иметь значение 00 или 09 (английский разрешён всегда) или же ДОЛЖЕН иметь ту же CodePage которая установлена в DOS'е. Если эти условия не выполнены environment переменная игнорируется. ----- 17. DosWin32 анализирует на загрузке environment переменную TZ. Если она наличествует и задана в формате xxx[+/-]n[n][yyy], то xxx интерпретируется как название таймзоны [+/-]n[n] как её смещение относительно UTC (в диапазоне -12 -:- +12 часов), а если присутствует yyy, то оно: а) интепретируется как название "летней" таймзоны; б) к заданному смещению добавляется +1 час. ВНИМАНИЕ: "Летнее" время не "привязано" к конкретным датам, просто если его имя указано в environment переменной, то считается, что на текущей загрузке должно применяться именно оно. --------------------------------------------------------------------------- --------------------------------------------------------------------------- II. Общие ограничения и особенности (development information) 1. Для запуска программ под отладчиком (пока - td32) можно либо запускать отладчик, а из-под него грузить задачу, либо "вешать" DOSWIN32.RTM в резидент (см. выше), либо использовать RD32.EXE входящую в комплект (см. ниже). ПРИМЕЧАНИЕ: Есть одна хитрость при запуске td32 в режиме эмуляции: когда td32 говорит 'error loading program' стоит переключиться на экран программы (по F5) - там будет причина. Ну, и то же самое, при "внезапном" завершении программы, когда Вы запускаете программу с неотрезолвленными импортами. ВНИМАНИЕ: В td32 есть несколько _грубейших_ ошибок (в части поддержки dpmi). Одна связна с пунктом меню SystemInformation, вторая с нажатием на правую кнопку мыши в окне отладчика, третья - при нормальном завершении процесса (или его "обресечивании" по CtrlF2 он теряет информацию о паузах, ну и так далее. Есть так же и несколько "несвязанных" (с dpmi), но не мене грубых). Кроме того, в td32 был код "заточенный" под ошибки 32rtm :). Борланду предложения по исправлениям и модификациям отправлены, однако они, в настоящее время не сопровождают данный продукт. Как ИНФОРМАЦИЮ о временном способе обхода этих ошибок можно использовать патчи и специальные dll, размещённые на ftp с которого распространяется и данный дистрибутив. ----- 2. При переключении контекстов (запуск "дочерних" процессов и/или работа с отладчиком) на настоящий момент сохраняется только "основной" контекст (включая fpp), а sse-extention не сохраняются даже если они присутствуют у процессора. Возможно это поведение изменится в последующих версиях - если будет потребность. ----- 3. Несколько отличается от BPP логика работы с мышью - каждая задача имеет "личное" состояние hide/show курсора. Управление курсором возможно ТОЛЬКО через ENABLED_MOUSE_INPUT (при старте задачи DISABLE). В общем - почти как в win32 :). ----- 4. Работа с registry 4.1 Ограничения практически те же что и в w9x, однако КРАЙНЕ не рекомендуется хранить там большие объёмы - это, всё-таки, ДОС и издержки получаются ощутимые. 4.2 Сохранение "регистри" на диск, дабы минимизировать накладные расходы, происходит либо по "явному запросу" (RegFlushKey - с любым валидным ключом), либо по завершении любой задачи. Естественно, сохранение происходит только в тех случаях, когда были модификации. 4.3 Файл НЕ "лочится" на время работы системы (иначе невозможно "предотвратить" запись, что может иногда требоваться. ----- 5. Реализация clipboard'а. Поддерживаются только форматы CF_TEXT и CF_OEMTEXT (из "стандартных") и RegisterClipboardFormat. Реализована поддержка клипбоарда для ДОСовских задач (в ситуации, когда ядро "в резиденте", ну или ДОСовский процесс запущен в качестве дочернего). ВНИМАНИЕ: Перекодировка между форматами не проводится - т.к. ANSI-codepage и OEM-codepage одинаковые. ПРИМЕЧАНИЕ: Поскольку ДОСовые задачи прежде чем пользоваться клипбордом проверяют наличие windows в enhanced моде, приходится отвечать им, что тут они. Версии 22.0 :) 5.1 Ограничения RegisterClipboardFormat: a. Максимальная длина имени - 62 символа (without 0) b. Максимальное число ОДНОВРЕМЕННО зарегистрированных форматов - 15 c. Клипбоадр "един", т.е. хранить в нём данные разных форматов ОДНОВРЕМЕННО - нельзя. --------------------------------------------------------------------------- --------------------------------------------------------------------------- III. System-specific (development information) 1. Идентификация. Если Вам нужно определить под DOSWIN32 работает программа или под Win32 (а это может быть необходимо - ниже), то простейший способ это GetVersion/GetVersionEx. 1.1. GetVersionEx При работе под DOSWIN32 поле szCSDVersion будет содержать строку 'DW32' (без апострофов, разумеется) - сиречь DosWin32 ;-), а dwPlatformId будет равен 3 (это отличие необходимо для корректной работы борландовской rtl, которая учитывает возможность сборки под 32rtm, частичная совместимость с которым оставлена). 1.2. GetVersion Позволяет определить и факт работы под эмулятором и его версию и даже где РЕАЛЬНО работает программа: if(dwVersion > 0xF0000000) { // emulator // dwWindowsMinorVersion -> EMULATOR_VERSION dwBuild = (DWORD)LOWBYTE(HIWORD(dwVersion)); dwExecMode = (DWORD)HIBYTE(HIWORD(dwVersion)) & 3; // 0 - in DOS, 1 - on win9x, 2 - on NT } ----- 2. При работе с memory mapped files, следует учитывать, что различные View НЕ когерентны, а "сброс" на диск View открытых в режиме FILE_MAP_WRITE осуществляется независимо от того, была ли модификация. Использования этого режима следует избегать при написании программ рассчитанных на работу (в том числе) под DosWin32 - могут быть потери в производительности. Вообще, поддержка FileMapping и, в особенности, "записываемого" View сделана только для возможности запуска "неадаптированных" программ (например, ms-link v7.1). Ограничения на lpBaseAddress в MapViewOfFileEx такие же как для NT. ----- 3. Есть не вполне тривиальная проблема с OEM-ANSI / ANSI-OEM конвертациями - как знают все писавшие консольные аппликации, входная строка приходит в ANSI, а выводить её (например, при сообщении "файл не найден") надо в OEM. Но при работе под DPMI ANSICP == OEMCP поэтому все перекодировки сведены к копированию "один в один" :). С другой стороны, данные иногда читаются из файла и их реально надо перекодировать. "Автоматического" решения этой проблемы придумать не удалось, однако какое-то решение есть. ЕСЛИ Вам нужна такая работа, то (в тех местах, где она нужна) используйте вместо OemToCar[Buff]/CharToOem[Buff], соответственно dpmiOemToChar[Buff]/dpmiCharToOemBuff. Эти ф-ции осуществляют реальное преобразование. Разумеется только для поддерживаемых языков - в текущей версии список 437/866/850/852/775. Если программа собирается не с помощью UniLink, то точки входа в эти процедуры Вы можете получить через GetProcAddress (к user32). Если же программа собирается с помощью UniLink то можно использовать "статическую" компоновку. При этом при работе в Win32 отличий не будет никаких, а при работе под DPMI будет производиться реальное преобразование. ВНИМАНИЕ: для того, чтобы программы со ссылками на эти функции нормально компоновались и ДО их использования НЕОБХОДИМО поставить в программе вызов функции dpmiNlsInit(). (прототипы всех ф-ций в ulnkhdr.h). После такого вызова программы будут адекватно работать как в DPMI-режиме, так и в обычном Win32. ----- 4 Особенности работы с длинными именами (и GetShortPathName/GetLongPathName). 4.1. Обычно консольные аппликации имеют !AreFileApisANSI. Однако, если Вы "вручную" в своей программе сделали SetFileApisANSI, то при работе под DPMI ничего не изменится. И если Вы подавали строки в ANSI, то при работе под DPMI Вы должны САМИ озаботиться перекодировкой (dpmiCharToOem[Buff]A). 4.2. Узнать есть ли поддержка lfn на текущей загрузке (это необходимо для программ умеющих работать с файлами и по длинным и по коротким именам) можно выполнив GetSystemInfo. Если поддержка включена, то в поле wReserved находиться сигнатура 'lf'. 4.3. Отличия в работе GetShortPathName (и GetLongPathName). 4.3.1. Всегда устанавливается ПРАВИЛЬНЫЙ код ошибки (дело в том, что Win32 устанавливает FILE_NOT_FOUND, даже когда на самом деле PATH_NOT_FOUND, а PATH_NOT_FOUND устанавливается только при отсутствующем диске). ВНИМАНИЕ: при задании в качестве входного имени директории (сиречь, имени кончающегося на '\\') и не найденной директории с таким именем, ошибка будет PATH_NOT_FOUND. А при попытке обращения к "недопустимому" устройству (см. ниже) будет ERROR_INVALID_DRIVE 4.3.2. Всегда (а не только при "меняющемся имени") проверяется наличие файла/пути (см. ниже. Кроме того GetLongPathName (в DOS и w9x) даёт "реальные" символы (в плане upper/lower case). 4.3.3. Всегда "разворачиваются" умолчания - т.е., при задании входной строки вида "." Вы получите что-то вроде "C:\\dir\\" (сиречь полный путь к текущей директории), а при строке вида "D:" полный путь к текущей директории на диске D:. Что открывает некоторые дополнительные возможности :) Другими словами полный эквивалент такой работы под Win32 можно получить ПОСЛЕДОВАТЕЛЬНЫМ вызовом GetFullPathName и GetShortPathName/GetLongPatName. Конструкции вида '.\' удаляются, а '..\' "обрабатываются". В общем, в выходной строке будет РЕАЛЬНОЕ полное имя (в формате 8.3 для GetShortPathName). 4.3.4. Ф-ции НЕ работают с "сетевыми" путями (ну откуда, под ДОСом сеть? :) и с путями заданными в виде "\\\\.\\A:" - такие пути подразумевают нефайловую работу. Кроме того, они работают ТОЛЬКО с устройствами поддерживающими direct-access. Сиречь попытка применить их к 'устройству', реализованному нестандартным драйвером, эффекта не даст. ВНИМАНИЕ: Функции НЕ работают с CD-дисками. Уж слишком много там разных форматов, а нужно это редко. Так что при попытке обращения к CD'шнику Вы получите ERROR_INVALID_DRIVE. Так же они не работают с SUBST'ed устройствами. 4.3.5. У GetShortPathName есть одно СУЩЕСТВЕННОЕ отличие от работы под Win32. ("нарваться" на него в старых программах невозможно - см. ниже, а вот в новых - если Вы будете пользоваться этой возможностью - следует предварительно проверить, что работа идёт под DosWin32.) Вполне очевидно, что просто GetShortPathName решает все проблемы с длинными именами для OPEN_EXISTING, однако не решает их для CREATE_NEW. Для того, что бы не приходилось в каждой аппликации изобретать какие-то способы решить эту проблему и сделана нижеописанная модификация. ЕСЛИ счётчик (szBuff) указан как отрицательный (ну оччень большой положительный :), то значением счётчика считается -(long)szBuff, а вот поведение ф-ции несколько меняется. После полной проверки наличия пути, при обработке ПОСЛЕДНЕГО элемента (это может быть не только файл, но и директория), алгоритм работы следующий: a) Если файл с таким именем (длинным или коротким) найден - обработка стандартная. б) Если файла с таким именем нет, а имя удовлетворяет критерию 8.3, то возвращается это имя (однако код ошибки устанавливается, чем можно воспользоваться для проверки "новизны" файла :). в) Если файла нет, а имя "длинное", то возвращается автосгенерённое короткое имя с гарантией того, что оно является уникальным (код ошибки также устанавливается). ПРИМЕЧАНИЕ: Имена возвращённые GetLongPathName при ОТКЛЮЧЕННОЙ поддержке lfn (на данной загрузке) НЕЛЬЗЯ использовать в файловых операциях (если, конечно, они "длинные"). ВНИМАНИЕ: GetShortPathName/GetLongPathName в NT будут работать не совсем аналогично тому как они ведут себя под ДОСом - например могут понимать и NTFS ;-). ПРИМЕЧАНИЕ: В NT GetLongPathName действует ТОЛЬКО в отношении имени файла и "неявной" части пути - всё что прописано (во входном имени) явно, таким и останется. Например, если задано "длинное" имя пути в uppercase, то оно не будет преобразовано в realcase. ПРИМЕЧАНИЕ: В NT4 имя остаётся в том виде, которое эквивалентно возвращаемому GetFullPathName. ----- 5. SetEnvironmentVariable для переменных класса '=C:' в Win32 позволяют задать "произвольное" значение и ошибка возникнет на ближайшей файловой операции с "неполным" путём. В DosWin32 ошибка возникнет уже при SetEnvironmentVariable если путь реально не существует. Другими словами, есть РЕАЛЬНАЯ синхронизация между переменными данного типа и CurrentDiretory указанного диска. ----- 6. Для всех "дисковых" операций (начиная с GetLogicalDrives) фантомный флоппи-драйв (если он присутствует в системе) "невидим". Это правило распространяется и на DOS-задачи пока активен эмулятор. ----- 7. Если используются ф-ции работы с ini-файлами, следует учитывать, что максимальная длина значения (строки) 2kB. Соответственно, если используются Get/Write-PrivateProfileStruct (зачем бы? :) то sizeof структуры должен быть МЕНЬШЕ 1kB. В отличие от Win32 все эти ф-ции устанавливают реальный код ошибки возвращаемый GetLastError. Общие ограничения при работе с ini-файлами: максимальный размер файла - 65kB (как в win9x) максимальная длина ИМЕНИ секции - 128 байт максимальная длина ИМЕНИ ключа - 128 байт максимальный размер ДАННЫХ в секции - размер файла - имя секции :) максимальная длина строки ЗНАЧЕНИЯ - 2kB максимальная длина СТРОКИ (включая комментарий) - 2560 байт ВНИМАНИЕ: При попытке записи "не лезущих" данных возникает ошибка (Win32 просто "молча" усекает) Если ini-файл содержит не валидные данные (неправильная структура "ДЕШИФРУЕМОЙ" строки, файл считается "битым" и работа с ним невозможна. Разумеется, "полное сканирование" не производится - только до того места, которое требуется в запрошенной операции. Точнее до конца соответствующей секции :). ----- 8. Поскольку адресное пространство "общее", то загрузка задач на "прописанные" в них адреса приводит к невозможности запуска "из-под" одной задачи другую, если эта другая собрана без релокаций (по умолчанию все exe'ники на одинаковом адресе). Типичный пример - запуск из-под FAR'а pkzip25/rar3. Для того что бы (по возможности) избежать такой ситуации память (адресное пространство) распределяется "сверху вниз", а задачи грузятся на "заказанные" адреса, только если у них нет релокаций. Такая логика чуть-чуть замедляет загрузку, однако приводит к тому, что случаев вида: 'Can't memory at requested address' становится существенно меньше. ВНИМАНИЕ: Если какая-то программа "закладывалась" на то, что её будут грузить на фиксированные адреса (но, при этом, содержала релокации), она может перестать работать. Я, правда, такое встречал только в "кракерских поделках", но чего не бывает:). ПРИМЕЧАНИЕ: Для возможности отладки на "привычных" адресах (ну и, запуска программ описанных в предыдущем абзаце :), сделана возможность "обхода" вышеописанной логики. Если у программы (библиотеки) стоит в заголовке бит IMAGE_FILE_AGGRESIVE_WS_TRIM (для ulink ключ -GF:AGGRESSIVE), то считается, что её можно грузить только на определённые в заголовке адреса (так же как и при "обрезанных" релокациях). Поскольку этим битом никто и никогда не пользуется :), а название похоже по смыслу, то... ----- 9. При "одноаргументной" форме запуска win32-задачи (с NULL в качестве lpApplicationName), в командной строке запускаемой задачи подставляется реальное (полное) имя exe'шника. ----- 10. SetActiveConsoleScreenBuffer имеет крайне ограниченную реализацию (только для поддержки "ленивых" программ использующих её для запоминания экрана на входе и восстановления на выходе). При её использовании с ScreenBuffer отличным от текущего, экран очищается (и устанавливается 3я видео мода). ----- 11. SetConsoleScreenBufferSize допускает только те размеры, которые поддерживаются bios'ом. ПРИМЕЧАНИЕ: Изменение размеров приводит к очистке экрана. --------------------------------------------------------------------------- --------------------------------------------------------------------------- IY. Дополнительные возможности (development information) Если Вы хотите "проверить" аппликацию под Win32, то это можно сделать, поменяв руками сигнатуру с 'PE' на 'BPE' или используя run32 из 'development' дистрибутива (doswin32.zip). ОДНАКО - при работе под NT (где dpmi-хост очень "слабенький") требуется, что бы был установлен крошечный "псевдодрайвер" (реально это корректор логической ошибки в r0 интерфейсе dpmi). Основные драйвера (как для nt так и для win9x) "встроены" в DOSWIN32.RTM. Драйвера НЕ надо прописывать в system.ini или регистри - они грузятся динамически. ПРИМЕЧАНИЕ: При сборке задачи UniLink такой режим (принудительное использование dpmi) может быть установлен линкером (-aX). ВНИМАНИЕ: Для работы под NT есть несколько замечаний: 1. Вряд ли будет работать под 3.51 :), а под nt4 тестилось только для SP6. 2. В NT, к сожалению, необходим кусочек r0-кода. Посему до первого запуска dpmi-задач использующих эмулятор кто-то с административными привилегиями должен запустить ntdinst (включен в поставку), которая установит "псевдодрайвер". После чего (для использования) никаких особых привилегий уже не требуется. 3. Поведение GetShortPathName/GetLongPathName не вполне адекватно работе в ДОСе/Win9x (используются совсем другие механизмы). ПРИМЕЧАНИЕ: Вообще-то nt-шный драйвер никому мешать не должен, а всех ресурсов "съедает" около 70 байт памяти :), но буде захочется его снести - либо удалить файл, либо удалить запись в registry (и перезагрузиться). 4. В NT _крайне_ убог механизм поддержки нестандартных расширений (точнее - его нет вообще :), посему поддержка ошибок FPP не работает для задач запретивших (виртуальные) прерывания. Кроме того, если из-под 32хбитовой dpmi-задачи запустить 16-битовую ДОСовую, которая породит ошибку fpu, то обработать её она уже не сможет (ошибка в логике ядра). Впрочем, эта ошибка есть и в win9x :), но там её хоть исправить можно. В NT же будет сообщение из драйвера о такой ошибке и задача будет снята на ближайшем int21 (раньше не получается). Если это была не "одиночная" задача а dos-сессия, то (до возврата управления в эмулятор) может быть восстановлена не вся таблица векторов. Так что, лучше в такой ситуации не продолжать сессию, а сказать exit. --------------------------------------------------------------------------- V. Расширения ядра (development information) 1. Возможность добавления своего "резолвинга" отсутствующих импортов. (В текущей модели предполагается, что эта возможность нужна только "временно" - пока соответствующая ф-ция не будет включена в ядро.) Ниже "системными dll" называются стандартные Win32 system-dll's такие как kernel32.dll. 1.1. Все имеющиеся (на данный момент) системные dll "встроены в ядро". Это приводит к незначительному отличию от работы задачи под DosWin32 - для всех таких dll GetModuleHandle возвращает HINSTANCE, даже если не было соответствующего вызова LoadLibrary. 1.2. Полный список системных dll в текущей версии: kernel32.dll, user32.dll, advapi32.dll, ole32.dll, shell32.dll, mpr.dll, ntdll.dll, version.dll, winmm.dll, oleaut32.dll, rpcrt4.dll, winspool.drv, gdi32.dll (последняя - просто, что бы не было попыток загрузки "нормальной" :). 1.3. Разумеется, в каждой из них имеется НЕ полный набор точек входа - для консольных задач - полный и не нужен, а места требуется много. Ну и, кроме того, многие точки входа "реализованы" в виде "заглушек" (ERROR_CALL_NOT_IMPLMENTED, ERROR_INVALID_HANDLE, etc). Но это-то привычно для любого пишущего под Win32 :). 1.4. В случае если каких-то точек входа в системных dll не хватает (не важно, в одной или в нескольких) существует два решения. Первое и очевидное использовать GetProcAddress. Однако такой подход возможен только для новых задач, и даже для них порождает лишний system-dependet код, что нежелательно. Если требуемая точка входа (потенциально) нужна многим задачам, она будет внесена в ядро следующих билдов. А для того, что бы была возможность проверки работы без "ожидания" выхода следующего билда предназначен механизм описанный в 1.5. 1.5. Для создания отсутствующих точек входа в системных dll предназначена специальная dll (DOSWIN32.EXT) находящаяся в %SystemDirectory%. Наличие этой dll в %SystemDirectory% НЕ обязательно, однако если она там присутствует, то она грузится для любого процесса (кроме случая описанного в 1.7) и, если GetProcAddress из ЛЮБОЙ системной dll должен вернуть NULL, производится просмотр её экспортов. Если экспорт с соответствующим именем (или номером) найден - возвращается он. ВНИМАНИЕ: Если в %SystemDirectory% находится файл с именем DOSWIN32.EXT и он НЕ является валидной dll, не сможет запуститься ни одна задача (в которой не "задавлено" её использование). 1.6. DOSWIN32.EXT это почти обычная dll, с одной особенностью - импорты из неё (если они есть :) могут относится ТОЛЬКО к "встроенным" dll. Основное назначение этой dll - дополнительные "заглушки" (пока они не внесены в ядро). ВНИМАНИЕ: Если Вы собираете эту dll посредством Borland's ilink32, учтите, что в нём есть ошибка - он проставляет наличие EntryPoint даже когда её реально нет. 1.7. Если Вашей задаче НЕ требуется использование extension dll (DOSWIN32.EXT) даже когда она присутствует, то можно чуть-чуть ускорить загрузку (и уменьшить расход памяти) указав ядру, что для данной задачи она гарантированно не требуется. В качестве такого флага используется атрибут IMAGE_FILE_BYTES_REVERSED_LO, который игнорируется Win32 на x86 :). ПРИМЕЧАНИЕ: Некоторые старые линкеры (например, tlink32 до версии 5.0 от борланда или wlink от ваткома) ставили этот бит всегда. Новые линкеры (ilink32 от bcb >= 4, link от vc >=4) его не ставят. Поставить этот бит при использовании UniLink можно просто ключом линкера (-ay вместо -ax / -aY вместо -aX), а при использовании любого другого линкера либо "руками" либо написав тулзу из 5ти строк :). ----- 2. "Псевдосистемные" dll's. Если требуемой _системной_ dll нет в ядре, можно, конечно, просто сделать dll с таким именем (например, commdlg.dll :), однако такой подход слегка "опасен" - поскольку имя dll совпадает со стандартной Win32 системной dll существует риск, что юзер скопирует её в системную директорию Win32 и... Для того, что бы этого избежать сделано следующее: При _любом_ LoadLibrary (в том числе для статических dll), если путь не задан явно, а расширение файла '.DLL' (или не указано) в момент "просмотра" %SystemDirectory% СНАЧАЛА там смотрится файл с заданным именем и расширением '.RTM', и лишь потом '.DLL'. Это относится ТОЛЬКО к %SystemDirectory%. Такое поведение отличается от "чистого" Win32, однако оно позволяет избежать вышеописанной опасности. ----- 3. Для того, что бы не приходилось писать код при создании DOSWIN32.EXT или '.RTM'-файлов содержащих только "заглушки" предназначен механизм "служебных" forward-exports. Другими словами, как DOSWIN32.EXT, так и ".RTM-заменитили" могут представлять собой export-only dll :) сиречь содержать только секцию экспорта. Если, конечно, Вы используете для их сборки UniLink (мне не известен другой линкер позволяющий такое реализовать). 3.1. Создание служебных forward-exports производится, как и для обычных, forward-exports через def-file, причём имя dll "", а экспорт указывается по номеру в котором закодированы параметры "заглушки". Поскольку "ручное" кодирование чревато ошибками (да и сложно/неудобно) в каталоге SAMPLES прилагается программа makedef.exe, которая делает такой def-файл из текстового описания "заглушек". Формат описателей в файле makedef.txt. ----- 4. Работа с плагинами (extention) ЧУЖИХ пакетов. Если (для целей предварительной отладки) нужно, что бы LoadLibrary в некоей задаче (имеющейся только в виде исполняемого модуля) проходило даже при наличии unresolved import's, достаточно поставить в exe-файле, НЕИСПОЛЬЗУЕМЫЙ Win32 бит (бит 0x40 в FileHeader.Characteristics). Как известно, из документации win32, это то ли 'reserved for future use', то ли IMAGE_FILE_16BIT_MACHINE :). Но не смотрит и не знает его ни одна из win32-систем. Поставить бит можно либо любым бинарным редактором (смещение 0x16 от сигнатуры), либо, опять, же написав тулзу из 5 строк :). 4.1. Наличие этого флага приводит, так же, к тому, что все LoadLibrary[Ex] в которых имя задано полным путём, предварительно (автоматически) выполняют GetShortPathName на это имя. ----- 5. "Заявки" на добавление "заглушек" и ф-ций в ядро, следует отправлять на support@doswin32.com, с указанием пакета, для которого они понадобились. ----------------------------------------------------------------------------