Программный интерфейс приложения в Windows 7. Новые функции Win64 API
Изучение основ и других аспектов программирования с использованием Win 32/64 API. Интерфейс программирования приложений Win32. Направления расширения функциональности WinMain и WndProc. Разработка для x64 с помощью языка программирования Visual C++.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | курсовая работа |
Язык | русский |
Дата добавления | 17.10.2017 |
Размер файла | 283,1 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ
ОБНИНСКИЙ ИНСТИТУТ АТОМНОЙ ЭНЕРГЕТИКИ - филиал
федерального государственного автономного образовательного учреждения
высшего профессионального образования
«Национальный исследовательский ядерный университет «МИФИ»
(ИАТЭ НИЯУ МИФИ)
Факультет Кибернетики
Кафедра Компьютерных систем, сетей и технологий
Курсовая работа
по дисциплине «Операционные системы»
Тема работы: Программный интерфейс приложения в Windows 7.
Новые функции Win64 API
Выполнил: Студент 4 курса гр. ВТ2-С10
Специальности 230101 Карпенко С.В.
Научный руководитель: Профессор кафедры КССТ
Куликова Н.В.
Обнинск 2013
Содержание
Введение
1. Windows API. Определение и задачи
2. Интерфейс программирования приложений Win32
2.1 Расширение функциональности WinMain
2.2 Класс окна
2.3 Создание окна
2.4 Расширение функциональности WndProc
3. С чего начать? Переход от Win32 к Win64
3.1 Операционная система x64
4. Разработка для x64 с помощью Visual C++
4.1 Как сделать код совместимым с Win64
4.2 Отладка
Заключение
5. Приложения
5.1 Приложение 1
5.2 Приложение 2
Литература
Введение
На выбор данной темы курсовой работы меня подтолкнул курсовой проект по системному программному обеспечению: "Использование концепции.NET в системном программировании". Над этим проектом я работал в прошлом семестре, в ходе которого я познакомился с платформой разработки приложений.Net FrameWork. Я изучал разные среды разработки, такие как С#, C++ и др. В ходе работы я изучил принципы работы линкеров, трансляторов и компиляторов в данных средах, изучил debugger'ы. Данная работа углубит мои знания в процессе разработки приложений с использованием интерфейса Win32/64 API. В данный момент на мировом рынке активно продвигается операционная система Windows от корпорации Microsoft, т.о. можно сделать вывод, что изучение основ и других аспектов программирования с использованием Win 32/64 API благоприятно повлияет на мою эрудированность в сфере программирования. А знания о переходе от Win32 к Win64 помогут мне разрабатывать актуальные приложения под систему Windows 7/8, которые на данный момент шире всего распространены. программирование интерфейс приложение visual
1. Windows API. Определение и задачи
Windows API (англ. application programming interfaces) -- общее наименование целого набора базовых функций интерфейсов программирования приложений операционных систем семейств Microsoft Windows корпорации «Майкрософт». Для создания программ, использующих Windows API, «Майкрософт» выпускает комплект разработчика программного обеспечения, который называется Platform SDK, и содержит документацию, набор библиотек, утилит и других инструментальных средств для разработки.
Windows API был изначально спроектирован для использования в программах, написанных на языке Си или C++. Работа через Windows API -- это наиболее близкий к системе способ взаимодействия с ней из прикладных программ. Более низкий уровень доступа, необходимый только для драйверов устройств, в текущих версиях Windows предоставляется через Windows Driver Model.
2. Интерфейс программирования приложений Win32
Для того, чтобы сделать как простое, так и более сложное приложение с помощью Win32 API необходимо, сначала, выяснить, каким образом происходит инициализации, какие функции при этом используются, как они взаимодействуют между собой и т.д.
Как известно, любое приложение на языках C и C++ должно иметь функцию main. Эта функция является начальной точкой для приложения. Подобным же образом любое приложение Win32 должно иметь функцию WinMain. Синтаксис функции WinMain выглядит следующим образом:
int WINAPI WinMain
(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow);
§ HINSTANCE hInstance - дескриптор экземпляра приложения. Этот дескриптор содержит адрес начала кода программы в ее адресном пространстве. Дескриптор hInstance чаще всего требуется функциям, работающим с ресурсами программы.
§ HINSTANCE hPrevInstance - дескриптор предыдущего экземпляра приложения. Этот дескриптор остался от старых версий Windows - скорее всего, вам он никогда не пригодится.
§ LPSTR lpCmdLine - указатель на начало командной строки, введенной при запуске программы.
§ int nCmdShow - это значение содержит желаемый вид окна (например, свернутый или развернутый)
Значение, которое возвращается функцией WinMain (тип int) - код завершения программы. Принято, что если программа завершила свое выполнение без ошибок, возвращается 0.
Функция WinMain - первая функция, которая выполнятся в программе (ее еще называют «точка входа» или «entry point»). С нее все начинается, и ею (желательно) все должно закончиться.
Наряду с функцией WinMain, в каждом приложении Win32 также должна быть определена еще одна функция, обычно называемая WndProc и представляющая собой оконную процедуру. Синтаксис функции WndProc выглядит следующим образом:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
При вызове этой функции ей передаются следующие параметры:
§ HWND hWnd - описатель окна, от которого пришло сообщение.
§ UINT message - идентификатор сообщения.
§ WPARAM wParam и LPARAM lParam - параметры сообщения.
Назначением этой функции является обработка сообщений, получаемых приложением от операционной системы. В каком случае приложение получает сообщения от операционной системы? Оно получает их постоянно! Например, представим, что было создано диалоговое окно с кнопкой ОК. Когда пользователь нажимает кнопку, операционная система посылает приложению сообщение, оповещающее о нажатии кнопки. Функция WndProc отвечает за реагирование на это событие. В этом примере соответствующей реакцией на это событие может быть закрытие диалогового окна.
2.1 Расширение функциональности WinMain
Для начала создайте внутри функции WinMain структуру класса окна типа WNDCLASSEX. Эта структура содержит информацию об окне, такую как используемые в приложении значки, цвет фона окна, отображаемое в заголовке окна название, имя функции процедуры окна и т.д. Типичная структура WNDCLASSEX выглядит следующим образом:
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
После того как класс окна будет создан, необходимо зарегистрировать его. Воспользуйтесь функцией RegisterClassEx, которой следует передать структуру класса окна в качестве аргумента:
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
После того как класс будет зарегистрирован, можно приступать к созданию окна. Используйте функцию CreateWindow, как показано ниже:
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("Win32 Guided Tour Application");
// Параметры CreateWindow обозначают:
// szWindowClass: имя класса окна
// szTitle: текст, который будет отображаться в титуле
// WS_OVERLAPPEDWINDOW: тип создаваемого окна
// CW_USEDEFAULT, CW_USEDEFAULT: начальные позиции (x, y)
// 500, 100: размеры окна (width, length)
// NULL: предки окна
// NULL: это окно не будет иметь меню
// hInstance: первый параметр для WinMain
// NULL: не используется в этом приложении
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 100,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
{
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
}
Эта функция возвращает объект HWND, являющийся дескриптором окна.
2.2 Класс окна
При создании нового окна ему присваивается «Класс окна» (window class). Класс окна задает оконную функцию, используемую по умолчанию. Кроме этого, класс окна задает другие параметры окна, такие, как стиль, меню окна, цвет рабочей области и т.д. Разные классы окон могут указывать на одну и ту же функцию обработки сообщений. Для создания класса его необходимо зарегистрировать.
Итак, регистрация! За нее отвечает функция RegisterClass. В ее параметре необходимо передать указатель на структуру WNDCLASS. Обычно для заполнения структуры и вызова RegisterClass создают отдельную функцию. Но это - дело вкуса.
Вот простейший пример такой функции:
ATOM RegMyWindowClass(HINSTANCE hInst, LPSTR lpzClassName)
{
WNDCLASS wcWindowClass = {0};
//адрес функции обработки сообщений
wcWindowClass.lpfnWndProc = (WNDPROC)WndProc;
//стиль окна
wcWindowClass.style = CS_HREDRAW|CS_VREDRAW;
//дискриптор экземпляра приложения
//название класса
wcWindowClass.hInstance = hInst;
wcWindowClass.lpszClassName = lpzClassName;
//загрузка курсора
wcWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
//загрузка цвета окон
wcWindowClass.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
//регистрация класса
return RegisterClass(&wcWindowClass);
}
§ WNDPROC lpfnWndProc - адрес функции обработки сообщений.
§ HINSTANCE hInstance - уже знакомая переменная, описывающая экземпляр.
§ LPCTSTR lpszClassName - имя нового класса.
§ HICON hCursor - описатель курсора мыши.
§ HBRUSH hbrBackground - цвет рабочей области окна.
Функция RegisterClass возвращает уникальный «описатель класса окна» типа ATOM. Если при регистрации класса произошла ошибка, это значение будет равно нулю. Чтобы узнать, что произошло, можно вызвать функцию GetLastError().
Существует также функция RegisterClassEx, приведенная выше. Это аналог функции RegisterClass с возможностью присвоения окнам маленькой иконки. При работе с этой функцией необходимо пользоваться структурой WNDCLASSEX.
2.3 Создание окна
На вашем месте у меня возникло бы желание увидеть те самые пресловутые окна, из-за которых столько шума. Окно в Windows создается функцией CreateWindow. Вот ее прототип:
HWND CreateWindow(LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD wStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam);
Как видите, у функции множество параметров:
§ LPCTSTR lpClassName - имя класса для создаваемого окна (это имя использовалось при регистрации класса).
§ LPCTSTR lpWindowName - имя окна.
§ DWORD dwStyle - стиль окна.
§ int x - позиция по горизонтали верхнего левого угла окна.
§ int y - позиция по вертикали.
§ int nWidth - ширина окна.
§ int nHeight - высота окна.
§ HWND hWndParent - используется для создания «дочернего окна» («child window»). Сюда передается описатель «родительского окна» («parent window»).
§ HMENU hMenu - описатель меню (если hMenu равно нулю, используется меню класса, указанного в lpClassName).
§ HINSTANCE hInstance - экземпляр приложения.
§ LPVOID lpParam - указатель на пользовательский параметр окна. Этот указатель со всеми остальными параметрами функции CreateWindow будет занесен в структуру CREATESTRUCT. В сообщениях WM_CREATE или WM_NCCREATE параметр lParam будет содержать указатель на эту структуру.
Функция CreateWindow возвращает уникальный описатель окна HWND. Если функция вернула ноль, значит, во время создания окна произошла ошибка. Какая именно, можно узнать, вызвав функцию GetLastError.
2.4 Расширение функциональности WndProc
Назначением функции WndProc является обработка сообщений, получаемых приложением. Обработка таких сообщений обычно реализуется путем использования функции Switch.
Для начала реализуем обработку сообщения WM_PAINT. Приложение получает это сообщение, когда возникает необходимость в обновлении какой-либо области окна приложения. При создании окна данное сообщение передается для указания на необходимость обновления всего окна.
При обработке сообщения WM_PAINT сперва необходимо вызвать функцию BeginPaint, а в завершение следует вызвать функцию EndPaint. Между вызовами этих двух функций обрабатывается логика по отображению текста, кнопок и других элементов управления в окне. Данное приложение отображает в окне строку "Hello, World!". Для отображения текста следует использовать функцию TextOut.
Обычно приложение обрабатывает множество других сообщений, таких как WM_CREATE и WM_DESTROY. Ниже приведен код простой, но полноценной функции WndProc:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello, World!");
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,
5, 5,
greeting, _tcslen(greeting));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
Итак, сейчас упрощенно, что же произойдет, если щелкнуть по окну левой кнопкой мыши.
1. Пользователь нажимает левую кнопку мыши в то время когда курсор мыши находится над рабочей областью окна.
2. Windows помещает сообщение WM_LBUTTONDOWN в очередь потока.
3. Цикл обработки сообщения должен вынуть сообщение с помощью функции GetMessage и передать его на обработку функции DispatchMessage.
4. Функция DispatchMessage находит окно, которому предназначено сообщение и помещает сообщение в его очередь.
5. Функция окна обрабатывает сообщение WM_LBUTTONDOWN и возвращает результат.
6. Тело цикла заканчивается, и управление снова передается функции GetMessage для ожидания новых сообщений.
Пример рабочего кода для вышеописанного алгоритма можно посмотреть в приложении 1.
3. С чего начать? Переход от Win32 к Win64
Приятная особенность Win64 и процессорной архитектуры x64 заключается в том, что они достаточно сильно отличаются от своих предшественников - как раз настолько, чтобы вызывать интерес, в то же время не требуя основательного переобучения. Хотя мы, предпочитаем считать, будто при переходе на платформу x64 удастся отделаться простой перекомпиляцией своих программ, реальность такова, что нам все равно придется долго возиться с ними в отладчике. И здесь хорошее понимание новой программно-аппаратной платформы будет важным подспорьем.
Системы x64 хороши еще и тем, что они - в отличие от систем на основе процессоров Itanium - позволяют использовать либо Win32, либо Win64 на одном компьютере без серьезных потерь в производительности. И несмотря на некоторые, весьма туманные различия между реализациями x64 от Intel и AMD x64-совместимая версия Windows должна работать с любой из них. Вам не понадобятся отдельные сборки Windows для x64-процессоров AMD и Intel.
3.1 Операционная система x64
В любом обзоре архитектуры Windows лучше начинать с рассмотрения адресации и адресного пространства. Хотя 64-разрядный процессор теоретически мог бы адресоваться к 16 экзабайтам памяти (264), в настоящее время Win64 поддерживает 16 Тб - значение, которое представлено 44 разрядами. Почему же нельзя задействовать все 64 разряда, чтобы адресоваться к 16 экзабайтам памяти? По целому ряду причин.
Начнем с того, что нынешние процессоры x64 обычно позволяют обращаться лишь к 40-разрядному представлению физической памяти (1 Тб). Сама архитектура (но не современное оборудование) допускает расширение до 52 разрядов (4 петабайтов). Даже если бы это ограничение было снято, размеры таблиц страниц, необходимых для проецирования такого громадного объема памяти, оказались бы просто гигантскими.
Как и в Win32, адресуемая память делится на области пользовательского режима и режима ядра. Каждому процессу выделяется собственное уникальное пространство размером 8 Тб в нижней части памяти, а код режима ядра размещается в верхних 8 Тб и разделяется всеми процессами. У разных версий 64-разрядной Windows разные ограничения на объемы физической памяти (табл. 1 и 2).
Табл. 1
Общие ограничения на память
32-разрядные модели |
64-разрядные модели |
||
Виртуальное адресное пространство (одного процесса) |
4 Гб |
16 Тб |
|
Виртуальное адресное пространство для каждого 32-разрядного процесса |
2 Гб (3 Гб при запуске системы с ключом /3GB) |
4 Гб при компиляции с параметром /LARGEADDRESSAWARE (иначе 2 Гб) |
|
Виртуальное адресное пространство для каждого 64-разрядного процесса |
Неприменимо |
8 Тб |
|
Пул подкачиваемой памяти режима ядра |
470 Мб |
128 Гб |
|
Пул не подкачиваемой памяти режима ядра |
256 Мб |
128 Гб |
|
Элемент системной таблицы страниц (Page Table Entry, PTE) |
660-900 Мб |
128 Гб |
Табл. 2
Ограничения на физическую память в зависимости от процессоров
Операционная система |
32-разрядные модели |
64-разрядные модели |
|
Windows XP Professional |
4 Гб (1-2 процессора) |
128 Гб (1-2 процессора) |
|
Windows Server 2003, Standard Edition |
4 Гб (1-4 процессора) |
32 Гб (1-4 процессора) |
|
Windows Server 2003, Enterprise Edition |
64 Гб (1-8 процессоров) |
1 Тб (1-8 процессоров) |
|
Windows Server 2003, Datacenter Edition |
64 Гб (8-32 процессора) |
1 Тб (8-64 процессора) |
Так же, как и в Win32, размер страницы на платформе x64 равен 4 Кб. Первые 64 Кб адресного пространства никогда не проецируются на физическую память, поэтому младший допустимый адрес - 0x10000. В отличие от Win32 системные DLL по умолчанию не загружаются по адресу в верхней части адресного пространства пользовательского режима. Вместо этого они загружаются после 4 Гб, обычно по адресам, близким к 0x7FF00000000.
Приятная особенность процессоров x64 - поддержка битового флага No Execute, который в Windows используется для реализации аппаратной защиты от выполнения данных как кода (Data Execution Protection, DEP). Существование многих вирусов и "багов" на платформе x86 как раз и обусловлено тем, что процессор может выполнять данные так, будто это байты кода. Переполнение буфера (намеренное или случайное) может привести к тому, что процессор будет выполнять содержимое области памяти, где должны храниться данные. Благодаря DEP операционная система гораздо четче разграничивает области памяти, в которых находится код, и становится способной перехватывать попытки выполнения кода, выходящие за эти границы. Это уменьшает уязвимость Windows перед атаками.
Для выявления ошибок компоновщик (linker) на платформе x64 по умолчанию присваивает адресам загрузки исполняемых файлов первое значение, большее 32-разрядного числа (4 Гб). Это помогает быстро находить проблемные места в существующем коде после его переноса на Win64. В частности, если указатель хранится как 32-битное значение (например, как DWORD), то при работе в Win64-версии вашей программы он окажется усеченным и станет недопустимым, тут же вызвав нарушение доступа к памяти (access violation). Такой прием резко упрощает отлов ошибок, связанных с указателями.
Затронутый вопрос указателей и DWORD-значений позволяет плавно перейти к системе типов в Win64. Какой размер должен иметь указатель? Как насчет LONG? И описателей (handles) наподобие HWND? К счастью, Microsoft, ведя нас по весьма запутанному пути от Win16 к Win32, заодно создала новые модели типов, легко расширяемые и до 64-разрядных. В общем, если не считать нескольких исключений, все типы, отличные от указателей и size_t, совершенно одинаковы, что в старой Win32, что в новой Win64. То есть у 64-битного указателя размер 8 байтов, а у int, long, DWORD и HANDLE остался прежний размер - 4 байта.
Формат файлов в Win64 называется PE32+. С точки зрения структуры, он почти во всем идентичен формату PE в Win32. Лишь некоторые поля вроде ImageBase в заголовке расширены, одно поле удалено и одно изменено так, чтобы оно отражало новый тип процессоров (табл. 3).
Табл. 3
Изменения в полях заголовков PE-файлов
Поле заголовка |
Изменение |
|
Magic |
0x20b вместо 0x10b |
|
BaseOfData |
Убрано |
|
ImageBase |
Расширено до 64 битов |
|
SizeOfStackReserve |
Расширено |
|
SizeOfStackCommit |
Расширено |
|
SizeOfHeapReserve |
Расширено |
|
SizeOfHeapCommit |
Расширено |
Помимо заголовка PE, изменений не так уж много. В некоторых структурах, например IMAGE_LOAD_CONFIG и IMAGE_THUNK_DATA, часть полей просто расширена до 64 битов. Больший интерес представляет введение раздела PDATA, так как он высвечивает одно из основных различий между реализациями Win32 и Win64: концепцию обработки исключений.
На платформе x86 обработка исключений базируется на стеке. Когда Win32-функция содержит код try/catch или try/finally, компилятор генерирует инструкции, создающие небольшие блоки данных в стеке. Каждый блок данных try указывает на предыдущую структуру данных try, образуя связанный список, в котором структуры, добавленные последними, помещаются в начало списка. По мере вызова функций и выхода из них начало связанного списка обновляется. Как только возникает исключение, ОС просматривает связанный список блоков в стеке, отыскивая подходящий обработчик. Все детали этого процесса я изложил в своей статье за январь 1997 г. (microsoft.com/msj/0197/Exception/Exception.aspx), так что здесь я не буду вдаваться в подробности.
В Win64 (в версиях для x64 и Itanium) применяется табличная обработка исключений. Никакого связанного списка блоков данных try в стеке не создается. Вместо этого каждый исполняемый файл в Win64 содержит таблицу функций периода выполнения (runtime function table). В каждой записи этой таблицы хранятся начальный и конечный адреса функции, а также местонахождение большого набора данных о коде, обрабатывающем исключения в данной функции, и структура ее фрейма стека. Детальное содержимое этих структур см. в определении IMAGE_RUNTIME_FUNCTION_ENTRY в файле WINNT.H и в x64 SDK.
Когда возникает исключение, ОС просматривает обычный стек потока. При этом, анализируя каждый фрейм и сохраненный указатель инструкции, ОС определяет, в каком модуле исполняемого файла находится этот указатель. Далее ОС ищет в найденном модуле таблицу функций периода выполнения, находит в ней подходящую запись и на основании содержащихся в этой записи данных принимает решения по обработке исключения.
А как быть, если вы сгенерировали код непосредственно в памяти, не используя нижележащий модуль формата PE32+? В Win64 имеется API-функция RtlAddFunctionTable, позволяющая сообщить ОС о динамически генерируемом коде.
Недостаток табличной обработки исключений (в сравнении с x86-моделью на основе стека) заключается в том, что поиск записей в таблице функций по адресам кода занимает больше времени, чем простой просмотр связанного списка. Зато исключаются издержки, связанные с тем, что в x86-модели приходится обновлять блок данных try при каждом выполнении функции.
В x64-совместимых версиях Windows не появилось слишком уж много новых API-функций - большинство таковых в Win64 добавлено в выпуски Windows для процессоров Itanium. Две наиболее важные API-функции - IsWow64Process и GetNativeSystemInfo - позволяют Win32-приложениям определять, выполняются ли они в Win64, и, если да, выяснять реальные возможности данной системы. Если же 32-разрядный процесс обращается к GetSystemInfo, он видит лишь те возможности, которые свойственны обычной 32-разрядной системе. Так, GetSystemInfo способна сообщать о диапазонах адресов лишь 32-разрядных процессов. В табл. 4 перечислены API-функции для платформы x64, которых не было на платформе x86.
Табл. 4
Изменения в полях заголовков PE-файлов
Функциональность |
API-функции |
|
Обработка исключений |
RtlAddFunctionTable |
|
Реестр |
RegDeleteKeyEx |
|
NUMA (Non-Uniform Memory Access) |
GetNumaAvailableMemoryNode |
|
Перенаправление WOW64 |
Wow64DisableWow64FsRedirection |
|
Разное |
GetLogicalProcessorInformation |
4. Разработка для x64 с помощью Visual C++
Хотя x64-код можно было писать в Microsoft C++ до появления Visual Studio 2005, это было весьма неудобно. Поэтому здесь исходим из того, что вы работаете в Visual Studio 2005 и что вы выбрали инструментарий для платформы x64, который по умолчанию не устанавливается. Предположим, что у нас уже есть какой-то Win32-проект (пользовательского режима) на C++, который мы хотим компилировать для обеих платформ - как x86, так и x64.
Первый шаг в компиляции программы для x64 - создание конфигурации 64-разрядной сборки. Как пользователь Visual Studio, известно, что по умолчанию у проектов две конфигурации сборки: Debug и Retail. Поэтому остается создать еще две конфигурации: Debug и Retail для x64.
Начнем с загрузки существующего проекта/решения. В меню Build выберите Configuration Manager. В диалоговом окне Configuration Manager в раскрывающемся списке Active Solution Platform выберите New (рис. 1). После этого вы должны увидеть диалог New Solution Platform.
Рис. 1 Создание новой конфигурации сборки
Выберем x64 в качестве новой платформы (рис. 2), прочие параметры оставьте в состоянии по умолчанию и щелкните OK. Вот и все! Теперь у нас должно быть четыре конфигурации сборки: Win32 Debug, Win32 Retail, x64 Debug и x64 Retail. Переключаться между ними мы будем через Configuration Manager.
Рис. 2 Выбор платформы сборки
Теперь посмотрим, насколько совместим с x64 наш код. Создадим конфигурацию x64 Debug по умолчанию и соберем проект. Если его код не тривиален, все шансы за то, что мы получим при компиляции ошибки, не встречавшиеся в Win32-конфигурации. Но справиться с этими проблемами и сделать код действительно совместимым как с Win32, так и с x64 сравнительно легко, если только мы не нарушали все принципы написания портируемого C++-кода. И не потребуются тонны директив условной компиляции.
4.1 Как сделать код совместимым с Win64
Вероятно, при преобразовании Win32-кода в x64-код больше всего усилий понадобится для того, чтобы сохранить корректность ваших определений типов. Выше говорилось о системе типов в Win64? Используя Windows-типы, определенные через typedef в заголовочных файлах Windows, а не "родные" для компилятора C++ типы (int, long и др.), мы упростим себе написание чистого Win32-кода, способного работать на платформе x64.
Вероятно, наиболее частая и легко устранимая ошибка, которую можно встретить при переносе кода, вызвана предположением о том, что значение указателя может быть сохранено или перенесено в 32-разрядном типе вроде int, long или даже DWORD. Но вся штука в том, что указатели в Win32 и Win64 имеют разные размеры, а целочисленные типы остались прежней длины.
Здесь помогают типы _PTR, определенные в заголовочных файлах Windows. Такие типы, как DWORD_PTR, INT_PTR и LONG_PTR, позволяют объявлять переменные целочисленного типа, которые всегда имеют достаточный размер для хранения указателя на целевой платформе. Например, переменная, определенная как тип DWORD_PTR, является 32-битной целой при компиляции для Win32 и 64-битной при компиляции для Win64. Немного практики, и использование таких переменных станет вашей второй натурой: объявляя какой-либо тип, вы всегда будете спрашивать себя, нужен здесь DWORD или DWORD_PTR?
Возможны ситуации, где надо точно указывать, сколько именно байтов следует отвести под целый тип. На такие случаи в тех же заголовочных файлах (Basetsd.h и др.), где определяются DWORD_PTR и прочие типы, предлагаются определения целых специфической длины, например INT32, INT64, INT16, UINT32 и DWORD64.
Еще одна проблема, связанная с различиями в размерах типов, относится к форматированию вывода printf и sprintf. Вот я раньше часто грешил конструкциями %X или %08X при форматировании значений указателей и был строго наказан за это при запуске подобного кода в x64-системе. Правильный способ - использование %p, при котором автоматически учитывается размер указателя на целевой платформе. Кроме того, printf и sprintf поддерживают префикс I для типов, размер которых зависит от платформы. Скажем, для вывода значения переменной UINT_PTR можно было бы использовать %Iu. Если же вы точно знаете, что переменная всегда будет 64-битной знаковой, то могли бы указать %I64d.
Вычистив ошибки, вызванные определениями типов, неподходящими для Win64, вы все равно можете остаться с кодом, который работает только на платформе x86. Тогда, вероятно, лучше пойти по простейшему пути и написать две версии функции: одну - для Win32, другую - для x64. И здесь нам очень пригодится набор макросов препроцессора:
M_IX86
_M_AMD64
_WIN64
Правильное использование макросов препроцессора очень важно для написания корректного кросс-платформенного кода. Макросы _M_IX86 и _M_AMD64 применяются только при компиляции под определенную платформу, а _WIN64 - при компиляции для любой 64-разрядной версии Windows, в том числе выпуска для процессоров Itanium.
Пытаясь применить макрос препроцессора, хорошенько подумайте о том, чего вы добиваетесь. Например, действительно ли ваш код специфичен только для процессоров x64? Если да, пишите:
#ifdef _M_AMD64
С другой стороны, если тот же код мог бы работать и на x64, и на Itanium, лучше сделать так:
#ifdef _WIN64
Для себя я принял полезное правило при использовании любого из этих макросов: всегда создавать явные варианты #else (подчеркиваю, явные!), чтобы можно было быстро понять, не пропущено ли что-то. Для примера возьмем плохо написанный код:
#ifdef _M_AMD64
// Здесь находится x64-код
#else
// Здесь находится x86-код
#endif
Что будет, если я теперь скомпилирую его для третьей процессорной архитектуры? Сам того не желая, я скомпилирую x86-код. Гораздо лучше переделать предыдущий код примерно так:
#ifdef _M_AMD64
// Здесь находится x64-код
#elif defined (_M_IX86)
// Здесь находится x86-код
#else
#error !!! Нужно написать код для этой архитектуры
#endif
Одна из частей моего Win32-кода, которую удалось перенести на платформу x64 лишь с большим трудом, - подставляемый (inline) ассемблерный код, не поддерживаемый Visual C++ для x64. Но не бойтесь, любители ассемблера. Существует 64-разрядный MASM (ML64.exe); его вместе с документацией можно получить через MSDN. ML64.exe и другие инструменты для x64 (в том числе CL.EXE и LINK.EXE) доступны из командной строки. Чтобы настроить нужные переменные окружения, достаточно запустить файл VCVARS64.BAT.
4.2 Отладка
И вот вы наконец добились чистой компиляции Win32- и x64-версий своего кода. Остался последний фрагмент головоломки - выполнение и отладка этого кода. Хотя вы скомпилировали свою x64-версию на компьютере с процессором x64, для отладки в режиме x64 понадобятся средства удаленной отладки, предоставляемые Visual Studio. К счастью, если вы работаете с Visual Studio IDE на 64-разрядной машине, IDE сама позаботится обо всех необходимых операциях. Если по какой-то причине вы не можете использовать удаленную отладку, остается лишь один вариант - взять x64-версию WinDbg.
Если вы никогда не пользовались удаленной отладкой, сильно волноваться не стоит. Как только вы ее настроите, она почти ничем не будет отличаться от локальной отладки.
Первый шаг - установка на целевой компьютер 64-разрядной MSVSMON. Обычно это делается с помощью программы RdbgSetup, поставляемой с Visual Studio. После запуска MSVSMON зайдите в меню Tools для настройки соответствующих параметров защиты соединения между вашей 32-разрядной Visual Studio и экземпляром MSVSMON.
Далее из Visual Studio сконфигурируйте свой проект на применение удаленной отладки x64-кода. И для начала откройте окно свойств проекта (рис. 3).
Рис. 3 Выбор платформы сборки
Убедитесь, что текущей является ваша 64-разрядная конфигурация, и выберите Debugging в разделе Configuration Properties. Ближе к верхней части вы увидите раскрывающийся список Debugger to launch. Его изначальное значение - Local Windows Debugger. Смените его на Remote Windows Debugger. Под этим списком вы можете задать удаленную команду (например, имя программы), которая должна выполняться в момент начала отладки, а также указать имя удаленного компьютера и тип соединения.
Если вы все правильно настроили, можете начинать отладку своего x64-приложения точно так же, как и Win32-программы. О соединении с MSVSMON свидетельствует строка "connected", которая появляется в трассировочном окне этой программы при каждом успешном подключении отладчика. С этого момента большинство операций выполняется так же, как и в хорошо известном вам отладчике Visual Studio. Не забудьте открыть окно регистров, чтобы увидеть все 64-битные регистры, а также заглянуть в окно дизассемблированного кода, чтобы посмотреть на такой знакомый, но все же слегка другой ассемблерный x64-код.
Заметьте, что 64-разрядный минидамп в отличие от 32-разрядного нельзя напрямую загрузить в Visual Studio. Вместо этого нужно использовать Remote Debugging. Кроме того, в настоящий момент Visual Studio 2005 не поддерживает отладку interop-вызовов между управляемым и неуправляемым 64-разрядным кодом.
Заключение
Win API - мощный инструмент для создания приложений под операционные системы семейства Windows. С помощью этого интерфейса можно проектировать и создавать приложения как на высоком уровне, используя конструкторы диалогов и окон, так и на низком - вручную описывая все диалоги и т.п. Однако и на высоком уровне иногда приходится много работать, чтобы достичь необходимого результата. Win API имеет множество библиотек, для создания разнородных приложение, например, библиотеки opengl32.lib, glu32.lib, glut.lib и прочие позволят использовать оконное приложение, для вывода графической информации, непосредственно в окно, что является важным в современной игровой индустрии.
5. Приложения
5.1 Приложение 1
// kursovik.cpp: определяет точку входа для приложения.
//
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#define MAX_LOADSTRING 100
HWND fact, hButton1, hButton2, hButton3, hButton4, hButton5, hButton6, Edit1, Edit2, Edit3;
int aTemp, bTemp, sumTemp, multTemp;
// Глобальные переменные:
HINSTANCE hInst; // текущий экземпляр
TCHAR szTitle[MAX_LOADSTRING]; // Текст строки заголовка
TCHAR szWindowClass[MAX_LOADSTRING]; // имя класса главного окна
// Отправить объявления функций, включенных в этот модуль кода:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK View(HWND, UINT, WPARAM, LPARAM);
LONG_PTR Factorial (LONG_PTR x){
if(x < 0) // если пользователь ввел отрицательное число
return 0; // возвращаем ноль
if (x == 0) // если пользователь ввел ноль,
return 1; // возвращаем факториал от нуля - не удивляетесь, но это 1 =)
else // Во всех остальных случаях
return x * Factorial (x - 1); // делаем рекурсию.
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: разместите код здесь.
MSG msg;
HACCEL hAccelTable;
// Инициализация глобальных строк
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_KURSOVIK, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Выполнить инициализацию приложения:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KURSOVIK));
// Цикл основного сообщения:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// ФУНКЦИЯ: MyRegisterClass()
//
// НАЗНАЧЕНИЕ: регистрирует класс окна.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_KURSOVIK));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_KURSOVIK);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassEx(&wcex);
}
//
// ФУНКЦИЯ: InitInstance(HINSTANCE, int)
//
// НАЗНАЧЕНИЕ: сохраняет обработку экземпляра и создает главное окно.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Сохранить дескриптор экземпляра в глобальной переменной
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 400, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// ФУНКЦИЯ: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// НАЗНАЧЕНИЕ: обрабатывает сообщения в главном окне.
//
// WM_COMMAND - обработка меню приложения
// WM_PAINT - Закрасить главное окно
// WM_DESTROY - ввести сообщение о выходе и вернуться.
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
static int sx, sy;
//LPCTSTR szMsg = "I'm aplication, compilled with Win64 API";
LPCTSTR szMsg = "Я - приложение, скомпилированное с помощью интерфейса и средств компиляции Win64";
RECT rc;
SIZE sz;
LONG_PTR factorialTemp = 0;
char temp[16] = {0};
switch (message)
{
case WM_CREATE:
//Вызов диалогового окна "О программе"
hButton1 = CreateWindow("BUTTON", "О программе", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT,
10, 10, 120, 20, hWnd, (HMENU)BTN1, NULL, NULL);
//Факториал сообщения введенного в Edit box
fact = CreateWindow("BUTTON", "Факториал", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT,
270, 40, 120, 20, hWnd, (HMENU)factorial, NULL, NULL);
//Эхо сообщения введенного в Edit box
hButton2 = CreateWindow("BUTTON", "Эхо", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT,
140, 40, 120, 20, hWnd, (HMENU)BTN2, NULL, NULL);
Edit1 = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT,
10, 40, 120, 20, hWnd, 0, hInst, NULL);
//Ввод числа А
hButton3 = CreateWindow("BUTTON", "InPut A", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 10, 70, 120, 20, hWnd, (HMENU)BTN3, NULL, NULL);
Edit2 = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 140, 70, 120, 20, hWnd, 0, hInst, NULL);
//Ввод числа В
hButton4 = CreateWindow("BUTTON", "InPut B", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 10, 100, 120, 20, hWnd, (HMENU)BTN4, NULL, NULL);
Edit3 = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 140, 100, 120, 20, hWnd, 0, hInst, NULL);
//Сумма введенных чисел
hButton5 = CreateWindow("BUTTON", "Show sum", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 270, 70, 120, 20, hWnd, (HMENU)BTN5, NULL, NULL);
//Произведение введенных чисел
hButton6 = CreateWindow("BUTTON", "Show mult", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON | BS_TEXT, 270, 100, 120, 20, hWnd, (HMENU)BTN6, NULL, NULL);
break;
case WM_SIZE:
sx = LOWORD(lParam);
sy = HIWORD(lParam);
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Разобрать выбор в меню:
switch (wmId)
{
case factorial:
char f[256];
GetWindowTextA(Edit1, f, 256);
ltoa(Factorial(atol(f)), f, 10);
MessageBoxA(0, f, _T("Факториал"), MB_OK);
break;
case BTN1:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case BTN2:
char ch[256];
GetWindowTextA(Edit1, ch, 256);
MessageBoxA(0, ch, _T("Эхо"), MB_OK);
break;
case BTN3:
char aChar[256];
GetWindowTextA(Edit2, aChar, 256);
aTemp = atoi(aChar);
MessageBoxA(0, _T("Число А введено"), _T("Window A"), MB_OK);
break;
case BTN4:
char bChar[256];
GetWindowTextA(Edit3, bChar, 256);
bTemp = atoi(bChar);
MessageBoxA(0, _T("Число B введено"), _T("Window B"), MB_OK);
break;
case BTN5:
char sumChar[256];
sumTemp = aTemp + bTemp;
itoa(sumTemp, sumChar, 10);
MessageBoxA(0, sumChar, _T("Сумма"), MB_OK);
break;
case BTN6:
char multChar[256];
multTemp = aTemp * bTemp;
itoa(multTemp, multChar, 10);
MessageBoxA(0, multChar, _T("Произведение"), MB_OK);
break;
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case ID_VIEW_MYNEWDIALOG:
DialogBox(hInst, MAKEINTRESOURCE(IDD_VIEW), hWnd, View);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for (float i=1; i<4; i++) {
MoveToEx(hdc, 133+i, 0, NULL);
LineTo(hdc, 133+i, 33+i);
}
for (float i=1; i<4; i++) {
MoveToEx(hdc, 0, 33+i, NULL);
LineTo(hdc, sx, 33+i);
}
for (float i=1; i<4; i++) {
MoveToEx(hdc, 0, 63+i, NULL);
LineTo(hdc, sx, 63+i);
}
for (float i=1; i<4; i++) {
MoveToEx(hdc, 0, 123+i, NULL);
LineTo(hdc, sx, 123+i);
}
GetClientRect(hWnd, &rc);
GetTextExtentPoint32(hdc, szMsg, _tcslen(szMsg), &sz);
TextOut(hdc, (rc.right + rc.left - sz.cx)/2, ((rc.bottom + rc.top - sz.cy)/2) - 30, szMsg, _tcslen(szMsg));
//szMsg = "Below, we can see Factorial of 10 in HEX radix:";
szMsg = "Ниже, мы можем увидеть факториал числа 10 в DEC и HEX системе счисления:";
GetTextExtentPoint32(hdc, szMsg, _tcslen(szMsg), &sz);
TextOut(hdc, (rc.right + rc.left - sz.cx)/2, ((rc.bottom + rc.top - sz.cy)/2) - 10, szMsg, _tcslen(szMsg));
ltoa(Factorial(10), temp, 10);
GetTextExtentPoint32(hdc, LPCTSTR(temp), _tcslen(LPCTSTR(temp)), &sz);
TextOut(hdc, (rc.right + rc.left - sz.cx)/2 - sz.cx*2/3, ((rc.bottom + rc.top - sz.cy)/2) + 10, LPCTSTR(temp), _tcslen(LPCTSTR(temp)));
ltoa(Factorial(10), temp, 16);
GetTextExtentPoint32(hdc, LPCTSTR(temp), _tcslen(LPCTSTR(temp)), &sz);
TextOut(hdc, (rc.right + rc.left - sz.cx)/2 + sz.cx*2/3, ((rc.bottom + rc.top - sz.cy)/2) + 10, LPCTSTR(temp), _tcslen(LPCTSTR(temp)));
// TODO: добавьте любой код отрисовки...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Обработчик сообщений для окна "О программе".
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
INT_PTR CALLBACK View(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
// Resource.h
#define IDC_MYICON 2
#define IDD_KURSOVIK_DIALOG 102
#define IDS_APP_TITLE 103
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_KURSOVIK 107
#define IDI_SMALL 108
#define IDC_KURSOVIK 109
#define IDR_MAINFRAME 128
#define IDD_VIEW 129
#define ID_VIEW_MYNEWDIALOG 130
#define BTN1 131
#define BTN2 132
#define BTN3 133
#define BTN4 134
#define BTN5 135
#define BTN6 136
#define factorial 137
Руководство пользователя.
При запуске программы появится следующее окно:
в котором продемонстрированы основные и часто использованные функции интерфейса WIN API. В Данном окне продемонстрированы статические элементы приложения, такие, как текст, который всегда будет рисоваться в центре нашего окна, чтобы убедиться в этом можно попробовать изменить размеры нашего окна. Так же, к статическим элементам относятся поля ввода и кнопки, которые так же размещены в нашем окне, но они размещены относительно, т.е. при изменении размеров окна, она их габариты и размеры останутся неизменными. К динамическим элементам относятся диалоговые окна и выпадающие списки.
Каждой статической кнопке соответствует поле ввода, кнопки InPut - являются кнопками подтверждения ввода переменных, с которыми можно провести некоторые операции, которые представлены так же в виде кнопок.
5.2 Приложение 2
Header.h
#include<windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <glut.h>
HWND hWnd;
HGLRC hGLRC;
HDC hDC;
double xSide=0.0;
double ySide=0.0;
double zSide=0.0;
double xSideO=0.0;
double ySideO=0.0;
double zSideO=0.0;
double Scale=1.0;
double ScaleO=1.0;
double TurnX=0.0;
double TurnY=0.0;
double TurnZ=0.0;
double TurnXO=0.0;
double TurnYO=0.0;
double TurnZO=0.0;
int objCount=1;
MSG msg;
bool keys[256];
GLuint filter; // Используемый фильтр для текстур
GLuint fogMode[]= { GL_EXP, GL_EXP2, GL_LINEAR }; // Хранит три типа тумана
GLuint fogHint[]= { GL_NICEST, GL_FASTEST, GL_DONT_CARE };
GLuint fogfilter= 1; // Тип используемого тумана
GLuint foghint= 2;
GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 1.0f}; // Цвет тумана
double fogDensity = 0.1f;
API.cpp
#include "Header.h"
BOOL SetWindowPixelFormat(HWND);
BOOL display (HWND);
BOOL resize (int, int);
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
void newRotate (float a, float x, float y, float z)
{
float m[16];
glGetFloatv (GL_MODELVIEW_MATRIX, m);
glLoadIdentity ();
glRotatef (a,x,y,z);
glMultMatrixf (m);
}
void newTranslate (float x, float y, float z)
{
float m[16];
glGetFloatv (GL_MODELVIEW_MATRIX, m);
glLoadIdentity ();
glTranslatef (x,y,z);
glMultMatrixf (m);
}
void DispalyList () {
glNewList(1,GL_COMPILE);
glBegin(GL_LINES);
glColor3d(15.0,0.0,0.0);
glVertex3d(-50.0, 0.0, 0.0);
glVertex3d(50.0, 0.0, 0.0);
glEnd();
glBegin(GL_LINES);
glColor3d(0.0,15.0,0.0);
glVertex3d(0.0, -50.0, 0.0);
glVertex3d(0.0, 50.0, 0.0);
glEnd();
glBegin(GL_LINES);
glColor3d(15.0,15.0,15.0);
glVertex3d(0.0, 0.0, -50.0);
glVertex3d(0.0, 0.0, 50.0);
glEnd();
glEndList();
glNewList(2,GL_COMPILE);
glPushMatrix();
glTranslated(0.5,0,0);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(-0.5,0,0);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(0,0.5,0);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(0,-0.5,0);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(0,0,0.5);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(0,0,-0.5);
glColor3d(1,0,0);
glutSolidSphere(1.3,20,20);
glPopMatrix();
glEndList();
glNewList(3,GL_COMPILE);
glPushMatrix();
glTranslated(1.5,1.5,1.5);
glColor3d(1,1,1);
glutSolidSphere(0.8,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(-1.5,-1.5,-1.5);
glColor3d(1,1,1);
glutSolidSphere(0.8,20,20);
glPopMatrix();
glEndList();
glNewList(4,GL_COMPILE);
glPushMatrix();
glTranslated(3,3,3);
glColor3d(1,0,1);
glutSolidSphere(0.8,20,20);
glPopMatrix();
glPushMatrix(); // сохраняем текущие координаты
glTranslated(-3,-3,-3); // сдвигаемся по оси Х
glColor3d(0,1,1);
glutSolidSphere(0.9,20,20); // рисуем сферу
glPopMatrix(); // возвращаемся к старой системе координат
glPushMatrix();
glTranslated(-3,0,0);
glColor3d(1,1,0);
glutSolidSphere(0.8,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(3,0,0);
glColor3d(0,0,1);
glutSolidSphere(0.7,20,20);
glPopMatrix();
glEndList();
glNewList(5,GL_COMPILE);
glPushMatrix();
glTranslated(-4,4,4);
glColor3d(1,1,0);
glutSolidSphere(0.5,20,20);
glPopMatrix();
glPushMatrix(); // сохраняем текущие координаты
glTranslated(4,-4,-4); // сдвигаемся по оси Х
glColor3d(1,0,0);
glutSolidSphere(0.4,20,20); // рисуем сферу
glPopMatrix(); // возвращаемся к старой системе координат
glPushMatrix();
glTranslated(0,-4,0);
glColor3d(0,1,0);
glutSolidSphere(0.3,20,20);
glPopMatrix();
glPushMatrix();
glTranslated(0,4,0);
glColor3d(0,1,1);
glutSolidSphere(0.6,20,20);
glPopMatrix();
glEndList();
}
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR str,int nWinMode){
WNDCLASS wclass;
wclass.hInstance=hThisInst;
wclass.cbClsExtra = 0;
wclass.cbWndExtra = 0;
wclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wclass.hIcon = NULL;
wclass.lpfnWndProc = WindowFunc;
wclass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wclass.lpszMenuName = NULL;
wclass.hCursor = LoadCursor(NULL,IDC_ARROW);
wclass.lpszClassName = L"Window";
RegisterClass(&wclass);
hWnd = CreateWindowA("Window", "Win API WND",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
50, //CW_USEDEFAULT
50, //CW_USEDEFAULT
800, //CW_USEDEFAULT
800, //CW_USEDEFAULT
HWND_DESKTOP, NULL, hThisInst, NULL);
if (!hWnd) return 0;
ShowWindow(hWnd,nWinMode);
Подобные документы
Исследование теоретических аспектов разработки программы посредством использования Visual Basic. Анализ достоинств и недостатков данного языка программирования. Изучение особенностей создания интерфейса приложения. Основные этапы реализации программы.
практическая работа [460,6 K], добавлен 22.01.2013Интерфейс API, реализация функций API на уровне ОС, системы программирования и с помощью внешних библиотек. Характеристики сетевого интерфейса прикладного программирования Winsock, особенности его применения в операционных системах UNIX и Windows.
контрольная работа [74,2 K], добавлен 04.06.2015Исследование базовых концепций программирования приложений под операционную систему Windows. Изучение истории создания универсального языка программирования Си. Разработка графического пользовательского интерфейса. Обзор правил игры и алгоритма работы.
курсовая работа [58,2 K], добавлен 09.11.2012Техника создания графики при помощи API функций, экспортируемых библиотекой GDI32.DLL. Разработка на языке программирования С++ в среде программирования Microsoft Visual C++ программы для отображения часов реального времени в цифровом и аналоговом виде.
курсовая работа [2,8 M], добавлен 27.01.2010Теоретические основы написания Windows-приложений с использованием библиотеки MFC. Основы программирования под Windows. Проектирование приложений в среде Microsoft Visual C++. Описание логической структуры приложения, его функциональное назначение.
курсовая работа [1,3 M], добавлен 12.12.2011Разработка Windows-приложений с использованием библиотеки MFC. Базовый набор классов, написанных на языке С++ и предназначенных для упрощения процесса программирования под Windows. Фундаментальные идеи объектно-ориентированного программирования.
курсовая работа [348,1 K], добавлен 02.07.2011Использование скриптового языка программирования для разработки web-приложений (сценариев). Изучение основ объектно-ориентированного программирования в языке PHP. Ознакомление со специальными методами для работы с классами. Назначение интерфейсов.
контрольная работа [25,1 K], добавлен 14.03.2015Разработка программного продукта с помощью языка программирования Visual Basic. Описание интерфейса пользователя и возможностей программы. Исходный код основных модулей. Программа, демонстрирующая основные возможности диаграмм и среды Visual Basic.
контрольная работа [989,9 K], добавлен 29.03.2011Исследование возможностей и областей использования языка программирования JavaScript. Сравнительный анализ языков программирования JavaScript и PHP. Разработка интерактивного Web-приложения на примере теста по теме "Программирование на языке Delphi".
практическая работа [26,0 K], добавлен 04.02.2015Программная реализация настольного приложения с использованием языка программирования C#. Проектирование и структура пользовательского интерфейса, требования к нему и оценка функциональности. Разработка руководства пользователя и его использование.
курсовая работа [297,6 K], добавлен 10.03.2015