Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 11, М.: Диалог-МИФИ, 1993, 269 стр.

[Назад] [Содеожание] [Дальше]

1.2. Простейшее приложение Windows

В этом разделе мы создадим простейшее приложение Windows. Оно будет мало напоминать "настоящие" приложения, которые поставляются вместе с Windows, но на данном этапе наша основная задача - научиться создавать файл загрузочного модуля приложения Windows с использованием системы разработки Borland C++ версии 3.1.

Функция WinMain

Любая программа MS-DOS, составленная на языке программирования C или C++, должна содержать функцию с именем main. Эта функция первой получает управление сразу после того, как специальный стартовый модуль устанавливает расположение стека и кучи программы, а также выполняет все необходимые инициализирующие действия.

Если вы создаете приложение Windows с использованием языка программирования C или C++, прежде всего вы должны создать функцию с именем WinMain, которая является аналогом функции main в программах для MS-DOS.

Функция WinMain должна быть определена следующим образом:

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
// Тело функции
}

В определении функции WinMain использованы, вероятно, незнакомые вам типы - PASCAL, HINSTANCE, LPSTR. Эти типы описаны в include-файле с именем windows.h, который поставляется вместе с компилятором и должен быть включен в исходный текст программы при помощи оператора #include.

Тип PASCAL определен в файле windows.h следующим образом:

#define PASCAL  _pascal

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

Это связано с тем, что такая функция должна сама освобождать стек перед возвратом. Если перед вызовом функции, описанной с ключевым словом _pascal или PASCAL, записать в стек неправильное количество параметров, перед возвратом из функции обязательно произойдет неправильное освобождение стека.

В частности, функция WinMain должна использовать ровно четыре параметра, как показано в предыдущем примере. Привычная вам функция main программы MS-DOS могла либо совсем не иметь параметров, либо использовать параметры argc и argv.

Функция WinMain возвращает значение типа int, что позволяет передать операционной системе Windows или отладчику код завершения приложения.

Первые два параметра имеют тип HINSTANCE, который в Windows версии 3.1 является 16-разрядным идентификатором. Не следует однако думать, что тип HINSTANCE эквивалентен типу int. Изучив include-файл windows.h, вы сможете убедиться в том, что это не так.

Параметр с именем hInstance является идентификатором приложения. Любое приложение перед запуском получает свой уникальный идентификатор, который передается ему через параметр hInstance.

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

Так как Windows - мультизадачная среда, вы можете запустить одновременно несколько приложений, и каждое приложение будет иметь свой, уникальный идентификатор.

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

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

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

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

if(hPrevInstance) return 0;

В этом случае если при запуске приложения параметр hPrevInstance отличен от нуля, то это означает, что ранее уже была запущена копия данного приложения. В этом случае приложение завершается с кодом, равным нулю.

Третий параметр функции WinMain имеет имя lpszCmdLine. Он имеет тип LPSTR, который определяется в include-файле windows.h следующим образом:

#define FAR _far
typedef char FAR* LPSTR;

Из этого определения видно, что параметр lpszCmdLine является дальним указателем на символьную строку. Что это за строка?

Программе MS-DOS при запуске из командной строки вы можете передать параметры. Эти параметры программа получает через параметры argc и argv функции main.

Аналогично, при запуске приложения Windows вы также можете указать параметры. Эти параметры должны быть записаны в текстовом виде после имени exe-файла приложения. Параметры можно задать для любой пиктограммы любой группы Program Manager. Для этого выберите из меню "File" строку "Properties" и в поле "Command Line" после пути к exe-файлу приложения допишите необходимые параметры.

Еще один способ указания параметров заключается в использовании для запуска приложения командной строки, появляющейся в отдельной диалоговой панели при выборе строки "Run..." из меню "File" приложения Program Manager.

После запуска приложение может проанализировать строку параметров, пользуясь переменной lpszCmdLine как дальним указателем на строку параметров. Учтите, что перед передачей параметров приложению никакой обработки строки параметров не производится, приложение получает строку параметров точно в таком виде, в котором она была указана при запуске. В частности, если было указано несколько параметров, разделенных пробелом или другим символом, переменная lpszCmdLine будет указывать на строку, содержащую все разделительные пробелы (или другие символы). Строка параметров будет закрыта двоичным нулем.

Последний параметр функции WinMain имеет имя nCmdShow и тип int.

Этот параметр содержит рекомендации приложению относительно того, как оно должно нарисовать свое главное окно. Приложение может проигнорировать эти рекомендации, не учитывая значение параметра nCmdShow, однако это плохой стиль программирования.

Вы знаете, что практически любое приложение может увеличивать свое окно до размеров экрана видеомонитора (или до некоторого предельного размера, зависящего от самого приложения) или уменьшать его, сворачивая в пиктограмму. При запуске приложения с помощью строки "Run..." меню "File" приложения Program Manager кроме командной строки вы можете указать режим запуска "Run Minimized". В этом режиме правильно спроектированное приложение (способное анализировать параметр nCmdShow) при запуске сразу сворачивает свое главное окно в пиктограмму.

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

У вас может возникнуть вопрос - зачем переопределять тип _pascal как PASCAL, а тип _far как FAR?

Дело в том, что операционная система Windows задумана (во всяком случае Windows NT) как переносимая на различные платформы. Windows NT успешно работает не только на процессорах фирмы Intel, но и, например, на процессоре Alpha, созданном фирмой DEC и имеющем свою собственную архитектуру. Если вы планируете в будущем перетранслировать исходные тексты своих приложений для Windows NT, вам нельзя закладывать в них особенности архитектуры процессоров Intel. Например, тип FAR для Windows версии 3.1 определен как _far, а для Windows NT этот же тип может быть определен по-другому. Например, так:

#define FAR

Как дополнительная помощь в создании переносимых (мобильных) приложений, в операционной системе Windows программный интерфейс (API) отделен от самой операционной системы. Поэтому программный интерфейс Windows в принципе может быть реализован в среде другой операционной системы, такой, как UNIX или OS/2.

Программа "Hello, world!" для Windows

Давайте попробуем создать для Windows вариант известной всем программы, приведенной в книге Кернигана и Риччи, посвященной программированию на языке C:

main()
{
  printf("Hello, world!");
}

Задачей этой программы, как следует из исходного текста, является вывод на экран строки "Hello, world!".

Как мы уже говорили, вместо функции main приложение Windows использует функцию WinMain, причем необходимо указать все четыре параметра этой функции.

К сожалению, вы не сможете воспользоваться функцией printf, так как ни эта, ни другие аналогичные функции консольного ввода/вывода в обычных приложениях Windows использовать нельзя. Для вывода текстовой строки "Hello, world!" мы воспользуемся функцией из программного интерфейса Windows с именем MessageBox.

Создайте на диске каталог с именем hello и скопируйте в него файлы hello.cpp и hello.prj из одноименного каталога, расположенного на дискете, которую вы купили вместе с книгой. Если вы приобрели книгу без дискеты, воспользуйтесь исходным текстом программы, приведенным в листинге 1.1.


Листинг 1.1. Файл hello\hello.cpp


// ----------------------------------------
// Простейшее приложение Windows
//   "Hello, world!"
// ----------------------------------------
#define STRICT
#include <windows.h>
#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  MessageBox(NULL, "Hello, world!", "Main Window", MB_OK);
  return 0;
}

Первой строкой в программе является определение символа STRICT:

#define STRICT

Это определение влияет на обработку файла windows.h, обеспечивая более строгую проверку типов данных. Такая проверка облегчит вам в дальнейшем преобразование исходных текстов программ для 32-разрядных приложений Win32s или Windows NT. И хотя в нашем простом примере проверять почти нечего, мы включили определение STRICT для сохранения единого стиля во всех примерах программ.

Следующая строка сообщает компилятору, что не нужно выводить предупреждающее сообщение о том, что расположенная следом функция не пользуется своими параметрами:

#pragma argsused

В нашем первом примере мы игнорировали все четыре параметра, однако из-за использования соглашения о передаче параметров PASCAL все параметры функции WinMain должны быть описаны.

Для вывода строки "Hello, world!" мы использовали функцию MessageBox:

MessageBox(NULL, "Hello, world!", "Main Window", MB_OK);

Прототип функции MessageBox определен в файле windows.h:

int WINAPI MessageBox(HWND, LPCSTR, LPCSTR, UINT);

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

int WINAPI MessageBox(HWND hwndParent, LPCSTR lpszText,
    LPCSTR lpszTitle, UINT fuStyle);

Не вдаваясь в подробности, скажем, что эта функция создает на экране диалоговую панель с текстом, заданным вторым параметром lpszText (в нашем случае - с текстом "Hello, world!"), и заголовком, заданным третьим параметром lpszTitle ("Main Window").

Параметр hwndParent указывает так называемый идентификатор родительского окна, создающего диалоговую панель (его мы рассмотрим позже). Этот параметр можно указывать как NULL, в этом случае у диалоговой панели не будет родительского окна.

Первый параметр в нашем примере необходимо указать как NULL.

Последний параметр fuStyle - константа MB_OK, значение которой определено в файле windows.h. Использование в качестве последнего параметра значения MB_OK приводит к появлению в диалоговой панели одной кнопки с надписью "OK". Когда вы нажмете на эту кнопку, функция MessageBox возвратит управление в функцию WinMain.

Обратите внимание на то, что имена параметров функции MessageBox имеют префиксы. Эти префиксы используются для того, чтобы включить в имя параметра информацию о его типе. При создании приложений Windows приходится использовать очень много типов данных. Префиксы позволяют избежать ошибок, связанных с неправильным использованием параметров. Подробнее о префиксах в именах параметров и переменных вы можете узнать из приложения с названием "Имена параметров функций".

Для завершения работы приложение использует функцию return:

 return 0;

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

Теперь займемся созданием приложения, для чего воспользуемся интегрированной средой разработки Borland C++ for Windows версии 3.1 или Borland Turbo C++ for Windows.

Для создания приложения прежде всего вам нужно образовать новый prj-файл. Запустите среду разработки и из меню "Project" выберите строку "Open Project...". На экране появится диалоговая панель "Open Project File" (рис. 1.1).

Рис. 1.1. Диалоговая панель "Open Project File"

С помощью меню "Directories" выберите и сделайте текущим созданный вами каталог с именем hello. Если в этом каталоге уже имеется файл hello.prj, выберите его и нажмите кнопку "OK". Если файла нет (вы не купили дискету с примерами программ), наберите в поле "File Name" имя hello.prj и нажмите кнопку "OK". В этом случае будет создан новый файл проекта.

При создании нового проекта в нижней части основного окна Borland C++ появится окно "Project:hello" (рис. 1.2).

Рис. 1.2. Окно "Project: hello"

В этом окне отображается список файлов, входящих в проект hello.prj. При создании нового проекта этот список пуст. Нам надо добавить в проект файл с именем hello.cpp. Для добавления файла нажмите клавишу <Insert>. На экране появится диалоговая панель "Add To Project List" (рис. 1.3), с помощью которой можно добавить к проекту файл с программой, объектным модулем, библиотекой объектных модулей и т. п.

Рис. 1.3. Диалоговая панель "Add To Project List"

Выберите при помощи меню или наберите в поле "File Name" имя hello.cpp, затем нажмите кнопку "Add". В списке файлов проекта появится имя добавленного вами файла. Так как проект нашего первого приложения состоит из одного файла, после добавления файла hello.cpp нажмите кнопку "Done".

Для того чтобы открыть окно редактирования файла сделайте в окне "Project:hello" двойной щелчок левой клавишей мыши по имени файла (установите курсор мыши на имя файла и нажмите с небольшим интервалом два раза левую клавишу мыши), в нашем случае по имени hello.cpp. В главном окне появится окно редактирования (рис. 1.4).

Рис. 1.4. Окно редактирования

Если вы добавили к проекту пустой файл hello.cpp и затем открыли его двойным щелчком по имени файла, окно редактирования не будет содержать никакого текста. Наберите в нем текст программы, приведенный в листинге 1.1.

Учтите, что транслятор Borland C++ версии 3.1 для отображения текста программы использует шрифт BorlandTE, в котором нет русских букв. Если вы будете работать с русскими буквами, замените этот шрифт на другой, например Courier Cyrillic. Для этого выберите в меню "Options" строку "Environment". Затем в появившемся подменю выберите строку "Preferences". На экране появится диалоговая панель "Preferences" (рис. 1.5).

Рис. 1.5. Диалоговая панель "Preferences"

В этой диалоговой панели с помощью меню "Font" вы можете выбрать шрифт для окна редактирования. Мы рекомендуем вам также в группе переключателей "Auto Save" включить все переключатели (как это показано на рис 1.5). После внесения всех изменений нажмите кнопку "OK".

Кроме этого в меню "Options" выберите строку "Application..." и в появившейся диалоговой панели нажмите мышью пиктограмму с надписью "Windows App" и затем кнопку "OK". При этом транслятор будет настроен на создание обычных приложений Windows.

Для сохранения внесенных изменений выберите в меню "Options" строку "Save..." и в появившейся диалоговой панели нажмите кнопку "OK", предварительно убедившись, что все три имеющихся там переключателя находятся во включенном состоянии (отмечены галочкой).

Подготовив файл hello.cpp, выберите из меню "Compile" строку "Build all". На экране появится диалоговая панель "Compile Status", в которой будет отображаться ход трансляции файлов проекта и сборки файла загрузочного модуля (рис. 1.6)

Рис. 1.6. Диалоговая панель "Compile Status"

Вам надо следить за сообщениями об ошибках (Errors) и предупреждениями (Warnings). Если вы не допустили ошибок при наборе содержимого файла hello.cpp, после редактирования вы должны получить только одно предупреждающее сообщение:

Linker Warning: No module definition file specified: using defaults

Это сообщение говорит о том, что в проект не включен так называемый файл определения модуля (module definition file) и поэтому используется файл, принятый по умолчанию. Для простоты мы сознательно не стали включать в проект этот файл, но проекты всех наших следующих приложений будут содержать файл определения модуля.

После завершения процесса редактирования в поле "Status" диалоговой панели "Compile Status" появится слово Success (успех). Для продолжения работы вы должны нажать кнопку "OK".

Теперь попробуем запустить созданное нами приложение. Для этого из меню "Run" выберите строку "Run". После проверки файлов проекта на экране появится диалоговая панель, озаглавленная "Main Window" (рис. 1.7).

Рис. 1.7. Диалоговая панель "Main Window"

Она содержит текстовую строку "Hello, world!", прочитав которую, вы можете нажать кнопку "OK". Это приведет к завершению работы нашего первого приложения.

Не следует думать, что все приложения Windows так же просты, как это. Мы еще не затронули основного в Windows - окон и сообщений! Однако теперь вы умеете создавать приложения Windows, что и было нашей основной задачей на данном этапе.

[Назад] [Содеожание] [Дальше]