Смекни!
smekni.com

Ознакомление с приложениями Windows (стр. 11 из 18)

· приложение содержало дополнительные данные, называемые ресурсами приложения (не путать с ресурсами компьютера или операционной системы). В виде ресурсов приложения могли выступать пиктограммы, курсоры, меню, диалоги и пр., использовавшиеся приложением. Эти ресурсы включались в специальном формате в файл образа задачи.

Обычная DOS–задача не могла делать таких вещей. Поэтому в Windows был принят новый формат выполняемого файла, а для нормального построения образа задачи пришлось изменить стандартный компоновщик (linker) так, чтобы он мог использовать информацию об экспортируемых и импортируемых функциях и собирать выполняемые файлы в новом формате. Соответственно, такой компоновщик нуждался в информации об экспортируемых и импортируемых функциях, а также о некоторых дополнительных параметрах Windows–приложения. Чтобы предоставить эту информацию компоновщику надо было написать специальный текстовой файл — файл описания модуля (def–файл).

В файле описания модуля перечислялись имена экспортируемых функций, имена импортируемых и библиотеки, содержащие функции, подлежащие импорту, задавался размер стека, давалось короткое описание приложения и пр. Это было не слишком удобно, так как при вызове какой–либо новой функции из Windows API (а их более 1000), необходимо было добавлять ее имя в файл описания модуля.

В тех случаях, когда приложение нуждалось в собственных ресурсах, необходимо было отдельно описать эти ресурсы в специальном текстовом файле описания ресурсов (rc–файле). Вместе с компиляторами до сих поставляется специальный компилятор ресурсов RC.EXE, который воспринимает файл описания ресурсов, компилирует его и создает файл ресурсов приложения (res–файл). Затем тот–же компилятор ресурсов может объединить уже построенный выполняемый файл с файлом ресурсов приложения.

Таким образом построение приложения состояло из следующих этапов:

· разработка исходных текстов — .c, .cpp, .h, .hpp, .rc и .def файлы;

· компиляция исходного текста программы — из .c и .cpp получаем .obj (иногда .lib);

· компиляция ресурсов приложения — из .rc получаем .res;

· компоновка приложения — из .obj и .lib получаем .exe;

· встраивание ресурсов приложения в выполняемый файл — из .exe и .res получаем рабочий .exe.

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

Следующий этап

Очевидно, что необходимость перечислять кучу имен функций в файле описания приложения никого не приводила в восторг. Поэтому на следующем этапе была включена поддержка Windows–приложений непосредственно в компиляторы[17]. Для этого было добавлено ключевое слово _export (иногда __export), которое применяется при описании экспортируемых функций непосредственно в тексте C–программы. Для таких функций компилятор включает в объектный файл специальную информацию, так что компоновщик может правильно собрать выполняемый файл. Так, например, было сделано в первом примере для оконной процедуры:

LRESULT WINAPI _export proc( ...

Помимо экспортированных функций, в файле описания приложения было необходимо перечислять все импортированные функции. А этот список был самым большим (Windows экспортирует более 1000 функций, которые могут импортироваться приложениями). Для решения этой задачи был изменен формат библиотек объектных файлов, так что появилась возможность описать имена функций, экспортированных другими модулями. Так компоновщик задачи может определить, какие функции импортируются и из каких модулей.

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

Библиотеки со списками функций, которые можно импортировать из Windows (то есть экспортированных компонентами Windows) входят в состав всех компиляторов. Иногда может возникнуть необходимость использования нестандартного компонента (или собственной динамически загружаемой библиотеки), для которых соответствующей библиотеки с ссылками нет. В таком случае можно воспользоваться специальным приложением — implib.exe — которое входит в состав большинства компиляторов (если его нет в составе компилятора, то значит его возможности реализованы в каком–либо другом инструменте, как, например, wlib.exe в Watcom C/C++). Implib позволяет по имеющемуся файлу динамически загружаемой библиотеки (.dll) или файлу описания проекта модуля библиотеки (.def), содержащему список экспортированных функций, построить библиотечный файл (.lib), с ссылками на функции библиотеки.

Первоначально, стремясь максимально уменьшить время загрузки модулей в память, при экспортировании функций им присваивались уникальные номера (назначаются разработчиком, либо просто по порядку). Конкретная функция однозначно определяется именем экспортирующей библиотеки динамической загрузки и идентификатором функции. Для задания идентификаторов экспортируемых функций используется файл описания модуля. Однако использование номеров вместо имен является не слишком удобным для человека, поэтому в Windows используются оба метода — функции доступны как по их идентификаторам, так и по их именам. Для Windows API общепринятым методом считается использование идентификаторов — Microsoft следит за тем, что бы все документированные функции сохраняли свои идентификаторы. А в Win32 API предполагается использование имен; более того, Microsoft не гарантирует, что идентификаторы документированных функций не будут изменяться.

Построение приложения сохранило в себе все шаги, однако создание файла описания приложения стало необязательным. При разработке приложений для Win32 API с файлами описания модуля практически не приходится иметь дело, он используется в очень редких случаях для построения динамически загружаемых библиотек. В настоящее время и в случае Windows API, и в случае Win32 API этот файл создается только если необходимо обеспечить нестандартную компоновку модуля, имеющего, например, разделяемые сегменты данных.

Современные компиляторы и системы поддержки проектов фактически остались в рамках рассмотренного порядка построения приложения. Небольшие изменения реализованы в разных компиляторах независимо друг от друга. Так, иногда включение файла ресурсов приложения в выполняемый модуль выполняется не компилятором ресурсов, а непосредственно компоновщиком; в других случаях специальные редакторы ресурсов позволяют обойтись без построения файла описания ресурсов (.rc), а создать непосредственно файл ресурсов приложения (.res). Особенно часто это делается в системах визуального программирования.

Если вы используете какую–либо систему поддержки проектов (Watcom IDE, Microsoft Developer Studio, Borland IDE, Symantec IDE и пр.) — а скорее всего это именно так — то вы должны только проследить за тем, что бы необходимые файлы были включены в проект. Система сама отследит, как и когда должен использоваться тот или иной исходный файл.

Обычно в проект включаются следующие файлы:

· исходные тексты на C и C++ — файлы с расширениями .c, .cpp, .c++;

· файл, содержащий ресурсы приложения, как правило только один, либо .rc либо .res;

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

· дополнительные объектные файлы .obj — отдельно включаются очень редко;

· файл описания модуля .def, только один, часто только при желании описать нестандартные параметры компоновки (см. ниже, в описании этого файла).

Файл описания модуля (.def)

Файл описания модуля (приложения) содержит обычный текст, который можно написать любым текстовым редактором. В качестве примера рассмотрим один из файлов описания модуля, использованный для построения приложения testapp.exe:

NAME TESTAPP
DESCRIPTION 'TESTAPP - test application'
EXETYPE WINDOWS
PROTMODE
STUB 'WINSTUB.EXE'
CODE LOADONCALL MOVEABLE
DATA MOVEABLE MULTIPLE
STACKSIZE 8192
HEAPSIZE 4096

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

Большинство компиляторов могут использовать собственные директивы, а также собственные расширения для параметров, задаваемых в директивах, не описанные в общих руководствах (как, к примеру, директива PROTMODE в приведенном примере). Кроме того список возможных директив в файлах описания модулей для Windows API и для Win32 API различается.

Windows API

Первая строка обычно задает имя модуля. Если вы строите приложение, то первой должна стоять директива NAME, а если динамически загружаемую библиотеку — LIBRARY. Указание обоих директив считается некорректным. Имя должно соответствовать имени файла, так для testapp.exe эта строка будет такой: NAME TESTAPP, а для mydll.dll — LIBRARY MYDLL.

LIBRARYdllname
NAMEexename

Обычно следующая строка — описание данного модуля. Она начинается с директивы DESCRIPTION, за которой следует произвольный текст, заключенный в апострофы: