Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по пользовательскому режиму ОС Windows Сурков Кирилл Андреевич, БГУИР 2006 (Мет пособие).doc
Скачиваний:
92
Добавлен:
15.06.2014
Размер:
404 Кб
Скачать
☆

Int SetStretchBltMode(

HDC hdc, // handle to DC

int iStretchMode // bitmap stretching mode

);

Int GetStretchBltMode(

HDC hdc // handle to DC

);

Растровая операция – способ комбинирования пикселей исходного изображения с пикселями поверхности отображения целевого контекста устройства. При масштабировании в сторону сжатия некоторые цвета могут пропадать. При растяжение то проблем не существует. При сжатии возможно 3 способа сжатия.

РИСОВАНИЕ ТОЧЕЧНЫХ РИСУНКОВ ПРОГРАММНЫМ СПОСОБОМ

После создания контекста виртуального устройства CreateCompatibleDC() поверхность отображения в этом контексте устройства имеет нулевые размеры. Если в полученном контексте устройства необходимо нарисовать изображение, то сначала необходимо создать в контексте устройства поверхность отображения (точечный рисунок). И установить его в контексте устройства. Для создания рисунка:

HBITMAP CreateCompatibleBitmap(

HDC hdc, // handle to DC

int nWidth, // width of bitmap, in pixels

int nHeight // height of bitmap, in pixels

);

После вызова этой функции нужно не забыть вызвать SelectObject. Созданный точечный рисунок – DDB. Часто при работе с изображениями (растровыми) применяется:

BOOL PatBlt(

HDC hdc, // handle to DC

int nXLeft, // x-coord of upper-left rectangle corner

int nYLeft, // y-coord of upper-left rectangle corner

int nWidth, // width of rectangle

int nHeight, // height of rectangle

DWORD dwRop // raster operation code

);

Она служит для заливки битового образа некоторым цветом. Цвет определяется цветом кисти.

РЕСУРСЫ WINDOWS

Ресурсы ОС – специальный тип данных, предназначенный для представления и использования в прикладных программах стандартных элементов пользовательских интерфейсов. Стандартные типы ресурсов Windows:

  • Menu.

  • Accelerator.

  • Icon.

  • Cursor.

  • Bitmap

  • String Table.

  • Dialog Box.

  • Font.

  • Version Info.

Программист может создавать свои собственные типы ресурсов. Ресурсы – отдельны от кода и данных. Ресурсы обладают свойством разделяемости. Несколько программ могут использовать копию одного ресурса. Загрузка ресурса из внешнего ехе файла выполняется с помощью

HGLOBAL LoadResource(

HMODULE hModule, // module handle

HRSRC hResInfo // resource handle

);

В ехе файле ресурс может идентифицироваться числом (0..65536) или строкой. Ресурсы создаются на языке описания ресурсов и располагается в фалах *.rc. Создается с помощью компилятора ресурсов Resourse COMPILER создаются rc. При сборке ехе файла rc файлы дописываются в ехе файл. Акселератор – трансляция нажатий клавиатуры. TranslateAccelerator помещается в цикл обработки сообщений. Эта функция работает на основе таблицы акселераторов (из ресурсов). Акселератор – комбинация клавиш.

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

ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ

DLL представляет собой дополняемый модуль ОС (обычно DLL), код и ресурсы которого могут использоваться в составе других динамических библиотек и приложений. DLL – программа с множеством точек входа. В отличие от статической библиотеки, которая включается в выполняемый exe модуль на этапе сборки, динамическая библиотека собирается в виде отдельно выполняемого модуля. Использование динамической библиотеки выполняется одним из двух способов:

  • Статического импорта

  • Динамического импорта

Для создания DLL в разных языках используются разные средства. В C++:

Создание:

__declspec(dllexport) int Min(int X, int Y);

При статическом импорте подключение DLL осуществляется наподобие обычных библиотек. Разница лишь в описании внешней функции. В C++: __declspec(dllimport) int Min(int X, int Y). Такого описания функции в исходном тексте функции не достаточно. Система требует подключения так называемой библиотеки импорта (lib-файла) при компоновке программы. Библиотека импорта создается системой Visual C автоматически при компиляции DLL. При подключении DLL необходимо знать еще один важный параметр – соглашение о вызове подпрограмм. Существуют следующие соглашения о вызове подпрограмм в ОС Windows:

  • __cdecl. Параметры передаются на стек в обратном порядке. За освобождение стека после вызова подпрограммы отвечает вызывающая программа.

  • __pascal. Передача параметров на стек в прямом порядке. Освобождение стека осуществляет сама вызванная подпрограмма.

  • __stdcall. Соглашение для стандартных DLL Windows. Передача параметров на стек происходит в обратном порядке. Освобождение стека выполняет вызванная подпрограмма.

  • __register. Передача параметров преимущественно через регистры процессора. Это не используется при создании DLL (это не стандартизировано).

Соглашение о вызове должно записываться в прототипе функции.

__declspec(dllexport) int __cdecl Min(int X, int Y);

Разные способы передачи параметров создают много трудностей при создании/использовании DLL. Главная трудность состоит в правильном создании и использовании DLL c вызовом подпрограмм __stdcall. В Office все подключается по __stdcall. В системе программирования VS98 использование соглашение о вызовах __stdcall накладывает определенные ограничения (правила) именования функций в DLL. Функция в DLL получает имя: _<имя_функции>@<количество_байт_параметров>. _Min@8.

Существует еще один способ создания библиотеки import. Библиотека импорта может создаваться на основе существующей DLL библиотеки. Она создается непосредственно из файла описания DLL библиотеки. Файл описания DLL имеет расширение DEF, является текстовым файлом, в котором перечислены имена функций экспортируемых из DLL. Справа от имени функции через разделитель записывается номер функции в DLL.

EXPORTS

_Min@8 @1

_Max@8 @2

Этот номер может использоваться для импорта функции. Лучше никогда не использовать вызов по номерам. Она устаревшая, непереносимая, галимая и т.д. DEF-файл создается вручную. При создании, использовании DLL в VC++. Технология использования DEF-файл считается устаревшей. Однако для создания DLL в среде VC++ со стандартным вызовом __stdcall и подключением этой библиотеки в другой среде программирования избежать DEF не получается. Ухудшающее обстоятельство: рекомендуется создавать DEF-файл вручную и включать его в проект VC++. Компилятор умеет обнаруживать в проекте DEF-файл и использовать его для именования функций в DLL. DEF-файл, который необходимо включать в проект, и DEF-файл передаваемый пользователю DLL (заказчику) отличаются. Первый файл записывается без _ & @8, т.е. Min.

DEF(1)

DEF(2)→LIB

Динамический импорт. Если при статическом импорте загрузку DLL в память обеспечивает ОС, то при динамическом импорте это делает программист вручную. Загрузить DLL можно с помощью функции:

HANDLE LoadLibrary(LPCSTR libFileName)

Имя файла отыскивается на диске в следующей последовательности:

  1. Текущий каталог

  2. Каталог системы Windows

  3. Системный каталог Windows (system32)

  4. В каталоге, содержащем исполняемый файл программы, вызвавшей функцию LoadLibrary

  5. Во всех каталогах перечисленных в переменной среды PATH

  6. В списке сетевых каталогов

После завершения работы с DLL вызывается void FreeLibrary(HANDLE);

Функция void* GetProcAddress(HANDLE,LPCSTR); По имени функции или ее номеру. Ответственность за правильность вызова лежит на программисте.

ФУНКЦИЯ ВХОДА/ВЫХОДА

DLL может иметь НЕОБЯЗАТЕЛЬНУЮ функцию BOOL WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpImpload), которая вызывается системой Windows автоматически в 4х случаях. hInst – дескриптор загружаемого модуля, который равен адресу, с которого DLL проецируется в память. dwReason – причина вызова функции (одна из 4х причин). lpImpload - показывает, как DLL загружается в память (методом неявной загрузки – статический импорт, или методом явной загрузки – динамический импорт). 4 причины вызова:

  1. DLL_PROCESS_ATTACH – при первой загрузке DLL каким-либо потоком

  2. DLL_THREAD_ATTACH – подключение потока. Когда происходит создание нового потока, который использует DLL. Это вызывается для каждого создаваемого потока. Для главного потока не вызывается.

  3. DLL_THREAD_DETACH - при завершении потока с помощью функции ExitThread.

  4. DLL_PROCESS_DETACH – при завершении процесса, если завершение потока включает завершение процесса. Если завершение процесса выполняется с помощью ExitProcess.

Вызов Exit… приводит к упорядоченному завершению.

Terminate… - просто вырубает поток, и могут не освободиться ресурсы (TerminateThread).

Следует избегать создания DllMain, т.к. она не является мобильным способом работы с DLL.

ЭКСПОРТ И ИМПОРТ ДАННЫХ

Вместо использования стандартных директив компилятора С++:

__declspec(dllexport)

__declspec(dllimport)

существует возможность альтернативного создания DLL с помощью ключевого слова extern. Его использование требует включение в проект DEF-файла. Этот способ в Win C++ считается устаревшим, т.к. не позволяет экспортировать/импортировать данные. При использовании нового способа данные экспортировать можно (как и функции):

__declspec(dllexport) int y;

__declspec(dllimport) int x;

На экзамене: каким образом происходит экспорт/импорт данных (механизм).

Рекомендуется избегать экспорта/импорта данных (этот подход является непереносимым с платформы на платформу).

ЗАГРУЗКА DLL В ПАМЯТЬ

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

ПОЛЕЗНЫЕ ФУНКЦИИ ПРИ РАБОТЕ В DLL И ИСПОЛНЯЕМЫМИ МОДУЛЯМИ

HMODULE GetModuleHandle(

LPCTSTR lpModuleName // module name

);

Проверяет, загружена ли библиотека в память и возвращает дескриптор этого модуля (если загружен).

DWORD GetModuleFileName(

HMODULE hModule, // handle to module

LPTSTR lpFilename, // file name of module

DWORD nSize // size of buffer

);

Возвращает полное имя загруженного модуля. Является незаменимой при анализе командной строки.

РАСШИРЕНИЕ СИСТЕМЫ GDI

В Win2000/XP существует расширение системы GDI (GDIX). GDIX обеспечивает создание и использование мультимедийных окон. Технология называется BuiltInMultimedia (BIM). Для создания окна с поддержкой мультимедиа: HANDLE CreateBIMBox(<параметры CreateWindow>, + параметры описывающие мультимедийность DWORD AlphaBlend, int alpha, int beta, int gamma); Эти параметры обеспечивают работу с 3D графикой. void DeleteBIMBox (HANDLE); При использовании GDIX код ошибки можно получить с помощью вызова функций GetLastBIMBoxError();

ОБЪЕКТЫ ЯДРА

Объекты ядра – это объекты ОС Windows, которые принадлежат ядру, а не процессу. Освобождение этих объектов в случае экстренного завершения процесса гарантируется ОС. Объектами ядра Windows являются:

  1. Файлы

  2. Процессы

  3. Потоки

  4. Проекции файлов в память

  5. Mutex

  6. Семафоры

  7. События

  8. Каналы

  9. Почтовые ящики (системные, а не пользовательские)

Программист не имеет явного доступа к объектам ядра. Для работы с объектами используется только API ОС. В ОС предусмотрено, что один и тот же объект ядра может использоваться несколькими процессами. Для этого в объекте ядра поддерживается счетчик числа пользователей объектов.

???

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

typedef struct _SECURITY_ATTRIBUTES {

DWORD nLength; //Ее размер (структуры)

LPVOID lpSecurityDescriptor; //Дескриптор защиты (указатель на другую структуру )

BOOL bInheritHandle; //Разрешает/запрещает наследования объекта ядра дочерними процессами

} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES;

Создание объекта ядра происходит с помощью функций Create… Все эти функции возвращают HANDLE.

BOOL CloseHandle(HANDLE). Дублирование дескриптора объекта ядра осуществляется функцией: DuplicateHandle.

МНОГОЗАДАЧНОСТЬ WINDOWS

Предыдущие версии Win (до Win95) поддерживали «не вытесняющую многозадачность». Что дает многозадачность:

  • Уменьшается простой процессора и загрузка вычислительных ресурсов становиться равномерной.

  • Пользователь быстрее получает результат от менее трудоемких задач.

  • Зацикливание одной задачи не приводит к блокированию всех остальных (типичное преимущество пакетной обработки (но не только)).

Windows поддерживает «2х уровневую» систему многозадачности. Она обеспечивает выполнение не только программ (процессов) но и подпрограмм (потоков процессов).

Процесс – это выполняемая программа, имеющая собственное виртуальное адресное пространство, код, данные, а также потребляющие ресурсы ОС (файлы, объекты синхронизации…). В ОС Windows процессы порождаются запуском новых экземпляров приложений (существенно отличается от способа создания процессов в ОС Unix, в котором кроме указанного способа существует еще 1 способ: fork). Поток (thread) – подпрограмма, выполняемая параллельно с главной программой. Главная программа, вообще говоря, тоже является потоком, но этот поток ассоциирован с целым процессом. Поток может выполнять любую подпрограмму. И одна и та же подпрограмма может выполняться несколькими потоками. Все потоки имеют одно и тоже виртуальное адресное пространство, обращаются к одним и тем же глобальным переменным и ресурсам своего процесса. Все потоки процесса, включая первичный поток, для ОС являются равноправными. Поток – это базовая единица, которой ОС выделяет процессорное время.

Экзамен:Четвертое достоинство использования многозадачности.

Процесс создается функцией:

BOOL CreateProcess(

LPCTSTR lpApplicationName, // name of executable module

LPTSTR lpCommandLine, // command line string

LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD

LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD

BOOL bInheritHandles, // handle inheritance option

DWORD dwCreationFlags, // creation flags

LPVOID lpEnvironment, // new environment block

LPCTSTR lpCurrentDirectory, // current directory name

LPSTARTUPINFO lpStartupInfo, // startup information

LPPROCESS_INFORMATION lpProcessInformation // process information

);

Функция запускает программу с параметрами заданными во втором параметре, отыскивая исполняемый модуль в следующем порядке:

  1. Каталог EXE-файла вызывающего процесса.

  2. Текущий каталог вызываемого процесса.

  3. Системный каталог Windows.

  4. Основной каталог Windows.

  5. Каталоги, перечисленные в переменной окружения PATH.

Первый параметр служит для совместимости со стандартом POSIX и задает реальное имя файла для запуска приложения. Как правило первый параметр = NULL. Если !=NULL, то запускается программа с именем lpApplicationName, и ей передаются параметры lpCommandLine. 3 и 4 – атрибуты защиты для процесса и первичного потока. 5 – определяет, унаследует ли вновь созданный процесс описатели родительского процесса. 6 – описывает как создавать процесс, например DEBUG_PROCESS – процесс запускается с целью отладки; CREATE_SUSPEND – создается с приостановленным первичным потоком. 7 - ? 8 – текущий каталог. 9 – эта структура служит для описания параметров главного окна (консольного окна), создаваемого процессом. Результат работы функции помещается в последний параметр.

typedef struct _PROCESS_INFORMATION {

HANDLE hProcess; //описатель процесса

HANDLE hThread; //описатель потока в пределах текущего процесса

DWORD dwProcessId; //уникальный идентификатор процесса в пределах системы

DWORD dwThreadId; //уникальный идентификатор потока в пределах системы

} PROCESS_INFORMATION;

При выполнении программы можно узнать описатели и уникальные идентификаторы своего процесса / потока с помощью функций:

HANDLE GetCurrentProcess(VOID); //возвращает псевдоописатели процесса (они действительны только в пределах //текущего процесса)

HANDLE GetCurrentThread(VOID); //возвращает псевдоописатели потока (они действительны только в пределах //текущего потока)

DWORD GetCurrentProcessId(VOID); //уникальный идентификатор в системе

DWORD GetCurrentThreadId(VOID); //уникальный идентификатор в системе

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

BOOL DuplicateHandle(

HANDLE hSourceProcessHandle, // handle to source process

HANDLE hSourceHandle, // handle to duplicate

HANDLE hTargetProcessHandle, // handle to target process

LPHANDLE lpTargetHandle, // duplicate handle

DWORD dwDesiredAccess, // requested access

BOOL bInheritHandle, // handle inheritance option

DWORD dwOptions // optional actions

);

Вызов CloseHandle(…) с псевдоописателями ничего не делает. В некоторых задачах бывает необходимо дождаться завершения дочернего процесса, это может быть сделано с помощью функции WaitForSingleObject(…) с передачей этой функции описателя процесса, полученного после вызова CreateProcess(…). Затем нужно не забыть вызвать функцию CloseHandle(…) для потока и процесса (сначала потока, потом процесса). Завершение процесса выполняется одной из двух функций: