close

Вход

Забыли?

вход по аккаунту

?

301 ТК Тематика КР укр

код для вставкиСкачать
 1$.Моніторинг змін файлів і каталогів.
Розробити програму, що дозволяє стежити за змінами (створення, запис, видалення файлів) визначеного користувачем каталогу.
Пояснення і рекомендації
Зміна папки.
Є кілька простих викликів API, за допомогою яких Ви можете запросити у файлової системи, щоб вона вибірково повідомила Вам щодо змін для файлів і папок. Як тільки Ви встановлюєте таку вахту, ваш потік може очікувати приходу подій. Файлова система відреагує на подію, як тільки вона виявить вид зміни, за яким ви спостерігаєте.
Без подальшої суєти успадкуємо FolderWatcher з ActiveObject. Задамо як джерело повідомлення - подія, а в якості приймача повідомлення - дескриптор до вікна, що відповідає на повідомлення. Вихідна подія встановлена в конструкторі FolderWatcher. Важливо також запустити утримуваний потік наприкінці конструктора.
class FolderWatcher : public ActiveObject {
public:
FolderWatcher (char const * folder, HWND hwnd)
: _notifySource (folder),
_hwndNotifySink (hwnd) {
strcpy (_folder, folder);
_thread.Resume ();
}
~FolderWatcher () {
Kill ();
}
private:
void InitThread () {}
void Loop ();
void FlushThread () {}
FolderChangeEvent _notifySource;
HWND _hwndNotifySink;
char _folder [MAX_PATH];
};
Всі дії в ActiveObject відбуваються усередині методу Loop. Тут ми встановлюємо "нескінченний" цикл, у якому потік повинен очікувати подію. Коли подія відбувається, ми перевіряємо прапорець _isDying (як звичайно) і посилаємо спеціальне повідомлення WM_FOLDER_CHANGE вікну, що має справу з повідомленнями. Це спеціально визначене нами повідомлення для передачі повідомлення про папку від одного потоку іншому.
Відбувається наступне: утримуваний потік робить інший виклик API, щоб дозволити файловій системі, довідатися, що вона має потребу в інших повідомленнях. Потім керування повертається до потоку, що перебуває в стані очикування. Одночасно Windows одержує наше повідомлення WM_FOLDER_CHANGE із черги повідомлень і посилає його віконній процедурі приймаючого вікна.
UINT const WM_FOLDER_CHANGE = WM_USER;
void FolderWatcher::Loop () {
for (;;) {
// Wait for change notification
DWORD waitStatus = WaitForSingleObject(_notifySource, INFINITE);
if (WAIT_OBJECT_0 == waitStatus) {
// If folder changed
if (_isDying) return;
PostMessage (_hwndNotifySink,
WM_FOLDER_CHANGE,
0,
(LPARAM) _folder);
// Continue change notification
if (!_notifySource.ContinueNotification ()) {
// Error: Continuation failed
return;
}
}
else {
// Error: Wait failed
return;
}
}
}
Розглянемо, що відбувається у віконній процедурі у відповідь на наше спеціальне повідомлення. Ми викликаємо метод Контролера OnFolderChange. Цей метод може робити все, що ми захочемо. У Провіднику (Explorer) він регенерує відображення вмісту папки, що ми спостерігаємо. У нашому прикладі він тільки викликає просте вікно повідомлення. Зверніть увагу, що ми передаємо ім'я зміненої папки як LPARAM. Зовсім неважливо, як визначити WPARAM й LPARAM, у повідомленні, це обумовлено користувачем.
Між іншим, Спостерігач Папки - тільки частина Контролера.
case WM_FOLDER_CHANGE:
pCtrl->OnFolderChange (hwnd, (char const *) lParam);
return 0;
void Controller::OnFolderChange (HWND hwnd, char const * folder) {
MessageBox (hwnd, "Change Detected, "Folder Watcher",
MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);
}
class Controller {
public:
Controller(HWND hwnd, CREATESTRUCT * pCreate);
~Controller ();
void OnFolderChange (HWND hwnd, char const *folder);
private:
FolderWatcher _folderWatcher;
}
Тепер ми знаємо, як мати справу з повідомленням, поглянемо на їхні джерела - події і файли, що змінюються. Об'єкт події створений файловою системою у відповідь на FindFirstChangeNotification. Дескриптор цієї події повернутий з виклику. Ми запам'ятовуємо цей дескриптор і використаємо його пізніше, щоб здійснити відновлення або відмовитися від нашого інтересу до подальших повідомлень. Зверніть увагу, що ми можемо встановлювати спостереження рекурсивно, тобто, спостерігати дану папку й всі її підпапки. Ми можемо також відобразити інтерес до специфічних змін, передаючи порозрядно АБО для будь-якої комбінації наступних прапорців:
FILE_NOTIFY_CHANGE_FILE_NAME (перейменування, створення або видалення файлу) FILE_NOTIFY_CHANGE_DIR_NAME (створення або видалення каталогу (папки)) FILE_NOTIFY_CHANGE_ATTRIBUTES FILE_NOTIFY_CHANGE_SIZE FILE_NOTIFY_CHANGE_LAST_WRITE (збереження файлу) FILE_NOTIFY_CHANGE_SECURITY Для зручності ми визначили кілька підкласів від FileChangeEvent, які відповідають до деяких корисних комбінацій цих прапорців. Один з них - FolderChangeEvent, що ми використали в нашому FolderWatcher.
class FileChangeEvent {
public:
FileChangeEvent(char const *folder, BOOL recursive, DWORD notifyFlags) {
_handle = FindFirstChangeNotification (folder, recursive, notifyFlags);
if (INVALID_HANDLE_VALUE == _handle)
throw WinException("Cannot create change notification handle");
}
~FileChangeEvent () {
if (INVALID_HANDLE_VALUE != _handle)
FindCloseChangeNotification (_handle);
}
operator HANDLE () const { return _handle; }
BOOL ContinueNotification () {
return FindNextChangeNotification (_handle);
}
private:
HANDLE _handle;
};
class FolderChangeEvent : public FileChangeEvent {
public:
FolderChangeEvent (char const * folder)
: FileChangeEvent (folder, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME) {}
};
class TreeChangeEvent : public FileChangeEvent {
public:
TreeChangeEvent (char const * root)
: FileChangeEvent (root, TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME
| FILE_NOTIFY_CHANGE_DIR_NAME) {}
};
Тепер необхідно просто узагальнити цей приклад.
2$. Процеси й потоки.
Розробити програму, що демонструє механізми підготовки, створення й керування процесами. Програма-оболонка повинна вміти створювати процес, очікувати завершення процесу в окремому потоці, а також завершувати запущені дочірні процеси. Пояснення і рекомендації
Варіант реалізації.
Програма, що наприкінці кожної навчальної пари видає повідомлення про те, що час роботи за комп'ютером минуло, і він повинен бути виключений. Якщо після закінчення 3 хвилин після видачі повідомлення комп'ютер продовжує працювати, то програма виключає його примусово.
При запуску програми в правому нижньому куті з'являється значок. При натисканні лівої кнопки миші по значку відкривається головне вікно програми (Рис 2.1). Рис 2.1 Головне вікно програми
Головне вікно містить інформацію про автора програми, а також показує час у яке програма виключить комп'ютер примусово. Для того щоб знову згорнути програму потрібно клацнути лівою кнопкою миші в будь-якому місці головного вікна програми. Розміри вікна зафіксовані й змінити їх за допомогою мишки не можна. Примітка: дана програма повинна бути занесена в "автозавантаження". При включенні комп'ютера після 18 годин 40 хвилин - до 7 годин 35 хвилин програма видасть наступне повідомлення (рис 2.2). Рис 2.2 Повідомлення від програми
Після натискання кнопки "ОК" програма завершить свою роботу.
При включенні комп'ютера з 7 годин 35 хвилин до 18 годин 40 хвилин програма не буде видавати повідомлень до кінця пари. Коли локальне (системне) час буде відповідати кінцю пари програма видасть наступне повідомлення (рис 2.3).
Рис 2.3 Повідомлення від програми по закінченні пари
Якщо після закінчення 3 хвилин після появи цього повідомлення комп'ютер не буде виключений, то програма виключить його самостійно.
Примітка: вимикання комп'ютера програмою може бути не здійснене через яку-небудь помилку або збій. У цьому випадку програма видасть повідомлення (рис 2.4).
Рис 2.4 Повідомлення від програми при помилці
Функції й прапори використовувані для вимикання комп'ютера:
Функція ExitWindowsEx закриває систему, або закриває й повторно перезапускає систему. Вона посилає повідомлення WM_QUERYENDSESSION всім додаткам, щоб визначити, чи можуть вони бути закінчені.
&nbspEWX_POWEROFF - Закриває систему й виключає живлення. Система повинна підтримати особливість " power-off feature ". &nbspWindows для NT/2000 або пізніших: процес запиту повинен мати SE_SHUTDOWN_NAME привілей. Функція LookupPrivilegeValue відновлює в локальному масштабі унікальний ідентифікатор (LUID) використовуваний на зазначеній системі, щоб представити зазначена назва(ім'я) привілею.
Функція OpenProcessToken відкриває символ доступу, пов'язаний із процесом.
Функція AdjustTokenPrivileges дозволяє або забороняє привілею в зазначеному символі доступу. Надання можливості або виведення з ладу привілеїв у символі доступу вимагає TOKEN_ADJUST_PRIVILEGES доступу.
Структура TOKEN_PRIVILEGES містить інформацію щодо набору привілеїв для символу доступу.
3$. Механізми міжпроцесних взаємодій (IPC).
Розробити програму, що реалізує функції обміну повідомленнями з іншою такою же програмою за допомогою бібліотек, що підключають динамічно (DLL).
Пояснення і рекомендації
Бібліотеки, що завантажують динамічно (DLL) - виконуваний програмний модуль Microsoft Windows, що завантажує тільки при необхідності (по запиті).
Процес створення динамічної бібліотеки проходить у два етапи. Перший з них полягає в створенні файлу-заголовка (*. h ), що визначає основні властивості проекту. Для цього необхідно створити новий проект, вибравши як тип проекту Dinamic Linked Library , після чого визначити "порожній проект" ( empty project ). Потім створити файли проекту. Нехай ім'я проекту буде funlib .
Далі створити файл заголовка - funlib . h (за допомогою меню File\New..., вибравши файл-заголовок). У цей файл включити наступний текст : // File funlib.h #define EXPORT extern "C" __declspec(dllexport) EXPORT BOOL CALLBACK return333(); EXPORT int CALLBACK MyInc(int i); Перший рядок файлу визначає деяку директиву EXPORT , що буде використатися для підключення сервісів, необхідних для створення посилань на експортовані функції (тобто функції, які можуть бути використані або імпортовані іншими програмами). Наступні два рядки є попереднім оголошенням функцій, директива EXPORT і робить їхніми динамічними функціями.
Тепер залишилося написати текст програми *. cpp:
//funlib.cpp #include #include #include "fulib.h" int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { return TRUE; } EXPORT BOOL CALLBACK EdrCenterText() { return 333; } EXPORT int CALLBACK MyInc(int i) { return ++i; } &nbspDllMain - аналог функції main для консольних додатків і функції WinMain для додатків, написаний під Windows. Ця функція автоматично викликається при завантаженні будь-який dll . Значення, що повертає, TRUE свідчить про успішне завантаження й ініціалізацію внутрішніх ресурсів. Створимо звичайний консольний додаток Win32 і назвемо його usedll . Для використання funlib.dll необхідно виконати наступне: помістити в каталог проекту файли funlib.h і funlib.lib. Другий файл є результатом компіляції попереднього проекту і його можна знайти в робочому каталозі. Крім цього, у меню Project\Settings\Link, категорія General, поле Object\library modules необхідно вписати ім'я файлу-бібліотеки для організації настроювань зв'язування. Текст програми usedll.cpp наведений нижче.
// usedll.cpp : Defines the entry point for the console application. #include "stdafx.h" #include "iostream.h" #include "funlib.h" int main(int argc, char* argv[]) { cout << return333() << endl; cout << MyInc(5) << endl; return 0; } Дана програма виконається якщо в каталозі, де розташовується файл usedll.exe розташований файл динамічної бібліотеки funlib.dll . У результаті виконання даної програми на екрані ви побачите наступну картину:
333 6 У висновку слід зазначити, що використовуючи механізми динамічного зв'язування, можна визначати не тільки функції, але змінні й навіть класи.
Другий спосіб використання DLL.
Розглянутий вище спосіб написання програми, що використає підключає динамічно бібліотеки придатний тільки в тому випадку, коли ви самі створили бібліотеки, або, принаймні, ви маєте в наявності файл бібліотеки *.lib . Однак, дуже часто трапляється так, що є тільки *.dll (або вона тільки планується). У цьому випадку можна використати інший спосіб, що не вимагає для компіляції проекту наявності файлів, пов'язаних з викликуваної *. dll .
Застосування даного способу полягає в наступному: • Створюються прототипи функцій, що мають ті ж параметри, що й необхідні функції усередині *. dll . Для нашого приклада це буде виглядати так:
typedef BOOL (WINAPI * Inc)(int i); • Оголошується змінна типу покажчика на дану функцію: Inc p ; • У програмі здійснюється завантаження бібліотеки: HINSTANCE h; h=LoadLibrary("funlib.dll");
• оголошеному покажчику на функцію привласнюється адреса функції з *. dll : p=(Inc)GetProcAddress(h,(LPCSTR)2); • Функція використається по призначенню: cout << p(6); • Після використання вивантажується з пам'яті: FreeLibrary(h); Цілком розглянутий приклад виглядає в такий спосіб: #include "stdafx.h" #include "iostream.h" #include "windows.h" HINSTANCE h; typedef int (CALLBACK * Inc)(int i); Inc p; int main(int argc, char* argv[]) { h=LoadLibrary("1111.dll"); p=(Inc)GetProcAddress(h,(LPCSTR)2); cout << p(6); FreeLibrary(h); return 0; }
Наявність *. dll потрібно тільки під час запуску *. exe файлу.
4$. Робота з реєстром.
Розробити програму, що відображає наступну інформацію з реєстру: список форматів файлів і пов'язані з ними додатка. Програма повинна дозволяти створювати нові типи файлів і зв'язувати їх із програмами. Пояснення і рекомендації
Системний реєстр - база даних, що зберігає параметри настроювання. Він містить інформацію й параметри настроювань для всіх апаратних засобів, програм, користувачів, і властивостей ПК. Щораз, коли користувач робить зміни в параметрах настроювання Панелі керування, або в асоціаціях файлів, системному настроюванню, або у встановленому програмному забезпеченні, зміни відбиваються й зберігаються в системному реєстрі.
Редактор системного реєстру (REGEDIT.EXE) включений у більшість версій Windows. Він дає можливість переглядати, шукати й редагувати дані в межах системного реєстру. Є кілька методів для запуску редактора, найпростіший - нажати на кнопку "Пуск", потім вибрати Виконати, і в поле "Відкрити:" надрукувати "regedit", і якщо редактор системного реєстру встановлений, він повинен відкритися.
Програма відображає ключі реєстру, що дозволяє додавати й змінювати ключі, додавати й змінювати значення, може бути представлена в такий спосіб (Рис 4.1).
Рис 4.1 Головне вікно програми
Наприклад, для перегляду підключей вітки "HKEY_CURRENT_CONFIG", необхідно клацнути лівою кнопкою миші за назвою вітці "HKEY_CURRENT_CONFIG".
Якщо користувачеві необхідно переглянути підключі ключа "Software", необхідно клацнути лівою кнопкою миші по підключу "Software".
Для відображення значень підключа "Fonts" необхідно двічі клацнути лівою кнопкою миші по підключу "Fonts" (Рис. 4.2).
Рис 4.2 Для додавання підключа до даного ключа необхідно клацнути правою кнопкою миші по ключі, до якого необхідно додати підключ (внаслідок чого з'явиться меню) і нажати пункт меню "Створити" - "Розділ". Поруч із ключем, до якого додали новий підключ, з'являється знак "+" (плюс) (Рис. 4.3).
Рис 4.3 Для перегляду створеного підключа необхідно клацнути лівою кнопкою миші по ключі (Рис. 4.4)
Рис 4.4 Нажавши правою кнопкою миші по підключу "New" (викликавши цим меню) можна перейменувати ключ або видалити його. Також можна для нього додати нове значення (Рис. 4.5).
Рис 4.5 Нажавши правою кнопкою миші за яким-небудь значенням, програма дозволяє змінити значення, перейменувати його, або зовсім видалити (Рис. 4.6).
Рис 4.6 Основні функції системного реєстру
Функції ПризначенняRegCloseKeyЗакриває дескриптор даного відкритого ключа. При цьому звільняються будь-які пов'язані з даним ключем системні ресурси. Закриття ключа аж ніяк не викликає затримку операцій запису, що відбуваються безпосередньо після виконання даної функції.RegCreateKeyExСтворює або відкриває підключ зазначеного ключа.RegDeleteKeyВидаляє підключ заданого ключаRegDeleteValueВидаляє значення з підключаRegEnumKeyExПерераховує підключи зазначеного відкритого ключа системного реєстру.RegEnumValueПерераховує значення зазначеного ключа, відкритого в системному реєстріRegOpenKeyExВідкриває підключ системного реєстру з необхідним типом доступу.RegQueryInfoKeyПовертає інформацію, що описує даний ключ системного реєстру.RegQueryValueExПовертає тип і дані для зазначеного імені значення, пов'язаного з відкритим ключем системного реєстру.RegSetValueExУстановлює іменоване значення будь-якого підключа системного реєстру 5$. Іменовані канали.
Розробити програму-сервер і програму клієнт для обміну повідомленнями між двома комп'ютерами за допомогою іменованих каналів. Клієнт посилає серверу запит на виконання однієї з команд: DIR, MKDIR, CD. Сервер приймає команду, виконує її, а результат відправляє клієнтові.
Пояснення і рекомендації
Іменовані канали PIPE використаються для гарантованої передачі даних по мережі. Створити іменований канал можна тільки на NT, в операційному середовищі Windows 95 можна використати іменований канал тільки з боку клієнта. Тому для перевірки даних прикладів вам потрібний NT і вище. Можна сказати, що це виділена лінія для обміну даними між процесами. В NT можна подивитися подібні виділені лінії. Тобто їхню кількість. Зайдіть в Control Panel, виберіть Server і клацніть на ньому. З'явиться вікно як нижче: Рис 5.1 Пункт Open Named Pipes може бути й не такий. Справа в тому, що даним механізмом користуються багато серйозних додатків, наприклад, MS SQL Server. Cоздадим дві прості консольні програми, які будуть эмулировать клієнта й сервера. Отже сервер: // CreateNamedPipe.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "iostream.h"
void main()
{
HANDLE hp;
hp=CreateNamedPipe("\\\\.\\pipe\\ipctest",PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE | PIPE_NOWAIT,1,0,0,NMPWAIT_USE_DEFAULT_WAIT,NULL);
if (hp!=INVALID_HANDLE_VALUE)
{
int i;
cin >> i;
}
else cout << "Error Create Name Pipe " << endl;
}
А тепер клієнт: // CreateFile.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "iostream.h"
void main()
{
HANDLE hp;
hp=CreateFile("\\\\Server\\pipe\\ipctest", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hp!=INVALID_HANDLE_VALUE)
{
int i;
cin >> i;
}
else cout << "Error pipe" << endl;
}
Як бачите для створення каналу треба викликати функцію CreateNamedPipe, а для клієнта створити файл указавши сервер Server. Ви повинні вказувати свій сервер. Тобто його ім'я в домені. А ім'я \\pipe\\ipctest повинне збігатися й у клієнті й у сервері. Це ім'я каналу. От після запуску прогаммі видно, що кількість іменованих каналів збільшилася на одиницю. Рис 5.2.Моніторинг потоку подій.
6$. Панель керування.
Аплєт повинен виводити список програм, що завантажують автоматично, і дозволяти змінювати цей список.
Пояснення і рекомендації
Автозавантаження в Windows XP
Способи автозавантаження:
Реєстр - у реєстрі автозавантаження представлене в декількох місцях: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run] - програми, які запускаються при вході в систему. Даний розділ відповідає за запуск програм для всіх користувачів системи.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce] - програми, які запускаються тільки один раз при вході користувача в систему. Після цього ключі програм автоматично віддаляються з даного розділу реєстру. Даний розділ відповідає за запуск програм для всіх користувачів системи.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx]
програми, які запускаються тільки один раз, коли завантажується система. Цей розділ використається при інсталяції програм, наприклад для запуску настроечных модулів. Після цього ключі програм автоматично віддаляються з даного розділу реєстру. Даний розділ відповідає за запуск програм для всіх користувачів системи. [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run] - програми, які запускаються при вході поточного користувача в систему
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce] - програми, які запускаються тільки один раз при вході поточного користувача в систему. Після цього ключі програм автоматично віддаляються з даного розділу реєстру.
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices]
програми, які завантажуються при старті системи до входу користувача в Windows. [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce]
програми звідси завантажуються тільки один раз, коли завантажується система. Наприклад, щоб автоматично запускати Блокнот при вході поточного користувача, відкриваємо Редактор реєстру (regedit.exe), переходимо в розділ [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run] і додаємо наступний ключ: "NOTEPAD.EXE"="C:\\WINDOWS\\System32\\notepad.exe"
Призначені завдання - програми можуть запускатися за допомогою "Майстра планування завдань". Подивитися список установлених завдань, а також додати нове можна так: "Пуск - Всі програми - Стандартні - Службові - Призначені завдання" - при цьому відкриється папка ..\WINDOWS\Tasks, у якій відображені призначені завдання. Щоб додати нове завдання, потрібно двічі клацнути лівою кнопкою миші по значку "Додати завдання". Запуск програм за допомогою цього майстра можливий однократно, при вході в Windows, при включенні комп'ютера, а також за розкладом.
Папка "Автозавантаження" - це папка, у якій зберігаються ярлики для програм, що запускають після входу користувача в систему. Ярлики в цю папку можуть додаватися програмами при їхній установці або користувачем самостійно. Існує дві папки - загальна для всіх користувачів й індивідуальна для поточного користувача. За замовчуванням ці папки перебувають тут: ..\Documents and Settings\All Users\Головне меню\Програми\Автозавантаження - це папка, програми з якої будуть запускатися для всіх користувачів комп'ютера.
..\Documents and Settings\Username\Головне меню\Програми\Автозавантаження - це папка, програми з якої будуть запускатися для поточного користувача (тут він названий Username).
Подивитися, які програми у вас запускаються таким способом можна, відкривши меню "Пуск - Всі програми - Автозавантаження". Якщо ви створите в цій папці ярлик для якої-небудь програми, вона буде запускатися автоматично після входу користувача в систему. Зміна папки автозавантаження - Windows зчитує дані про шляху до папки "Автозавантаження" з реєстру. Цей шлях прописаний у наступних розділах:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders] "Common Startup"="%ALLUSERSPROFILE%\Головне меню\Програми\Автозавантаження" - для всіх користувачів системи.
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders] "Startup"="%USERPROFILE%\Головне меню\Програми\Автозавантаження" - для поточного користувача. Перемінивши шлях до папки ми одержимо автозавантаження всіх програм із зазначеної папки. Наприклад: [HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders] "Startup"="c:\mystartup" - система завантажить всі програми, ярлики яких перебувають у папці c:\mystartup\, при цьому папка "Автозавантаження" всі так само буде відображатися в меню "Пуск", а якщо в користувача в ній нічого не було, то він й не помітить підміни.
Подивитися список програм, що завантажують автоматично, можна відкривши програму "Відомості про систему" (відкрийте "Пуск - Всі програми - Стандартні - Службові - Відомості про систему" або наберіть msinfo32.exe у командному рядку) і перейшовши в пункт "Програмне середовище - програми, що завантажують Автоматично,". Програма "Властивості системи" відображає групи автозавантаження з реєстру й папок "Автозавантаження".
Інша програма, що дозволяє подивитися список програм автозавантаження - "Настроювання системи" (для запуску наберіть msconfig.exe з командного рядка). Ця програма крім перегляду списку автозавантаження надає можливість відключення всіх пунктів автозавантаження (вкладка "Загальні") або вибіркових програм (вкладка "Автозавантаження").
7. Контроль роботи користувача з клавіатурою Розробити програму, що перехоплює події клавіатури. Всі події повинні протоколюватися в спеціальному журналі, що може бути звичайним текстовим файлом. Передбачити в програмі можливість запуску й останова програми перехоплення подій. Програма повинна активізуватися/дезактивизироваться (відображатися/ховатися) по натисканню спеціальної комбінації клавіш.
Пояснення і рекомендації
Варіант реалізації.
Інтерфейс програми (Рис 7.1). Програма працює із системного трея.
Рис 7.1 Зовнішній інтерфейс додатка
Навігація здійснюється за допомогою інформативного меню, зручного в роботі. Для того, щоб запустити програму на виконання досить вибрати пункт "Запустити службу". Щоб зупинити дію програми: пункт "Зупинити службу". У пункті "Параметри" надається можливість установити програму в "Автозавантаження" Windows. Після виконання програми, можна переглянути отримані результати за допомогою пункту меню "Журнал" ( Рис 7.2).
Рис 7.2 Журнал
Пункт меню "Вихід" - вихід з додатку.
8$. Одержання відомостей про систему.
Розробити програму, що відображає наступні відомості про конфігурацію комп'ютера.
Пояснення і рекомендації
Програма повинна відображати основні відомості про конфігурації комп'ютера по наступних позиціях:
1. Процесор
2. Пам'ять
3. Відео
4. Накопичувачі
5. Пристрою уведення
6. Мережна інформація
7. Операційна система
8. Системна інформація
Процесор - інформація про назву процесора, його типі, тактовій частоті, підтримуваних інструкціях.
Пам'ять - інформація про зайнятість пам'яті, повному й доступному обсязі фізичної й віртуальної пам'яті.
Відео - інформація про тип і драйвер відеокарти, типі монітора, дозволі екрана, колірній палітрі, частоті відновлення.
Накопичувачі - інформація про всі присутні накопичувачі (жорстких дисках, CDROM, і т.д.).
Пристрою уведення - інформація про пристрої уведення (клавіатура, миша).
Мережна інформація - інформація про мережне ім'я комп'ютера і його IP-адресі.
Операційна система - інформація про тип операційної системи встановленої на комп'ютері.
Системна інформація - інформація про ім'я комп'ютера, що тече користувачі, системному й Windows каталогах.
Зовнішній вигляд програми може бути представлений у такий спосіб:
Рис 8.1 Зовнішній вигляд програми.
У лівій частині головного вікна перебувають кнопки. Напису на кнопках указують на ту інформацію, що буде виведена при натисканні на кнопку. У нижньому лівому куті перебуває кнопка вихід. Натискання на неї приводить до завершення програми.
У правій частині головного вікна перебуває область, куди виводиться вся інформація. Текст із цієї області може бути скопійований у буфет обміну. При необхідності, у правій стороні цієї області автоматично з'являється смуга прокручування.
Інтерфейс програми написаний з використанням Win32 API. Розглянемо основні функції:
Функція DlgProc - функція ініціалізації вікна. Повідомлення WM_INITDIALOG приводить до ініціалізації діалогового вікна на якому розміщені всі необхідні елементи. Повідомлення WM_COMMAND посилає віконній процедурі всіма стандартними елементами (у нашому випадку - кнопками). В елементі wParam повідомлень, що посилають, зберігається інформація достатня для того, щоб програма змогла визначити, які дії їй необхідно виконати при натисканні тієї або іншої кнопки.
Функція GetSysInfo - призначена для одержання системної інформації, а саме: імені комп'ютера, імені поточного користувача, системного й Windows каталогу.
Всю цю інформацію повертають стандартні функції Win32:
GetComputerName - функція возвращающая ім'я комп'ютера.
GetUserName - функція возвращающая ім'я поточного користувача.
GetSystemDirectory - функція возвращающая ім'я системного каталогу.
GetWindowsDirectory - функція возвращающая ім'я каталогу в який установлена операційна система.
Вся інформація зберігається в рядку Info. Надалі стандартна функція SendMessage посилає елементу Edit (даний елемент безпосередньо виводить інформацію у вікні) повідомлення WM_SETTEXT і з ним передає рядок Info.
Функція GetCPUInfo - служить для одержання інформації про процесор, а саме: типі процесора, його тактової частоти, підтримуваних інструкціях. Інформацію про ім'я, тип процесора, а також його тактовій частоті програма бере з реєстру Windows. Для цього за допомогою Win32 функції (надалі всі стандартні Win32 функції будуть позначені знайомий ) RegOpenKeyEx*. Далі за допомогою функції RegQueryValueEx* проводиться вибірка значень по наступних ключах:
- ProcessorNameString - ім'я процесора
- Identifier - тип процесора
- ~Mhz - частота процесора
Для визначення підтримуваних інструкцій використається функція IsProcessorFeaturePresent*. Це логічна функція, що повертає значення true при підтримки процесором даної інструкції. Використаються наступні вхідні параметри:
- PF_MMX_INSTRUCTIONS_AVAILABLE - підтримка інструкцій MMX
- PF_XMMI_INSTRUCTIONS_AVAILABLE - підтримка інструкцій SSE
Як й у попередній функції, вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetMemoryStatus - призначена для одержання інформації про пам'яті, а саме: зайнятості пам'яті, повному й доступному обсязі фізичної й віртуальної пам'яті. Всю цю інформацію повертає функція GlobalMemoryStatus* параметром якої є покажчик на структуру MEMORYSTATUS. У результаті поля структури будуть містити наступну інформацію:
- dwMemoryLoad - відсоток зайнятості фізичної пам'яті
- dwTotalPhys - повний обсяг фізичної пам'яті (у байтах)
- dwAvailPhys - обсяг доступної фізичної пам'яті (у байтах)
- dwTotalVirtual - повний обсяг віртуальної пам'яті (у байтах)
- dwAvailVirtual - доступний обсяг віртуальної пам'яті (у байтах)
Як й у попередній функції, вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetInpDev - призначена для одержання інформації про пристрої уведення, а саме про клавіатуру й мишу.Інформація про клавіатуру програма одержує за допомогою функції GetKeyboardType*, результатом якої є значення, залежно від якого визначається тип клавіатури.Інформацію про мишу дає функція GetSystemMetrics () із вхідним параметром SM_CMOUSEBUTTONS. У результаті, функція повертає кількість кнопок миші, або значення 0, якщо миша не підключена.
&nbspBся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetWinVer - призначена для одержання інформації про операційну систему встановленої на комп'ютері.Всю необхідну інформацію для визначення операційної системи повертає функція GetVersionEx*, параметром якої є покажчик на структуру OSVERSIONINFOEX. У результаті поле dwMajorVersion у сполученні з полем dwMinorVersion структури дають можливість визначити тип операційної системи. Поле wServicePackMajor містить інформацію про встановлений Service Pack'ах, а поле dwBuildNumber - номер зборки ОС.
Вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetDisks - призначена для одержання інформації про всіх присутніх у системі накопичувачах (жорстких дисках, CDROM, і т.д.) їхніх типах і т.д.Для визначення імен дисків використається функція GetLogicalDrives* результатом якої є змінна типу DWORD. Далі використовуючи побітові зрушення й накладаючи бітову маску одержуємо інформацію про присутність того, або іншого імені в системі (допустимо якщо значення змінної дорівнює 000...00011101, те це означає що в системі присутні диски A, C, D, E).
Для визначення типу диска використається функція GetDriveType* вхідним параметром якої є ім'я диска, а залежно від значення, що повернула функція визначається тип диска. Функція GetDriveType* може повернути наступні значення:
- DRIVE_UNKNOWN - тип диска невідомий
- DRIVE_FIXED - жорсткий диск
- DRIVE_CDROM - CDROM
- DRIVE_REMOVABLE - знімний накопичувач
- DRIVE_REMOTE - знімний диск
- DRIVE_RAMDISK - RAM диск
Інформація про ім'я диска, типі файлової системи, мітці тому визначається за допомогою функції GetVolumeInformation*, а інформація про загальний обсяг диска, вільному й зайнятому просторі за допомогою GetDiskFreeSpaceEx*. Треба відзначити ту особливість, що функція GetDiskFreeSpaceEx* повертає значення обсягу диска, вільного й зайнятого простору в байтах, тому для зберігання таких більших чисел використається тип __int64 (64-битий).
Вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetNetInfo - призначена для одержання мережної інформації, а саме: мережного імені комп'ютера й IP-адреси. Мережне ім'я комп'ютера одержуємо при використанні функції GetHostName*, а IP-адреса при використанні GetHostByName. Вхідним параметром цієї функції служить мережне ім'я комп'ютера, а вихідним структура HOSTENT, що у списку h_addr_list містить всі IP-адреси комп'ютера.
Вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
Функція GetVideo - призначена для одержання інформація про тип і драйвер відеокарти, типі монітора, дозволі екрана, колірній палітрі, частоті відновлення. Для одержання інформації про тип відеокарти й монітора використалася функція EnumDisplayDevices*. У результаті виконання функції одержуємо структуру DISPLAY_DEVICE яка, залежно від вхідних параметрів, у поле DeviceString містить тип відеокарти, або монітора.Інформацію про драйвер відеокарти, дозволі екрана, колірній гамі й частоті відновлення дає функція EnumDisplaySettings, у результаті виконання якої одержуємо структуру DEVMODE. Поля даної структури будуть містити наступну інформацію:
dmDeviceName - інформація про драйвер відеокарти
dmPelsWidth - ширина екрана (у пикселях)
dmPelsHeight - висота екрана (у пикселях)
dmBitsPerPel - кількість біт кольори на один пиксель
dmDisplayFrequency - частота відновлення (у герцах)
Вся отримана інформація зберігається в рядку Info, що потім передається елементу Edit за допомогою функції SendMessage*.
9$. Вивантаження системи.
Розробити програму, що дозволяє виконувати одне з наступних дій: завершення поточного сеансу, перезавантаження комп'ютера, вимикання комп'ютера, перехід у сплячий режим.
Пояснення і рекомендації
Варіант реалізації.
При запуску програми відкривається вікно із трьома кнопками, як представлено на малюнку (Рис 9.1).
Рис 9.1 Зовнішній вигляд додатка
При натисканні на кнопку Logoff виробляється завершення всіх поточних процесів і завершення сеансу поточного користувача.
Кнопка Shutdown служить для завершення роботи Windows і вимикання харчування комп'ютера.
При натисканні на кнопку Reboot відбувається перезавантаження системи.
Натискання кнопки Cancel закриває вікно й завершує роботу програми. Аналогічні дії виробляються при натисканні на клавішу ESC.
Розглянути можливість додавання кнопок на форму з іншими функціями (типу - сон і т.д.).
10$. Мережне оточення.
Розробити програму, що сканує доступні ресурси зазначеного комп'ютера в мережі. Програма має перевірити чи є в мережі комп'ютер. Програма повинна дозволяти підключати мережні диски.
Пояснення і рекомендації.
Перелічити мережні підключення.
З командного рядка MS-DOS, для перегляду мережних ресурсів (дисків), до яких підключений комп'ютер, використається наступна команда:
net use
Програмно, для того, щоб почати перерахування підключених мережних ресурсів необхідно викликати функцію - WNetOpenEnum() і WNetEnumResources() для продовження перерахування.
Приклад перерахування мережних з'єднань.
ЗАУВАЖЕННЯ: Необхідно включити в проект бібліотеку mpr.lib, у якій зберігаються функції WNet*. Приклад коду
#include #include void main()
{
DWORD dwResult;
HANDLE hEnum;
DWORD cbBuffer = 16384;
DWORD cEntries = 0xFFFFFFFF;
LPNETRESOURCE lpnrDrv;
DWORD i;
dwResult = WNetOpenEnum( RESOURCE_CONNECTED,
RESOURCETYPE_ANY,
0,
NULL,
&hEnum );
if (dwResult != NO_ERROR)
{
printf( "\nCannot enumerate network drives.\n" );
return;
}
printf( "\nNetwork drives:\n\n" );
do
{
lpnrDrv = (LPNETRESOURCE) GlobalAlloc( GPTR, cbBuffer );
dwResult = WNetEnumResource( hEnum, &cEntries, lpnrDrv, &cbBuffer
);
if (dwResult == NO_ERROR)
{
for( i = 0; i < cEntries; i++ )
{
if( lpnrDrv[i].lpLocalName != NULL )
{
printf( "%s\t%s\n", lpnrDrv[i].lpLocalName,
lpnrDrv[i].lpRemoteName );
}
}
}
else if( dwResult != ERROR_NO_MORE_ITEMS )
{
printf( "Cannot complete network drive enumeration" );
GlobalFree( (HGLOBAL) lpnrDrv );
break;
}
GlobalFree( (HGLOBAL) lpnrDrv );
}
while( dwResult != ERROR_NO_MORE_ITEMS );
WNetCloseEnum(hEnum);
}
Як програмно підключити мережний диск.
Для підключення мережного диска можна скористатися прикладом:
NETRESOURCE netResource;
ZeroMemory(&netResource, sizeof(NETRESOURCE));
netResource.dwType = RESOURCETYPE_DISK;
netResource.lpLocalName = "Q:";
netResource.lpRemoteName = "\\\\SPIKE\\homedir";
if(WNetAddConnection2(&netResource, "firebird", "djf", NULL) != NO_ERROR)
{
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(Application->Handle, (const char *)lpMsgBuf, "GetLastError",
MB_OK | MB_ICONINFORMATION);
LocalFree(lpMsgBuf);
}
11$. Синхронізація процесів і потоків.
Написати програму, що моделює обробний пристрій і чергу до нього. Потік, що імітує пристрій, одержує якесь завдання із загальної змінної (значення, що зберігається в змінній, може бути інтерпретоване як час виконання завдання). Після обробки завдання потік-пристрій повідомляє потоків-черги, що він вільний, при цьому потік-черга поміщає в змінну нове завдання. Для синхронізації потоків використати об'єкт синхронізації за допомогою мютексів.
Пояснення і рекомендації
Додаток складається з одного або декількох процесів. Процес (process), у найпростіших термінах, є програма, що виконується. У контексті процесу працює один або кілька потоків. Потоки (thread) - основні модулі програми, серед яких операційна система розподіляє процесорний час. Потік може виконувати будь-яку частину коду процесу, включаючи частини, у цей час виконувані іншим потоком. Нитка (fiber) - секція коду, що виконує, для якої повинна бути вручну встановлена черговість обслуговування прикладною програмою. Нитки працюють у середовищі потоків, які встановлюють черговість їхнього обслуговування. Об'єкт завдання (job object) дозволяє управляти групою процесів як модулем програми. Об'єкти завдання - це іменовані, захищені, спільно використовувані об'єкти, які управляють атрибутами процесів, пов'язаних з ними. Операції, виконані на об'єкті завдання, зачіпають всі процеси, пов'язані з об'єктом завдання.
Мета й засоби синхронізації.
Існує досить великий клас засобів операційної системи, за допомогою яких забезпечується взаємна синхронізація процесів і потоків. Потреба в синхронізації потоків виникає тільки в мультипрограмній операційній системі й пов'язана зі спільним використанням апаратних й інформаційних ресурсів обчислювальної системи. Синхронізація необхідна для виключення гонок і тупиків при обміні даними між потоками, поділі даних, при доступі до процесора й пристроїв вводу-висновку. У багатьох операційних системах ці засоби називаються засобами міжпроцесної взаємодії - Inter Process Communications (IPC), що відбиває історичну первинність поняття "процес" стосовно поняття "потік". Звичайно до засобів IPC відносять не тільки засобу міжпроцесна синхронізації, але й засобу міжпроцесного обміну даними. Важливим поняттям синхронізації потоків є поняття "критичної секції" програми. Критична секція - це частина програми, результат виконання якої може непередбачено мінятися, якщо змінні, стосовні до цієї частини програми, змінюються іншими потоками в той час, коли виконання цієї частини ще не завершено. Критична секція завжди визначається стосовно певних критичних даних, при неузгодженій зміні яких можуть виникнути небажані ефекти
Щоб виключити ефект гонок стосовно критичних даних, необхідно забезпечити, щоб у кожен момент часу в критичній секції, пов'язаної із цими даними, перебував тільки один потік. При цьому неважливо, перебуває цей потік в активному або в припиненому стані. Цей прийом називають взаємним виключенням. Операційна система використає різні способи реалізації взаємного виключення. Деякі способи придатні для взаємного виключення при входженні в критичну секцію тільки потоків одного процесу, у той час як інші можуть забезпечити взаємне виключення й для потоків різних процесів.
Найпростіший й у той же час самий неефективний спосіб забезпечення взаємного виключення полягає в тому, що операційна система дозволяє потоку забороняти будь-які переривання на час його знаходження в критичній секції.
Для синхронізації потоків одного процесу можуть використатися глобальні блокуючі змінні. Із цими змінними, до яких всі потоки процесу мають прямий доступ, програміст працює, не звертаючись до системних викликів ОС.
Реалізація взаємного виключення за допомогою глобальних блокуючих змінних має істотний недолік: протягом часу, коли один потік перебуває в критичній секції, інший потік, якому потрібно той же ресурс, одержавши доступ до процесора, буде безупинно опитувати блокуючу змінну, даремно витрачаючи виділюване йому процесорний час, що могло б бути використане для виконання якого-небудь іншого потоку. Для усунення цього недоліку передбачаються спеціальні системні виклики для роботи із критичними секціями.
Узагальненням блокируючих змінних є так називані семафори Дийкстра. Замість двійкових змінних Дийкстр (Dijkstra) запропонував використати змінні, які можуть приймати цілі ненегативні значення. Такі змінні, використовувані для синхронізації обчислювальних процесів, одержали назву семафорів.
Для синхронізації потоків різних процесів використаються системні об'єкти синхронізації.
Прикладами таких синхронізуючих об'єктів є системні семафори, мютекси, події, таймери й інших - їхній набір залежить від конкретної ОС, що створює ці об'єкти по запитах процесів. Щоб процеси могли розділяти синхронізуючі об'єкти, у різних ОС використаються різні методи. Деякі ОС повертають покажчик на об'єкт. Цей покажчик може бути доступний всім родинним процесам, що успадковують характеристики загального батьківського процесу. В інших ОС процеси в запитах на створення об'єктів синхронізації вказують імена, які повинні бути їм привласнені. Далі ці імена використаються різними процесами для маніпуляцій об'єктами синхронізації. У такому, випадку робота із синхронізуючими об'єктами подібна до роботи з файлами. Їх можна створювати, відкривати, закривати, знищувати.
Крім того, для синхронізації можуть бути використані такі "звичайні" об'єкти ОС, як файли, процеси й потоки. Всі ці об'єкти можуть перебувати у двох станах: сигнальному й несигнальному - вільному. Для кожного об'єкта зміст, вкладений у поняття "сигнальний стан", залежить від типу об'єкта. Так, наприклад, потік переходить у сигнальний стан тоді, коли він завершується. Процес переходить у сигнальний стан тоді, коли завершуються всі його потоки. Файл переходить у сигнальний стан у тому випадку, коли завершується операція вводу-висновку для цього файлу. Для інших об'єктів сигнальний стан установлюється в результаті виконання спеціальних системних викликів. Припинення й активізація потоків здійснюються залежно від стану синхронізуючих об'єктів ОС.
12$. Поштові скриньки (MailSlots).
Розробити програму-сервер і програму клієнт для обміну повідомленнями між двома комп'ютерами за допомогою поштових слотів. Клієнт посилає серверу запит на виконання однієї з команд: DIR, MKDIR, CD. Сервер приймає команду, виконує її, а результат відправляє клієнтові.
Пояснення і рекомендації
Поштові слоти (mailslots) - це механізм однобічного пересилання даних між процесами по мережі. Поштові слоти по своїм властивостям відрізняються від каналів.
Через поштові слоти можна посилати тільки не занадто важливі дані, втрата або запізнювання яких не приносить особливої шкоди.
Наприклад, їх можна використати для циркулярного розсилання кодів відновлення стану якого-небудь процесу через кожні п'ять хвилин. Якщо ці дані один раз випадково не потраплять до одному з користувачів, нічого страшного не трапиться, а от у випадку посилки таких даних, як облікові транзакції, це зовсім неприйнятно.
Любий процес, що створює поштовий слот для прийому даних, називається сервером слоту. Навіть якщо саме по собі додаток є клієнтом й одержує дані від додатка-сервера, стосовно створеному поштового слоту воно являє собою сервер. Для даного слота будь-який комп'ютер мережі може бути сервером, але читати зі слота дані (або звертатися до нього іншими способами, тобто через спадкування) може тільки процес, що його створив.
Усякий процес, якому відоме ім'я слота, може посилати йому дані. Процеси, які посилають дані на поштовий слот, називаються клієнтами слоту.
Для створення слота в додатках використається функція CreateMailSlot. Її перший параметр - це покажчик на ASCII-рядок, що містить ім'я створюваного поштового слота. Слоти створюються тільки на локальному комп'ютері, тому повне ім'я слота виглядає в такий спосіб:
\\ .\mailslot\[Шлях]Ім'я.
В іменах поштових слотів підтримується псевдокаталогова структура. Для кращої організації поштових слотів у їхніх іменах можна вказувати шляхи в каталогах.
Функція CreateMailslot також приймає як параметри максимальний розмір повідомлень, час затримки для операцій читання й необов'язкову захисну структуру. Замість значення затримки можна вказати константу для режиму для блокування операцій читання на слоті до одержання повідомлення без обмеження часу.
Якщо функція CreateMailslot завершується успішно, вона повертає ключ для нового слота.
Для зміни часу затримки операцій читання після створення слота використається функція SetMailslot. Крім того, для одержання такої інформації про слот, як максимальний розмір повідомлення, розмір наступного повідомлення в черги й кількість повідомлень у черзі, можна використати функцію GetMailslotInfo.
Для читання зі слоту використається функція ReadFile, що завершує свою роботу після одержання повідомлення, незалежно від того, скільки байтів необхідно прочитати.
Крім того, за допомогою функції GetMailslotInfo можна довідатися кількість повідомлень, що очікують у черзі слота, і розмір наступного повідомлення.
Для запису повідомлення до слоту спочатку необхідно відкрити його ключ за допомогою функції CreateFile, а потім скористатися функцією WriteFile для посилки повідомлення. Ключ слота закривається функцією CloseHandle. При виклику CreateFile необхідно вказати ім'я слота, якому посилає повідомлення. Зазначене ім'я допускає циркулярне розсилання даних всім слотам з такими ж іменами на комп'ютерах домена.
Для локального комп'ютера ім'я слота виглядає в такий спосіб:
\\ .\mailslot\[Шлях]Ім'я
Для вилученого комп'ютера ім'я слота має такий вигляд:
\\Имяудаленногокомпьютера\mailslot\[Шлях]Ім'я
Для розсилання повідомлення всім слотам з однаковими іменами на комп'ютерах домену необхідно вказати наступне ім'я:
\\Имядомена\mailslot\[Шлях]Ім'я
Для розсилання повідомлення всім комп'ютерам у домені більше високого ієрархічного рівня використається наступне ім'я:
\\*\mailslot\[Шлях]Ім'я
Після успішного завершення функції CreateFile ключ, що вона повертає, можна використати у функції WriteFile для посилки повідомлень на слот.
По закінченні роботи з поштовим слотом його необхідно закрити викликом функції CloseHandle для звільнення асоційованих з ним системних ресурсів.
По завершенні процесу всі ключі слотів, асоційованих із процесом, закриваються автоматично. Після їхнього закриття поштовий слот знищується, а всі дані в його буфері губляться.
13$. Віконні інтерфейси.
Розробити програму, що створює кілька робочих столів і надає можливість перемикатися між ними.
Пояснення і рекомендації
Створення, видалення, перемикання.
Існує безліч програм, що створюють віртуальні робітники столи й працюючі як в Windows NT, так й в Win9x. Такі програми прибігають до різних хитрощів, наприклад, приховують вікна програм, що перебувають на "іншому робочому столі". Але в Windows NT існує стандартний спосіб створення робочих столів, що ми й розглянемо.
Створимо робочий стіл "NewDesktop".
THandle hDesktop: ; (або HDESK)
{...}
hDesktop=CreateDesktop('NewDesktop',null,null,DF_ALLOWOTHERACCOUNTHOOK,
DESKTOP_CREATEMENU + DESKTOP_CREATEWINDOW + DESKTOP_ENUMERATE + DESKTOP_HOOKCONTROL + DESKTOP_READOBJECTS + DESKTOP_SWITCHDESKTOP + DESKTOP_WRITEOBJECTS,null);
if hDesktop<>0 then Робітник стіл створений else помилка;
Далі на створеному столі треба запустити оболонку (shell). За замовчуванням оболонкою в нас буде explorer.exe. Його ми й запустимо.
var
_PROCESS_INFORMATION ;
_STARTUPINFOA si;
{...}
Запуск програми за допомогою CreateProcess.
FillMemory( @si, sizeof( si ), 0 );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWMAXIMIZED;
Але запускаємо програму не на звичайному робочому столі, а на знову созданом
Si.lpDesktop = PChar('Winsta0\NewDesktop');
CreateProcess(null,'explorer.exe',null,null,false,NORMAL_PRIORITY_CLASS,null,null,Si,pi);
Взагалі ж замість explorer.exe краще запускати userinit.exe. Ця програма запускає оболонку.
Тепер у нас на новому робочому столі запущений shell і можна перемикатися на новий робочий стіл (на нього можна перемикатися й без запуску програми, але дивитися там нема чого :)) Перемикання виробляється функцією SwitchDesktop.
if SwitchDesktop(hDesktop) then перемкнулися на новий робочий стіл else помилка;
але перемкнутися назад ми не зможемо, якщо не запустимо на цьому столі іншу програму, яка б перемкнула нас на "рідний" десктоп. Але про це пізніше, а поки зробимо так:
Sleep(10000); HDesk hOldDesktop;
І через 10 секунд перемкнемося назад. Для цього нам потрібний дескриптор "Winsta0\Default". Але де його взяти? А одержати його можна 2 способами: 1) Т.к. наш потім був запущений на "рідному"("Winsta0\Default") столі, то можна викликати функцію GetThreadDesktop:
hOldDesktop = GetThreadDesktop(GetCurrentThreadId); 2) Нам відома назва первісного робочого стола - "Default". Виконуємо OpenDesktop:
hOldDesktop = OpenDesktop('Default', DF_ALLOWOTHERACCOUNTHOOK, false,
DESKTOP_CREATEMENU, DESKTOP_CREATEWINDOW, DESKTOP_ENUMERATE, DESKTOP_HOOKCONTROL,
DESKTOP_JOURNALPLAYBACK, DESKTOP_JOURNALRECORD, DESKTOP_READOBJECTS,
DESKTOP_SWITCHDESKTOP, DESKTOP_WRITEOBJECTS);
if hOldDesktop<>0 then Все отлично else Помилка;
Перед перемиканням виведемо наприклад повідомлення про це. Просто MessageBox виведе повідомлення на старий робочий стіл і ми його не побачимо:(. Тому зробимо так: Перемкнемо висновок на новий робочий стіл:
SetThreadDesktop(hDesktop);
Для вдалого виконання цієї функції в нашого потоку не повинне бути відкрито ніяких вікон. Якщо вікна є, то або закриваємо їх або створюємо новий потік. Отже, функція виконалася вдало. Тепер посилаємо сообщние
MessageBox(0,'Перемикання робочого стола','Hello',mb_ok);
Віконце повинне вивестися на видимому робочому столі! Т.ч. вікно не модальне, то дамо користувачеві ще 10 секунд і після цього перемкнемо його на старий робочий стіл.
Sleep(10000);
За цей час бажано, щоб користувач нажав на OK у повідомленні - залишати вікна на робочому столі, що вбиває, - погана прикмета :)
SetThreadDesktop(hOldDesktop);
SwitchDesktop(hOldDesktop);
Тепер завершуємо explorer на NewDesktop і закриваємо цей робітник стіл.
TerminateProcess(pi.hProcess,1);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess); if CloseDesktop(hDesktop) then закрили робочий стіл else помилка;
Ну й напевно варто видалити дескриптор hOldDesktop
CloseHandle(hOldDesktop);
14$. Інтерфейс інтерпретатора команд.
Розробити програму, що встановлює свій тип документів у системі й запускається для перегляду й редагування таких документів.
Пояснення і рекомендації
Варіант реалізації.
Зовнішній вигляд програми може бути наступної (Рис 14.1).
Рис 14.1 Інтерфейс редактори Simplenote
Після запуску редактора можна вводити текст або вставляти заздалегідь скопійований (Рис 14.2).
Рис 14.2 Ілюстрація процесу редагування
Після чого, можна зберегти документ, вивести на печатку, скопіювати й т.д. Всі ці операції доступні при виборі відповідних команд основного меню або при виборі відповідної піктограми на інструментальній панелі (Рис 14.3).
Рис 14.3 меню, Що Випадає
Збережений файл буде мати розширення *.note і при його відкритті редактор буде запускатися автоматично. Simplenote дозволяє працювати з декількома документами відразу, їхнє розміщення в головному вікні може бути різним. Спосіб розміщення робочих вікон в Simplenote вибирається в пункті Window основного меню.
Рис 14.4 Cascade
Рис 14.5 Tile
15$. Хоронитель екрана.
Розробити хоронитель екрана, що працює у фоновому режимі й запускається під час тривалого простою комп'ютера. Програма повинна відображати деякий тривимірний об'єкт (можна використати засоби OpenGL) хаотично переміщається в межах видимого вікна. Програма повинна дозволяти встановлювати лінійні розміри об'єкта й швидкість його переміщення.
Пояснення і рекомендації
Наш хоронитель екрана буде працювати у фоновому режимі, при цьому він не повинен заважати роботі інших додатків, і споживати мінімум ресурсу. Технічно хоронитель екрана є звичайним виконуваним файлом (*.exe), перейменованим в *.scr, повністю керований повідомленнями Windows.
Для зменшення обсягу файлу, що виконує, в описаній програмі не використається бібліотека високого рівня MFC або CLX(VCL), вся робота виконується тільки засобами Win32 API. Також не використаються об'єктно-орієнтоване розширення мови. У результаті вдається зменшити розмір програми до 36.8K.
Для створення хоронителів екрана в комплект Visual C++ входить заголовний файл srcnsave.h (D:Program FilesMicrosoft Visual StudioVC98Includescrnsave.h), у якому перебувають визначення констант і функцій на всі випадки життя, необхідних для функціонування screensaver'а в середовищі Windows 9x/NT, і статична бібліотека scrnsave.lib. Точка входу в програму (функція WinMain) перебуває в самої scrnsave.lib, що дуже сильно полегшує нам життя. Наш хоронитель пишеться орієнтовно для Windows NT, хоча повинен працювати на всіх платформах. Розходження полягає в тому, що для Windows 9x доводиться писати ще одну функцію, відповідальну за зміну пароля. В NT і вище цю роль виконує системний процес Winlogon. Якщо ключ HKEY_CURRENT_USERControl PanelDesktopScreenSaverIsSecure у системному реєстрі Windows не дорівнює нулю, то Winlogon буде запитувати пароль перед виходом зі скринсейвера. Хоча без цієї функції можна й обійтися.
Отже, приступимося до написання самого коду. Завантажуємо середовище Visual C++. Створюємо проект "Win32 Application" (File->New->Projects->Win32 Application). В Project Name уводимо ssaver, в Location вибираємо папку де буде зберігається наш проект). Тиснемо Ok. З'явиться віконце "Win32 Application - Step 1 of 1. Залишаємо все без змін, тиснемо Fisnish. Ок. Маємо порожній проект. Додаємо новий файл вихідного коду в проект (меню File->New->Files->C++ source files). В File name пишемо ssaver, тиснемо Ок. Маємо файл ssaver.cpp. Перед Вами відкриється порожнє вікно, у якому, властиво й буде писатися програма. Набудовуємо середовище. У меню Build->Set active configuration вибираємо "ssaver - Win32 Release", ок. Підключаємо бібліотеку scrnsave.lib до проекту: меню Project->Settings, вкладка Link. Тут у рядку "Object library/modules" перераховані бібліотеки, що підключають за замовчуванням до Вашого проекту, нам треба дописати scrnsave.lib:
Для роботи хоронителя необхідно написати всього 3 функції (фактично тільки одну):
LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - є "функцією вікна" хоронителя. Вона одержує всі повідомлення системи (аналог функції WinMain у чистому Windows-додатку). Перший параметр hWnd - идентефикатор вікна нашого хоронителя. message - код повідомлення, що одержав хоронитель від системи, wParam й lParam - параметри повідомлення. У даній функції програміст повинен перехопити всі його повідомлення, що цікавлять, а неперехоплені передати на обробку функції LRESULT WINAPI DefScreenSaverProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - функція викликається системою щораз коли користувач натискає кнопку "настроювання..." у вікні настроювання хоронителів екрана (Пуск->Настроювання->Панель керування->Екран->Заставка).
BOOL WINAPI RegisterDialogClasses (HANDLE hInst); - викликається системою для реєстрації в ній додаткових класів (ми її не будемо використати).
Отже, у новому раніше створеному вікні пишемо наступний код:
#include
#include
LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0;
}
BOOL WINAPI ScreenSaverConfigureDialog (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
return true;
}
BOOL WINAPI RegisterDialogClasses (HANDLE hInst)
{
return true;
}
У перших двох рядках підключаються заголовні файли із прототипами функцій (windows.h - оголошення Win32 API, scrnsave.h - функції для роботи з хоронителем екрана). Далі оголошуються 3 основні функції, які й забезпечують роботу хоронителя екрана. Зараз у нас ScreenSaverProc нічого не робить (будемо поступово неї нарощувати). Тому що ми не використаємо ніяких спеціальних настроювань, те друга функція теж порожня. Нам не потрібно створювати додаткових системних класів, тому третя функція повинна повернути true. Тиснемо F7, середовище Visual C++ скомпілює програму і якщо не було помилок, ми одержимо повноцінний хоронитель екрана, правда він у нас поки нічого не робить. Зайдіть у папку з Вашим проектом, а потім у папку Release. Перейменуйте ssaver.exe в ssaver.scr. Тепер помістите ssaver.scr у системну папку Windows (В NT/2000 це C:WINNTSystem32, в 9x З:WINDOWSSYSTEM). Зайдіть на панель керування, запустите апплет "екран", далі вкладка "заставка". У списку з'явиться наш хоронитель під ім'ям aver (якщо файл починається з "ss", те ці дві букви не показуються):
При натисканні кнопки "Перегляд" нічого не відбувається, адже функція ScreenSaverProc порожня. Наш хоронитель запускається й відразу завершує свою роботу. Давайте наповнимо її яким-небудь корисним кодом.
Наприклад, у функцію ScreenSaverProc можна вписати код, що малює два вкладених квадрати, що обертаються в протилежні сторони.
16$.Синхронний I/O і файли.
Розробити програму, що дозволяє переглядати й змінювати вміст текстових файлів.
Пояснення і рекомендації
Розглянемо приклади роботи з файлами
#include #include void main( void ) {
FILE *file;
char* file_name = "file.txt";
char load_string[50] = "none";
file = fopen( file_name, "w" ); fputs( "string", file );
fclose( file );
file = fopen( file_name, "r" );
if( file != 0 )
{
fgets( load_string, 50 , file ); cout << "load_string = " << load_string << endl;
}
else
{
cout << "File not found !!!" << endl;
}
fclose(file);
}
Опис функцій роботи з файлами перебувають у бібліотеці stdio.h
Спочатку треба створити покажчик на змінну типу FILE ( FILE* file; ). Відкриття файлу виробляється викликом функції fopen ( file = fopen( file_name, "w" ); ) Перший параметр цієї функції - ім'я файлу, другий - указує в якому режимі повинен бути відкритий файл. "w" - відкрити для запису, "r" - відкрити для читання, "a" - доповнення файлу( це найбільш використовувані режими, хоча є й інші ). Запис і зчитування даних з файлу здійснюється наступними функціями : fputc, fputs, fgetc, fgets, fprintf, fscanf( опис цих функцій дивитеся в stdio.h). Закриття файлу здійснюється викликом функції fclose ( fclose( file ); ).
Робота з файлами за допомогою MFC( класи CFile, CStdioFile, ... ).
У бібліотеку MFC включено кілька класів для забезпечення роботи з файлами. Розглянуті нижче класи успадковуються від базового класу CFile. Клас CFile
Клас CFile призначений для забезпечення роботи з файлами. Він дозволяє спростити використання файлів, представляючи файл як об'єкт, якому можна створити, читати, записувати й т.д.
Щоб одержати доступ до файлу, спочатку треба створити об'єкт класу CFile. Конструктор класу дозволяє відразу після створення такого об'єкта відкрити файл. Але можна відкрити файл і пізніше, скориставшись методом Open.
Відкриття й створення файлів
Після створення об'єкта класу CFile можна відкрити файл, викликавши метод Open. Методу треба вказати шлях до відкриває файлу, що, і режим його використання. Прототип методу Open має такий вигляд:
virtual BOOL Open(LPCTSTR lpszFileName,
UINT nOpenFlags, CFileException* pError=NULL); Як параметр lpszFileName треба вказати ім'я файлу, що відкриває. Можна вказати тільки ім'я файлу або повне ім'я файлу, що включає повний шлях до нього.
Другий параметр nOpenFlags визначає дію, виконувана методом Open з файлом, а також атрибути файлу. Нижче представлені деякі возможеые значення параметра nOpenFlags:
CFile::modeCreate - Створюється новий файл. Якщо зазначений файл існує, то його вміст стирається й довжина файлу встановлюється рівної нулю.
CFile::modeNoTruncate - Цей файл призначений для використання разом з файлом
CFile::modeCreate. Якщо створюється вже існуючий файл, то його вміст не буде вилучено.
CFile::modeRead - Файл відкривається тільки для читання.
CFile::modeReadWrite - Файл відкривається для запису й для читання.
CFile::modeWrite - Файл відкривається тільки для запису.
CFile::typeText - Використається класами, породженими від класу CFile, наприклад CStdioFile, для роботи з файлами в текстовому режимі. Текстовий режим забезпечує перетворення комбінації символу повернення каретки й символу перекладу рядка.
CFile::Binary - Використається класами, породженими від класу CFile, наприклад CStdioFile, для роботи з файлами у двійковому режимі.
Необов'язковий параметр pError, що є покажчиком на об'єкт класу CFileException, використається тільки в тому випадку, якщо виконання операції з файлом викличе помилку. При цьому в об'єкт, що вказує pError, буде записана додаткова інформація.
Метод Open повертає не нульове значення, якщо файл відкритий і нуль у випадку помилки. Помилка при відкритті файлу може трапитися, наприклад, якщо методу Open зазначений для читання неіснуючий файл.
Приклади запису й читання з файлу
Приведемо фрагменти коду, у яких демонструється використання стандартних діалогових панелей вибору файлу й процедури читання й запису у файл.
Відкриття файлу й читання з нього
CString m_Text;// створення стандартної панелі вибору файлу Open
CFileDialog DlgOpen(TRUE,(LPCSTR)"txt",NULL,
OFN_HIDEREADONLY,(LPCSTR)" Text Files (*.txt) |*.txt||");
// відображення стандартної панелі вибору файлу Open
if(DlgOpen.DoModal()==IDOK) {// створення об'єкта й відкриття файлу для читання
CStdioFile File(DlgOpen.GetPathName(),CFile::
modeRead|CFile::typeBinary);
// читання з файлу рядка
CString& ref=m_Text;
File.ReadString(ref); // передається посилання на рядок m_Text
} Запустите програму - Build / Rebuild all ( будуть помилки ), виберіть Build / Set active configuration - Win 32 Realise, виберіть пункт меню "Project", далі "Settings...", закладку "C/C++", Category - Code Generation й у пункті "Use run-time library" виберіть "Multithreaded". Після цього зробіть знову Build / Rebuild all і програма буде працювати.
Відкриття файлу й запис із нього
CString m_Text;// створення стандартної панелі вибору файлу SaveAs
CFileDialog DlgSaveAs(FALSE,(LPCSTR)"txt",NULL,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
(LPCSTR)" Text Files (*.txt) |*.txt||");
// відображення стандартної панелі вибору файлу SaveAs
if(DlgSaveAs.DoModal()==IDOK)
{ // створення об'єкта й відкриття файлу для запису
CStdioFile File(DlgSaveAs.GetPathName(),
CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
// запис у файл рядка
File.WriteString((LPCTSTR)m_Text);
} 17$. Асинхронне ведення файлу.
Розробити програму, що дозволяє переглядати й змінювати вміст текстових файлів.
Пояснення і рекомендації
У цьому розділі будуть рассотрены приклади роботи з файлами
#include #include void main( void ) {
FILE *file;
char* file_name = "file.txt";
char load_string[50] = "none";
file = fopen( file_name, "w" ); fputs( "string", file );
fclose( file );
file = fopen( file_name, "r" );
if( file != 0 )
{
fgets( load_string, 50 , file ); cout << "load_string = " << load_string << endl;
}
else
{
cout << "File not found !!!" << endl;
}
fclose(file);
}
Опис функцій роботи з файломи перебувають у бібліотеці stdio.h
Спочатку треба створити покажчик на змінну типу FILE ( FILE* file; ). Відкриття файлу виробляється викликом функції fopen ( file = fopen( file_name, "w" ); ) Перший параметр цієї функції - ім'я файлу, другий - указує в якому режимі повинен бути відкритий файл. "w" - відкрити для запису, "r" - відкрити для читання, "a" - доповнення файлу( це найбільш використовувані режими, хоча є й інші ). Запис і зчитування даних з файлу здійснюється наступними функціями : fputc, fputs, fgetc, fgets, fprintf, fscanf( опис цих функцій дивитеся в stdio.h). Закриття файлу здійснюється викликом функції fclose ( fclose( file ); ).
Робота з файлами за допомогою MFC( класи CFile, CStdioFile, ... ).
У бібліотеку MFC включено кілька класів для забезпечення роботи з файлами. Розглянуті нижче класи успадковуються від базового класу CFile. Клас CFile
Клас CFile призначений для забезпечення роботи з файлами. Він дозволяє спростити використання файлів, представляючи файл як об'єкт, якому можна створити, читати, записувати й т.д.
Щоб одержати доступ до файлу, спочатку треба створити об'єкт класу CFile. Конструктор класу дозволяє відразу після створення такого об'єкта відкрити файл. Але можна відкрити файл і пізніше, скориставшись методом Open.
Відкриття й створення файлів
Після створення об'єкта класу CFile можна відкрити файл, викликавши метод Open. Методу треба вказати шлях до відкриває файлу, що, і режим його використання. Прототип методу Open має такий вигляд:
virtual BOOL Open(LPCTSTR lpszFileName,
UINT nOpenFlags, CFileException* pError=NULL); Як параметр lpszFileName треба вказати ім'я файлу, що відкриває. Можна вказати тільки ім'я файлу або повне ім'я файлу, що включає повний шлях до нього.
Другий параметр nOpenFlags визначає дію, виконувана методом Open з файлом, а також атрибути файлу. Нижче представлені деякі возможеые значення параметра nOpenFlags:
CFile::modeCreate - Створюється новий файл. Якщо зазначений файл існує, то його вміст стирається й довжина файлу встановлюється рівної нулю.
CFile::modeNoTruncate - Цей файл призначений для використання разом з файлом
CFile::modeCreate. Якщо створюється вже існуючий файл, то його вміст не буде вилучено.
CFile::modeRead - Файл відкривається тільки для читання.
CFile::modeReadWrite - Файл відкривається для запису й для читання.
CFile::modeWrite - Файл відкривається тільки для запису.
CFile::typeText - Використається класами, породженими від класу CFile, наприклад CStdioFile, для роботи з файлами в текстовому режимі. Текстовий режим забезпечує перетворення комбінації символу повернення каретки й символу перекладу рядка.
CFile::Binary - Використається класами, породженими від класу CFile, наприклад CStdioFile, для роботи з файлами у двійковому режимі.
Необов'язковий параметр pError, що є покажчиком на об'єкт класу CFileException, використається тільки в тому випадку, якщо виконання операції з файлом викличе помилку. При цьому в об'єкт, що вказує pError, буде записана додаткова інформація.
Метод Open повертає не нульове значення, якщо файл відкритий і нуль у випадку помилки. Помилка при відкритті файлу може трапитися, наприклад, якщо методу Open зазначений для читання неіснуючий файл.
Приклади запису й читання з файлу
Приведемо фрагменти коду, у яких демонструється використання стандартних діалогових панелей вибору файлу й процедури читання й запису у файл.
Відкриття файлу й читання з нього
CString m_Text;// створення стандартної панелі вибору файлу Open
CFileDialog DlgOpen(TRUE,(LPCSTR)"txt",NULL,
OFN_HIDEREADONLY,(LPCSTR)" Text Files (*.txt) |*.txt||");
// відображення стандартної панелі вибору файлу Open
if(DlgOpen.DoModal()==IDOK) {// створення об'єкта й відкриття файлу для читання
CStdioFile File(DlgOpen.GetPathName(),CFile::
modeRead|CFile::typeBinary);
// читання з файлу рядка
CString& ref=m_Text;
File.ReadString(ref); // передається посилання на рядок m_Text
} Запустите програму - Build / Rebuild all ( будуть помилки ), виберіть Build / Set active configuration - Win 32 Realise, виберіть пункт меню "Project", далі "Settings...", закладку "C/C++", Category - Code Generation й у пункті "Use run-time library" виберіть "Multithreaded". Після цього зробіть знову Build / Rebuild all і програма буде працювати.
Відкриття файлу й запис із нього
CString m_Text;// створення стандартної панелі вибору файлу SaveAs
CFileDialog DlgSaveAs(FALSE,(LPCSTR)"txt",NULL,
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
(LPCSTR)" Text Files (*.txt) |*.txt||");
// відображення стандартної панелі вибору файлу SaveAs
if(DlgSaveAs.DoModal()==IDOK)
{ // створення об'єкта й відкриття файлу для запису
CStdioFile File(DlgSaveAs.GetPathName(),
CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
// запис у файл рядка
File.WriteString((LPCTSTR)m_Text);
} 18$. Обробка виключень.
Розробити програму, що демонструє генерацію різних виняткових ситуацій і різні методи їхньої обробки. Програма повинна вміти породжувати й обробляти свої власні виключення.
Пояснення і рекомендації
Мова С представляє програмістові обмежені можливості обробки виключень, що виникли при роботі програми. Щодо цього С++ набагато більше розвинений. Тут у програміста істотно більші можливості по безпосередній обробці виключень. Комітет з розробці стандартів С++ надав дуже просту, але потужну форму обробки виключень. Типова функція, написана на С, виглядає приблизно так: long DoSomething()
{
long *a, c;
FILE *b;
a = malloc(sizeof(long) * 10);
if (a == NULL)
return 1;
b = fopen("something.bah", "rb");
if (b == NULL) {
free(a);
return 2;
}
fread(a, sizeof(long), 10, b);
if (a[0] != 0x10) {
free(a);
fclose(b);
return 3;
}
fclose(b);
c = a[1];
free(a);
return c;
}
Ви цілком і повністю залежите від значень, які повертають вам функції й для кожної помилки вам постійно потрібний код, що неї обробляє.
Try-catch-throw
Щоб комфортно працювати з виключеннями в С++ вам потрібно знати лише три ключових слова: • try (намагатися) - початок блоку виключень; • catch (піймати) - початок блоку, "ловящего" виключення; • throw (кинути) - ключове слово, "создающее" ("збудливе") виключення. А тепер приклад, що демонструє, як застосувати те, що ви довідалися: void func()
{
try
{
throw 1;
}
catch(int a)
{
cout << "Caught exception number: " << a << endl;
return;
}
cout << "No exception detected!" << endl;
return;
}
Якщо виконати цей фрагмент коду, то ми одержимо наступний результат: Caught exception number: 1
Якщо закоментировать рядок throw 1; функція видасть такий результат: No exception detected! Як бачите всі дуже просто, але якщо це застосувати з розумом, такий підхід здасться вам дуже потужним засобом обробки помилок. Catch може "ловити" будь-який тип даних, так само як й throw може "кинути" дані будь-якого типу. Т.е. throw AnyClass(); буде правильно працювати, так само як й catch (AnyClass &d) {};. Як уже було сказано, catch може "ловити" дані будь-якого типу, але зовсім не обов'язково при це вказувати змінну. Т.е. прекрасно буде працювати що-небудь типу цього: catch(dumbclass) { } так само, як й catch(dumbclass&) { }. Так само можна "піймати" і всі виключення: catch(...) { } Троеточие в цьому випадку показує, що будуть піймані всі виключення. При такому підході не можна вказати ім'я змінної. У випадку, якщо "кидаються" дані нестандартного типу (екземпляри певних вами класів, структур і т.д.), краще "ловити" їх по посиланню, інакше вся "кида" змінна буде скопійована в стек замість того, щоб просто передати покажчик на неї. Якщо кидаються дані декількох типів і ви хочете піймати конкретну змінну (вірніше, змінну конкретного типу), то можна використати кілька блоків catch, що ловлять "свій" тип даних: try {
throw 1;
// throw 'a';
}
catch (long b) {
cout << "пійманий тип long: " << b << endl;
}
catch (char b) {
cout << "пійманий тип char: " << b << endl;
}
"Створення" виключень
Коли збуджується виняткова ситуація, програма переглядає стек функцій доти, поки не знаходить відповідний catch. Якщо оператор catch не знайдений, STL буде обробляти виключення в стандартному оброблювачі, що робить усе менш добірно, чим могли б зробити ви, показуючи якісь незрозумілі (для кінцевого користувача) повідомлення й звичайно аварійно завершуючи програму. Однак більше важливим моментом є те, що поки проглядається стек функцій, викликаються деструктори всіх локальних класів, так що вам не потрібно забодиться про звільнення пам'яті й т.п.
Оператори throw без параметрів
Отже, ми побачили, як новий метод обробки помилок зручний і простий. Блок try-catch може містити вкладені блоки try-catch й якщо не буде визначено відповідного оператора catch на поточному уровен вкладення, виключення буде піймано на більше високому рівні. Єдина річ, про яку ви повинні пам'ятати, - це те, що оператори, що випливають за throw, ніколи не виконаються. try
{
throw;
// жоден оператор, що випливає далі (до закриваючої дужки) // виконаний не буде
}
catch(...)
{
cout << "Виключення!" << endl;
}
19$. Буфер обміну.
Розробити програму, що відслідковує зміну буфера обміну і якщо це растрове зображення, то воно відображається в спеціальному вікні.
Пояснення і рекомендації
Можна запропонувати наступний варіант реалізації.
Рис 19.1 Зовнішній вигляд додатка
Зовнішній вигляд програми зовсім простому й зрозумілий будь-якому користувачеві. На формі розміщена всього одна кнопка "Показувати картинку" (Show Picture). Користувач може переглянути вміст буфера обміну, нажавши кнопку Show Picture. Якщо буфер обміну містить растрове зображення, то з'являється вікно, у якому відображається даний малюнок (Рис 19.2).
Рис 19.2 Перегляд умісту буфера обміну
Якщо ж буфер обміну порожній, або не містить растрового зображення, то на екран виводиться повідомлення "Буфер обміну не містить картинки" (No picture in Clipboard) (Рис19.3).
Рис 19.3 Повідомлення
Закрити дане вікно можна, нажавши або кнопку OK, або /
20$ Проецируємі файлів.
Розробити програму, що дозволяє обмінюватися даними з іншою такою же програмою, використовуючи проєцируємі файли.
Пояснення і рекомендації
Створення об'єкта "проіцируємий файл"
Перший крок у відображенні файлу - це відкриття файлу за допомогою виклику функції CreateFile. Щоб гарантувати, що інші процеси не зможуть писати в частині файлу, що відображається, вам належить відкрити файл із монопольним доступом. Крім того, дескриптор файлу повинен залишатися відкритим доти, поки в процесу не зникне нестаток в об'єкті "проєцируємий файл". Легкий спосіб одержати монопольний доступ полягає в тому, щоб установити нуль у параметрі dwShareMode функції CreateFile. Дескриптор, повернутий CreateFile використається функцією CreateFileMapping, щоб створити об'єкт "проєцируємий файл".
Функція CreateFileMapping повертає дескриптор об'єкта "проєцируємий файл". Цей дескриптор повинен використатися при створенні подання файлу так, щоб Ви могли одержати доступ до спільно використовуваної пам'яті. Коли Ви викликаєте CreateFileMapping, то задайте ім'я об'єкта, число байтів, які відобразяться з файлу й дозвіл читання - запису у відображуваній пам'яті. Перший процес, що викликає функцію CreateFileMapping, створює об'єкт "проєцируємий файл". Процеси, що викликають CreateFileMapping для існуючого об'єкта одержують його дескриптор. Ви можете переконатися, чи успішно не завершився виклик CreateFileMapping, що створював або відкривав об'єкт "проєцируємий файл", за допомогою виклику функція GetLastError. GetLastError повертає значення NO_ERROR процесу, що створює, і ERROR_ALREADY_EXISTS наступним процесам.
Функція CreateFileMapping завершується помилкою, якщо прапорці доступу суперечать установленими тоді, коли функція CreateFile відкривала файл. Наприклад, щоб читати й записувати у файл:
Установите значення GENERIC_READ й GENERIC_WRITE у параметрі fdwAccess функції CreateFile.
Установите значення PAGE_READWRITE у параметрі fdwProtect функції CreateFileMapping.
Процедура створення об'єкта "проєцируємий файл" не закріплює фізичну пам'ять, вона тільки резервує її.
Розмір проєцируємого в пам'яті файлу
Розмір об'єкта "проєцируємий файл" не залежить від розміру відображуваного файлу. Однак, якщо об'єкт "проєцируємий файл" є більшим чим файл, система збільшує файл до повернення значення функцією CreateFileMapping. Якщо об'єкт "проєцируємий файл" є меншим чим файл, система проектує тільки певне число байтів з файлу.
Параметри dwMaximumSizeHigh й dwMaximumSizeLow функції CreateFileMapping дають можливість установлювати число байтів, які будуть відображатися з файлу:
Windows 95/98/Me: Параметр dwMaximumSizeHigh не використається, тому що ці операційні системи не підтримуються розрядн-32-розрядною файловою системою. Це значення повинне бути - нуль.
Якщо Ви не хочете змінювати розмір файлу (наприклад, коли відображаєте файл тільки для читання) викличте функцію CreateFileMapping й установите обоє її параметрів dwMaximumSizeHigh й dwMaximumSizeLow у нуль. Виконання цього прийому створює об'єкт "проєцируємий файл" точно такого ж розміру, як і файл. У противному випадку, Ви повинні обчислити або приблизно підрахувати розмір закінченого файлу, тому що об'єкти "проєцируємий файл" є статичними по розмірі; після створення їхній розмір не може збільшитися або зменшитися. Спроба проектувати файл із нульовою довжиною цим способом завершується помилкою з кодом помилки ERROR_FILE_INVALID. Програми повинні виявляти файли з нульовою довжиною й відкидати такі файли:
Windows NT/2000 або вище: Розмір об'єкта "проєцируємий файл", що під'єднується до іменованого файлу обмежений дисковим простором. Розмір подання файлу обмежений найбільшим доступним безперервним блоком незарезервованої віртуальної пам'яті. Це - якнайбільше - 2 Гбайта мінус віртуальна пам'ять, раніше зарезервована процесом.
Windows 95/98/Me: Розмір об'єкта "проєцируємий файл", що приєднується до іменованого файлу також обмежений дисковим простором. Розмір подання файлу обмежений найбільшим доступним безперервним блоком незарезервованої віртуальної пам'яті в спільно використовуваному адресному просторі. Це - якнайбільше 1 Гбайт мінус віртуальна пам'ять використовувана іншими процесами, такими як розрядн-16-розрядні базовані на Windows додатка або базовані на Win32 прикладні програми, які використають відображення (проектування) файлу на згадку.
Розмір об'єкта "проєцируємий файл" вибирається за допомогою контролю, як далеко у файлі з відображенням у пам'яті Ви можете "бачити". Якщо Ви створюєте 500 кілобайтний об'єкт "проєцируємиый файл" , Ви маєте доступ тільки до перших 500 КБ файлу, незалежно від розміру його файлу. Тому що, щоб створити більший об'єкт "проєцируємий файл", не потрібно від Вас системних ресурсів, створіть його по розмірах однаковим з файлом (установивши нулі в обох параметрах dwMaximumSizeHigh й dwMaximumSizeLow функції CreateFileMapping), навіть якщо Ви не припускаєте переглядати весь файл. У витрати системних ресурсів входить створення подань і доступу до них.
Якщо Ви хочете переглянути частину файлу, що починається не на початку файлу, Ви повинні створити об'єкт "проєцируємий файл". Цей об'єкт - розмір частини файлу, що Ви хочете переглянути плюс зсув у файлі.
Створення подання файлу
Щоб спроєцировати дані з файлу у віртуальну пам'ять процесу, Ви повинні створити подання файлу. Функції MapViewOfFile й MapViewOfFileEx використають дескриптор об'єкта "проєцируємий файл", повернутий CreateFileMapping, щоб створити подання файлу або його частини у віртуальному адресному просторі процесу. Ці функції завершуються помилкою, якщо прапорці доступу суперечать прапорцями певними тоді, коли CreateFileMapping створювала об'єкт "проєцируємий файл".
Функція MapViewOfFile повертає покажчик на подання файлу. За допомогою розіменування (одержання значення об'єкта, до якого відсилає даний покажчик) покажчика в діапазоні адрес, певних у функції MapViewOfFile, додаток може читати дані з файлу й писати дані у файл. Запис у подання файлу відбувається в результаті змін в об'єкті "проєцируємий файл ". Фактичний запис у файл на диску обробляється системою. Дані в дійсності не переміщаються, коли йде запис в об'єкт "проєцируємий файл". Замість цього, більша частина файлового уведення й висновку (I/O) даних хешується, щоб поліпшити загальну продуктивність системи. Додатка можуть скасувати цей режим роботи за допомогою виклику функції FlushViewOfFile, щоб змусити систему виконувати дискові транзакції (групові операції) негайно.
Функція MapViewOfFileEx працює в точній відповідності з функцією MapViewOfFile за винятком того, що вона дає можливість процесу в параметрі lpvBase установити базову адресу подання файлу в його віртуальному адресному просторі. Якщо по зазначеній адресі недостатньо місця, виклик завершується помилкою. Тому, якщо Ви повинні проектувати файл, по тому самому адресі в кілька процесів, процеси повинні домовитися про відповідну адресу:
Windows NT/2000 або вище: Параметром lpvBase повинне бути ціле число, кратне ступені дроблення розподіленої оперативної пам'яті, або виклик завершується помилкою. Щоб одержати ступінь дроблення розподіленої оперативної пам'яті, використайте функцію GetSystemInfo, що заповнює поля в членах структури SYSTEM_INFO.
Windows 95/98/Me: Адреса округляється вниз до найближчого цілого числа, кратного ступеня дроблення розподіленої оперативної пам'яті. Для наступних подань файлу, якщо зазначена адреса не відповідає адресі, у який система відобразила подання файлу, робота MapViewOfFileEx завершується помилкою.
Додаток може створити кілька подань файлу з того самого об'єкта "проєцируємий файл". Подання файлу може бути іншим по розмірі, чим об'єкт "проєцируємий файл", з якого воно отримано, але воно повинне бути менше, ніж об'єкт "проєцируємий файл". Зсув, що задає параметрами dwOffsetHigh й dwOffsetLow функції MapViewOfFile повинне бути кратне ступеня дроблення розподіленого простору системи.
Закриття об'єкта "проєцируємий файл"
Коли процес закінчує роботу з об'єктом "проєцируємий файл", він повинен знищити всі подання файлу у своєму адресному просторі, використовуючи функцію UnmapViewOfFile для кожного подання файлу. Ця функція анулює покажчик на віртуальний адресний простір процесу. Якщо яка - або зі сторінок подання файлу змінилася, з тих пор як подання було відображено, система записує змінені сторінки файлу на диск, використовуючи хешування. Щоб передати дані на диск негайно, викличте функцію FlushViewOfFile перед припиненням відображення подання файлу.
Ви повинні викликати функцію CloseHandle, щоб спочатку закрити об'єкт "проєцируємий файл", а потім закрити файл на диску. Ці виклики CloseHandle завершуються успішно, навіть тоді, коли є подання файлу, які усе ще є відкритими. Однак, що залишилися відображення подань файлу стають причиною витоку пам'яті.
21$. Керування енергозбереженням.
Поки потік працює, система не повинна перейти до сплячого режиму.
Пояснення і рекомендації
Керування енергозбереженням НИже наведена таблиця зі списком функцій обеспечивающих керування енергозбереженням.ФункціяОпис
CallNtPowerInformationУстановлює або одержує інформацію про харчування.
CanUserWritePwrSchemeОдержує інформацію про те, чи має поточний користувач право записувати схему харчування комп'ютера.
DeletePwrSchemeВидаляє певну схему харчування.
EnumPwrSchemesПерераховує всі схеми харчування.
GetActivePwrSchemeОдержує номер активної схеми харчування.
GetCurrentPowerPoliciesОдержує поточні настроювання політики системного харчування.
GetDevicePowerStateОдержує поточний стан енергозабезпечення обраного пристрою.
GetPwrCapabilitiesОдержує інформацію про можливості системного енергозабезпечення.
GetPwrDiskSpindownRangeОдержує діапазон останова диска.
GetSystemPowerStatusОдержує статус харчування системи.
IsPwrHibernateAllowedВизначає чи підтримує комп'ютер режим hibernation.
IsPwrShutdownAllowedВизначає чи підтримує комп'ютер "the soft off" стан харчування.
IsPwrSuspendAllowedВизначає чи підтримує комп'ютер сплячий режим.
IsSystemResumeAutomaticУказує поточний стан комп'ютера.
ReadGlobalPwrPolicyОдержує поточні настроювання глобальної політики харчування.
ReadProcessorPwrSchemeОдержує політику харчування процесора для певної схеми енергозбереження.
ReadPwrSchemeОдержує настроювання політики, які унікальні для певної схеми енергозбереження.
RequestWakeupLatencyПриблизно визначає як незабаром комп'ютер може перейти в робочий стан.
SetActivePwrSchemeУстановлює активну схему енергозбереження.
SetSuspendStateУстановлює стан припинення системи.
SetSystemPowerStateПрипиняє систему шляхом відключення харчування.
SetThreadExecutionStateДозволяє додаткам інформувати систему що що вони використаються.
WriteGlobalPwrPolicyЗаписує глобальну політику харчування.
WriteProcessorPwrSchemeЗаписує політикові харчування процесора для обраної схеми энерносбережения.
WritePwrSchemeЗаписує политиеку яка унікальна для схеми енергозбереження.
22$. Стиск даних.
Функції, використовувані Бібліотекою відновлення стислих даних
Пояснення і рекомендації
Нижченаведена таблиця описує кожну функцію, що перебуває в LzExpand.dll. Вони використаються, щоб розгорнути файли, які стискувалися по алгоритму Лемпеля - Зива.ФункціяПризначення
GetExpandedNameВитягає первісне ім'я стислого файлу, якщо в ході стиску файлу використався параметр /r.
LZCloseЗакриває файл, що був відкритий коли додаток викликало функцію LZOpenFile.
LZCopyКопіює вихідний файл у вихідний файл. Якщо вихідний файл стислий, ця функція створює розпакований вихідний файл. Якщо вихідний файл не стислий, ця функція робить копію первісного файлу. Ця функція призначена для операцій копіювання окремо взятого файлу.
LZInitПризначає пам'ять для структур використовуваних, щоб розвертати файли, а потім створює й инициализирует їх.
LZOpenFileСтворює, відкриває, повторно відкриває або видаляє зазначений файл і повертає дескриптор, що ідентифікує його.
LZReadЧитає максимально задане число байтів з файлу й копіює їх у буфер. Якщо файл стислий, ця функція розвертає байти перед копіюванням їх у приймаючий буфер.
LZSeekПоміщає покажчик позиції у файлі усередині розпакованого образа стислого файлу. Додаток викликає цю функцію для того, щоб помістити покажчик перед викликом функції LZRead.
23$. Таймери.
Пояснення і рекомендації
Можна запропонувати наступний варіант реалізації.
Рис 23.1 Вікно на запуск таймера
Зовнішній вигляд програми показаний на малюнку (Рис 28.1). Можна задати час відліку (годинники, хвилини й секунди). При натисканні на кнопку старт, почнеться відлік у зворотному порядку.
Для зупинки відліку служить кнопка Стіп (Рис 23.2).
Рис 23.2 Вікно на припинення таймера
Закінчення відліку супроводжується звуковим сигналом і повідомленням (Рис 23.3). Для виходу із програми служить кнопка "Вихід". Рис 23.3 Вікно при закінченні відліку
24$. Налагодження.
Розробити програму, що дозволяє відслідковувати запущена вона в середовищі отладчика або працює у звичайному режимі. Якщо виявлено отладчик, програма повинна виводити відповідне повідомлення й намагатися завершити своє виконання.
Пояснення і рекомендації
По-перше, треба знати як отладчик попадає в процес. Зробити це він може двома методами: створити ваш процес CreateProcess із прапором DEBUG_PROCESS або викликати DebugActiveProcess, щоб приєднатися до вже виконуваного процесу. У кожному разі, він залишає "сліди" у вашому процесі, якими користується функція IsDebuggerPresent.
Функція IsDebuggerPresent показує, чи запущений зухвалий її процес у контексті отладчика. Ця функція експортується з KERNEL32.DLL.
BOOL IsDebuggerPresent(VOID) - Аргументи: У цієї функції немає аргументів.
- Повертає значение, що:
Якщо поточний процес запущений у контексті отладчика, що повертає значення не дорівнює нулю. Якщо поточний процес не запущений у контексті отладчика, що повертає значення дорівнює нулю. Для припинення роботи отладчика служить функція VOID DebugBreak(VOID)
- Аргументи: У цієї функції немає аргументів.
- Повертає значение, що: Ця функція не возращает ніяке значення
25$. Керування пам'яттю.
Розробити програму, що демонструє механізми керування віртуальною пам'яттю.
Пояснення і рекомендації
Microsoft Win32 API надає набір функцій, які дозволяють процесу маніпулювати або визначати статус сторінок віртуального адресного простору. При цьому підтримується виконання наступних операцій:
Резервування області віртуального адресного простору. При резервуванні адресного простору не відбувається передача фізичної пам'яті, але тільки запобігає інші операції виділення пам'яті, які використають зазначену область. Ця операція ніяк не торкає віртуального адресного простору іншого процесу. Резервування пам'яті предовращает зайві витрати фізичної пам'яті, і надають процесу можливість заздалегідь зарезервувати адресний простір для таких динамічних структур даних, які мають властивість рости в часі. Для них процес одержує можливість виділяти рівно стільки фізичної пам'яті, скільки необхідно.
Передавати зарезервованої області віртуального адресного простору процесу физиескую пам'ять (в RAM або на вінчестері), що доступна тільки процесу, що викликав.
Указувати атрибути читання/запису, тільки читання або відсутності доступу для області пам'яті, що має під собою фізичну пам'ять. Це відрізняє функції по роботі з віртуальною пам'яттю від відповідних стандартних функцій виділення пам'яті, які завжди виділяють фрагмент пам'яті доступом як для читання, так і для запису.
Знімати резервування сторінок, роблячи область віртуальної пам'яті доступної для наступних операцій виділення пам'яті зухвалим процесом.
Звільнення сторінок, під якими перебувала фізична пам'ять, з відповідним звільненням фізичної пам'яті, що ставати доступною для наступного виділення пам'яті іншими процесами.
Фіксування розташування однієї або декількох сторінок, під якими розташована фізична пам'ять, в оперативній пам'яті (RAM), що забороняє системі переміщати зазначені сторінки у файл підкачування.
Одержувати інформацію про діапазон сторінок віртуального адресного протранства зухвалого або зазначеного процесу.
Змінювати атрибути захисту сторінок зазначеного діапазону, під якими розташована фізична пам'ять, для зухвалого або зазначеного процесу.
Виділення віртуальної пам'яті
Функції для роботи з віртуальною пам'яттю звертаються зі сторінками пам'яті. Ці функції використають розмір сторінки на поточному комп'ютері для округлення (з надлишком) зазначених розмірів й адрес. Функція VirtualAlloc використається для виконання однієї з наступних операцій:
Резервування однієї або більше вільних сторінок пам'яті.
Передача фізичної пам'яті однієї або більше резервованих сторінкам.
Резервування й передача фізичної пам'яті однієї або більше вільних сторінок пам'яті.
Ви можете або вказати системі стартова адреса сторінок пам'яті, з якого ви хотіли б зарезервувати або по якому ви хочете виділити фізичну пам'ять, або надати системі самої визначити ця адреса. Функція робить вирівнювання переданої адреси на границю сторінок. Зарезервовані сторінки недоступні програмі, але при передачі физичесокой пам'яті сторінкам ви можете вказати атрибути доступу PAGE_READWRITE, PAGE_READONLY або PAGE_NOACCESS. При передачі фізичної пам'яті сторінці для неї виділяється місце у файлі підкачування. Сторінка инициализируется й переміщається в оперативну пам'ять тільки при першій спробі читання або запису за адресою, що належить сторінці. Ви можете використати звичайні покажчики для одержання доступу до віртуальної пам'яті, для якої була передана фізична пам'ять постредством виклику функції VirtualAlloc.
Звільнення віртуальної пам'яті
Функція VirtualFree предназначеа для выполения однієї з наступних операцій:
Звільнення фізичної пам'яті, займаною однієї або декількома сторінками. Стан сторінок змінюється на зарезервоване. Звільнені сторінки оперативної пам'яті становяться доступними для виділення будь-яким процесом. Звільнення фізичної пам'яті може бути зроблене для будь-якого блоку сторінок.
Звільнення блоку, що складає з однієї або більше сторінок зарезарвированной пам'яті, зі зміною стану сторінок на вільне. Звільнення блоку сторінок робить їх доступними для виділення процесом. Сторінки можуть бути звільнені тільки в тому випадку, якщо вони предарительно були зарезервовані викликом VirtualAlloc.
Звільнення фізичної пам'яті, займаною однієї або декількома сторінками й зняття із цього блоку резервирвания. При цьому стан сторінки змінюється на вільне. Зазначений блок повинен містити блоки, які були зарезервовані шляхом виклику VirtualAlloc, а також блоки, яким була передана фізична пам'ять і ніякі більше.
Після того, як блок був звільнений, або його фізична пам'ять була передана системі, ви не повинні ніколи звертатися до нього. Вся інформація, що перебувала в блоці є назавжди загубленою. Спроба читання або запису по адресах, приналежащих звільненим сторінкам приводить до виключення порушення доступу (access violation). Якщо ви маєте потребу в інформації, що храиться в блоці, ніколи не звільняйте з під нього фізичну пам'ять або не знімайте з його резервування.
Для того, щоб указати, що дані в деякому діапазоні пам'яті довгий час не будуть представляти для вас інтересу, варто викликати функція VirtualAlloc, передавши їй у якості одного з параметрів прапор MEM_RESET. У цьому випадку зазначені сторінки при першому ж зручному випадку будуть перещены у файл підкачування. Проте, блок пам'яті можна буде використати надалі.
Робота зі сторінками
Для того, щоб визначити розмір сторінки на поточному комп'ютері, необхідно використати функцію GetSystemInfo
Функції VirtualQuery й VirtualQueryEx дозволяють одержати інформацію про регіон, що складається з некольких послідовно розташованих сторінок пам'яті, що починаються із зазначеної адреси у віртуальному адресному просторі процесу. При цьому функція VirtualQuery дозволяє одержати інформацію про пам'яті в зухвалому процесі, а VirtualQueryEx у зазначеному процесі й звичайно використається для поддежки отладчиков, які мають потребу в інформації про отлаживаемом процес. Якщо зазначена адреса не попадає на границю сторінки, то він округляється вниз до першого граничного значення. Зазначений блок розширюється за рахунок наступних сторінок, якщо вони мають наступні однакові атрибути:
Всі сторінки мають однаковий стан: їм передана фізична пам'ять, або вони зарезервовані, або вони вільні.
Якщо початкова сторінка пам'яті не є вільної, то буде дана інформація тільки про ті сторінки, які були зерезервированы тим викликом VirtualAlloc, що і зарезервував початкову сторінку діапазону.
Захист доступу у всіх сторінок повинна бути та сама (така, як PAGE_READONLY, PAGE_READWRITE або PAGE_NOACCESS).
Функція VirtualLock дозволяє зафіксувати одну або кілька сторінок, які имют під собою фізичну пам'ять, в оперативній пам'яті (RAM), запобігаючи сбрасвание цих сторінок ситемой у файл підкачування. Ця функція гарантує, що звертання до зазначених критичних сторінок пройде без звертання до диска. Фіксування сторінок у пам'яті є небезпечним, тому що воно обмежує можливості системи по керуванню пам'яттю. Надмірне використання функції VirtualLock може понизити работоcпособность системи через часте скидання сторінок з кодом, що виконується, у файл підкачування. Функия VirtualUnlock знімає фіксацію з пам'яті, що була зафіксована викликом VirtualLock.
Функція VirtualProtect дозволяє процесу змінити права доступу до будь-якої сторінки пам'яті, що має під собою фізичну пам'ять в адресному простанстве процесу. Наприклад, процес може виділити сторінки з доступом як для читання, так і для запису, а після того, як він змінить у них дані, установити для них доступ тільки для читання, або взагалі заборонити всякий до них доступ з метою запобігти випадковому перезапису. Функція VirtualProtect звичайно використається при роботі з пам'яттю, що була виділена функцією VirtualAlloc, але вона може також працювати й з пам'яттю, що виділила будь-яка інша функція. Однак виклик функції VirtualProtect змінює атрибути доступу відразу в цілій сторінці, а покажчики, які повертають інші функції, не обов'язково вирівняні по границі сторінки. Функція VirtualProtectEx подібна функції VirtualProtect, за виключення того, що вона дозволяє міняти атрибути доступу до пам'яті зазначеного процесу. Такі зміни атрибутів доступу звичайно потрібні отладчикам при доступі до пам'яті отлаживаемого процесу.
26$. Динамічно завантажуємі бібліотеки (DLL).
Створити бібліотеку функцій для роботи із двовимірними матрицями й скомпонувати її у вигляді DLL. Набір функцій повинен включати: транспонування матриць, обчислення визначника квадратної матриці, визначення зворотної матриці, множення двох матриць, додавання матриць, множення матриці на скаляр. Розробити програму, що демонструє використання функцій з бібліотеки. У програмі використати динамічне зв'язування.
Пояснення і рекомендації
Динамічно завантажуємі бібліотеки (DLL) - виконуваний програмний модуль Microsoft Windows, що завантажує тільки при необхідності (по запиті).
Процес створення динамічної бібліотеки проходить у два етапи. Перший з них полягає в створенні файлу-заголовка (*. h ), що визначає основні властивості проекту. Для цього необхідно створити новий проект, вибравши як тип проекту Dinamic Linked Library , після чого визначити "порожній проект" ( empty project ). Потім створити файли проекту. Нехай ім'я проекту буде funlib .
Далі створити файл заголовка - funlib . h (за допомогою меню File\New..., вибравши файл-заголовок). У цей файл включити наступний текст : // File funlib.h #define EXPORT extern "C" __declspec(dllexport) EXPORT BOOL CALLBACK return333(); EXPORT int CALLBACK MyInc(int i); Перший рядок файлу визначає деяку директиву EXPORT , що буде використатися для підключення сервисов, необхідних для створення посилань на експортовані функції (тобто функції, які можуть бути використані або імпортовані іншими програмами). Наступні два рядки є попереднім оголошенням функцій, директива EXPORT і робить їхніми динамічними функціями.
Тепер залишилося написати текст програми *. cpp:
//funlib.cpp #include #include #include "fulib.h" int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { return TRUE; } EXPORT BOOL CALLBACK EdrCenterText() { return 333; } EXPORT int CALLBACK MyInc(int i) { return ++i; } &nbspDllMain - аналог функції main для консольних додатків і функції WinMain для додатків, написаний під Windows . Ця функція автоматично викликається при завантаженні будь-який dll . Значення, що повертає, TRUE свідчить про успішне завантаження й ініціалізацію внутрішніх ресурсів. Створимо звичайний консольний додаток Win 32 і назвемо його usedll . Для використання funlib . dll необхідно виконати наступні: помістити в каталог проекту файли funlib . h й funlib . lib . Другий файл є результатом компіляції попереднього проекту і його можна знайти в каталозі Debug . Крім цього, у меню Project\Settings \ Link, Категорія General, поле Object\library modules необхідно вписати ім'я файлу-бібліотеки для організації настроювань зв'язування. Текст програми usedll.cpp наведений нижче.
// usedll.cpp : Defines the entry point for the console application. #include "stdafx.h" #include "iostream.h" #include "funlib.h" int main(int argc, char* argv[]) { cout << return333() << endl; cout << MyInc(5) << endl; return 0; } Дана програма виконається якщо в каталозі, де розташовується файл usedll . exe розташований файл динамічної бібліотеки funlib . dll . У результаті виконання даної програми на екрані ви побачите наступну картину:
333 6 У висновку слід зазначити, що використовуючи механізми динамічного зв'язування, можна визначати не тільки функції, але змінні й навіть класи.
Другий спосіб використання DLL.
Розглянутий вище спосіб написання програми, що використає підключа динамічно бібліотеки придатний тільки в тому випадку, коли ви самі створили бібліотеки, або, принаймні, ви маєте в наявності файл бібліотеки *. lib . Однак, дуже часто трапляється так, що є тільки *. dll (або вона тільки планується). У цьому випадку можна використати інший спосіб, що не вимагає для компіляції проекту наявності файлів, пов'язаних з викликуваної *. dll .
Застосування даного способу полягає в наступному: • Створюються прототипи функцій, що мають ті ж параметри, що й необхідні функції усередині *. dll . Для нашого приклада це буде виглядати так:
typedef BOOL (WINAPI * Inc)(int i); • Оголошується змінна типу покажчика на дану функцію: Inc p ; • У програмі здійснюється завантаження бібліотеки: HINSTANCE h; h=LoadLibrary("funlib.dll");
• оголошеному покажчику на функцію привласнюється адреса функції з *. dll : p=(Inc)GetProcAddress(h,(LPCSTR)2); • Функція використається по призначенню: cout << p(6); • Після використання вивантажується з пам'яті: FreeLibrary(h); Цілком розглянутий приклад виглядає в такий спосіб: #include "stdafx.h" #include "iostream.h" #include "windows.h" HINSTANCE h; typedef int (CALLBACK * Inc)(int i); Inc p; int main(int argc, char* argv[]) { h=LoadLibrary("1111.dll"); p=(Inc)GetProcAddress(h,(LPCSTR)2); cout << p(6); FreeLibrary(h); return 0; }
Наявність *. dll потрібно тільки під час запуску *. exe файлу.
27. Динамічний обмін даними.
Розробити DDE-сервер й DDE-клієнта для обміну даними, використовуючи гаряче зв'язування.
Пояснення і рекомендації
DDE.
Давній протокол для обміну даними між різними додатками, що з'явився ще на зорі ери Windows. З тих пор на його базі був створений інтерфейс OLE, а в 32-розрядному API Windows з'явилися й інші методи міжпрограмної взаємодії. Але ніша, займана DDE, залишилася незмінної - це оперативна передача й синхронізація даних у додатках.
Додатка, що використають DDE, розділяються на дві категорії - клієнти й сервери (не плутати з однойменною архітектурою СУБД). Обоє учасника процесу здійснюють контакти (conversations) по певних темах (topic), при цьому в рамках теми виробляється обмін елементами даних (items). Установлює контакт клієнт, що надсилає запит, що містить імена контакту й теми. Після встановлення контакту всяка зміна елемента даних на сервері передається даним клієнта.
Первісне програмування DDE було надзвичайно складною справою. Воно вимагало взаємозалежної обробки більш ніж десяти повідомлень Windows. У версії Windows 3.1 з'явилася бібліотека DDEML, що перевела керування DDE на рівень виклику процедур. Розроблювачі підсистеми DDE в Delphi, вірні ідеології створення VCL, звели інтерфейс цього протоколу до чотирьох компонентів: двом для сервера й двом для клієнта.
На рівні підтримки контакту лежать компоненти TDDEServerConv й TDDEClientConv. Перший відіграє пасивну роль - він тільки вказує ім'я однієї з підтримуваних сервером тим. Всі операції по встановленню й розриву контакту здійснює з додатка-клієнта другий компонент.
За допомогою одного контакту можуть бути зв'язані й синхронізована пара елементів даних. Для їхнього опису призначені компоненти TDDEServerItem й TDDEClientItem. Кожний з них під час роботи повинен указувати на контакт, до якого він прив'язаний. Крім того, у складі обох є властивості, що містять якийсь текст. При встановленому контакті їхній уміст синхронізується.
Крім цього в модулі DDEMAN описаний і п'ятий компонент, що управляє всіма зв'язками DDE. Архітектура "клієнт-сервер".
Рис 8.1 Взаємодія між клієнтом і сервером
Процес передачі запиту серверу можна називати транзакцією.
Строго говорячи, транзакція - це сукупність трьох дій: посилка запиту, виконання запиту, прийом відповіді.
Ініціалізація й створення каналу зв'язку
У процесі ініціалізації сервер повинен виконати такі дії:
зареєструвати себе в бібліотеці DDEML;
зареєструвати надаваний сервіс, яким зможе скористатися додаток-клієнт.
Клієнт повинен зробити наступне: зареєструвати себе в бібліотеці DDEML;
створити канал зв'язку із сервером, указавши необхідний сервіс.
Розглянемо ці дії докладніше. Реєстрація в бібліотеці DDEML
Якщо додаток збирається використати DDEML, він повинне зареєструвати себе в бібліотеці DDEML, викликавши спеціально призначену для цього функцію з ім'ям DdeInitialize.
Прототип функції DdeInitialize визначений у файлі ddeml.h (який повинен бути включений у вихідний текст DDEML-додатка поряд з файлом windows.h): UINT WINAPI DdeInitialize( DWORD FAR* pidInst,// адреса ідентифікатора додатка PFNCALLBACK pfnCallback, // адреса функції зворотного виклику DWORD afCmd, // прапори
DWORD ulRes);// зарезервовано Функція DdeInitialize використається в процесі ініціалізації й серверів, і клієнтів DDEML. Сама по собі вона не створює ніяких каналів передачі даних між додатками, однак процедура реєстрації додатка, виконуваний цією функцією, повинна бути проведена до виклику будь-яких інших функцій, що мають відношення до DDEML.
Займемося параметрами функції DdeInitialize.
Параметр pidInst - є покажчик на подвійне слово, у яке після реєстрації буде записаний ідентифікатор, привласнений копії додатка бібліотекою DDEML (одночасно можуть працювати кілька копій того самого DDEML-додатка). Іншими словами, у процесі реєстрації бібліотека DDEML присвоює копії додатку деякий ідентифікатор, під яким вона його "знає". Ви повинні вказувати отриманий від функції ідентифікатор при виклику всіх інших функцій бібліотеки DDEML.
Перед викликом функції DdeInitialize ваш додаток повинне записати в подвійне слово, адресу якого передається через перший параметр, нульове значення. Помітимо, що ідентифікатор копії додатку, присвоєний у процесі реєстрації, і ідентифікатор копії додатка, отриманий через параметр функції WinMain - різні за змістом (і за значенням) ідентифікатори.
Параметр pfnCallback - є покажчиком на функцію зворотного виклику додатком для обробки транзакцій. Як сервер, так і клієнт повинні визначити таку функцію. Функція зворотного виклику викликається системою DDEML і містить у собі всю логіку обробки транзакцій.
Якщо додаток викликає функцію DdeInitialize кілька разів для багаторазової реєстрації, щораз варто вказувати окрему функцію зворотного виклику. Багаторазова реєстрація цілком припустима, тому що щораз бібліотека DDEML буде створювати для себе новий ідентифікатор додатка. Така методика використається при створенні DLL-бібліотек, що працюють із DDEML. Звичайним додаткам досить зареєструвати себе один раз й, відповідно, визначити одну функцію зворотного виклику.
Через параметр afCmd передається подвійне слово, кожен біт якого є прапором, що визначає режими роботи каналу зв'язку, а також впливають на дії, виконувані функцією DdeInitialize.
Останній параметр із ім'ям ulRes зарезервований і повинен мати нульове значення.
Приведемо фрагмент коду, що виконує реєстрацію сервера в бібліотеці DDEML:
idInst = 0L;
lpDdeSrProc = MakeProcInstance((FARPROC)DDEServerCallback, hInst);
if(DdeInitialize((LPDWORD)&idInst, (PFNCALLBACK)lpDdeSrProc,
APPCLASS_STANDARD, 0L))
{
return FALSE;
}
У цьому фрагменті спочатку створюється перехідник для функції зворотного виклику, потім адреса цього перехідника вказується в другому параметрі функції DdeInitialize.
У випадку успіху функція DdeInitialize повертає нульове значення. Для перевірки можна також використати константу DMLERR_NO_ERROR, певну у файлі ddeml.h. Якщо відбулася помилка, повертається ненульовий код помилки. Відповідні константи визначені у файлі ddeml.h і мають префікс імені DMLERR.
Про прапори, переданих через параметр afCmd.
Символічні константи із префіксом імені APPCLASS дозволяють задати клас додатка з погляду використання DDEML.
Клас APPCLASS_STANDARD призначений для реєстрації стандартного DDEML-додатка. Цей клас використаний у наведеному вище фрагменті коду.
Клас APPCLASS_MONITOR призначений для відгадчиків і інших додатків, керуючих роботою системи DDEML.
Символічні константи із префіксом імені APPCMD дозволяють конкретизувати функції, виконувані додатком, і заощаджувати системні ресурси. Якщо DDEML-додаток виконує тільки функції клієнта, варто вказати прапор APPCMD_CLIENTONLY:
if(DdeInitialize((LPDWORD)&idInst, (PFNCALLBACK)lpDdeClProc,
APPCMD_CLIENTONLY, 0L))
{
return NULL;
}
У найпростіших випадках можна обмежитися використанням класу APPCLASS_STANDARD при створенні сервера DDEML і прапора APPCMD_CLIENTONLY при створенні клієнта DDEML. Інші прапори впливають на те, коли й навіщо буде викликатися функція зворотного виклику.
Якщо додаток більше не збирається працювати з бібліотекою DDEML, він повинне викликати функцію DdeUninitialize, передавши їй як єдиний параметр ідентифікатор копії додатка, отриманий від функції DdeInitialize:
BOOL WINAPI DdeUninitialize(DWORD idInst);
Реєстрація сервісу
Наступний етап в ініціалізації сервера DDEML полягає в реєстрації надаваного їм сервісу.
Бібліотека DDEML використає триступінчасту схему адресації даних, переданих по каналі зв'язку - сервіс (service), розділ (topic) і елемент даних (data item). Додаток задає елементи адреси у вигляді текстових рядків розміром не більше 255 байт.
Сервер DDEML може надавати сервіс одного або декількох видів. Як правило, один сервер надає тільки один сервіс, причому текстовий рядок, що ідентифікує сервіс, часто збігається з ім'ям додатка. Але можна вибрати будь-який інший рядок.
Другий елемент адреси - розділ. У рамках одного сервісу можна визначити кілька розділів. Коли клієнт DDEML створює канал із сервером, він указує сервіс і розділ. Розділ поєднує групу елементів даних або виконуваних функцій.
Канал DDEML служить для передачі блоків даних. У рамках одного роздягнула сервер може обмінюватися із клієнтом різними блоками даних, кожний з яких ідентифікується при передачі ім'ям елемента даних. У процесі створення каналу зв'язку не потрібно вказувати елементи даних.
Реєстрація сервісу виконується сервером DDEML звичайно відразу після виклику функції DdeInitialize і виконується у два етапи.
На першому етапі текстовий рядок імені сервісу зберігається в спеціальній системній таблиці (таблиці атомів), для чого викликається функція DdeCreateStringHandle:
HSZ WINAPI DdeCreateStringHandle(
DWORD idInst,// ідентифікатор додатка
LPCSTR psz, // адреса текстового рядка
int iCodePage); // кодова сторінка
Через параметр idInst додаток повинне передати ідентифікатор, отриманий на етапі реєстрації додатка в бібліотеці DDEML функцією DdeInitialize.
Параметр psz являє собою покажчик на текстовий рядок, закритий двійковим нулем. Розмір цього рядка не повинен перевищувати 255 байт.
Як значення для параметра iCodePage можна вказати CP_WINANSI (ця константа дорівнює нулю). Можна також використати значення, отримане від функції GetKBCodePage. Функція GetKBCodePage не має параметрів і повертає номер поточної кодової сторінки.
Ідентифікатор текстового рядка, повернутий функцією DdeCreateStringHandle і відповідний регистрируемому сервісу, варто передати функції DdeNameService:
HDDEDATA WINAPI DdeNameService(
DWORD idInst, // ідентифікатор додатка
HSZ hsz1, // ідентифікатор рядка імені сервісу
HSZ hsz2, // зарезервовано
UINT afCmd); // прапори
Через параметр idInst додаток повинне передати ідентифікатор, отриманий на етапі реєстрації додатка в бібліотеці DDEML функцією DdeInitialize.
Параметр hsz1 призначений для передачі імені сервісу через ідентифікатор текстового рядка, повернутою функцією DdeCreateStringHandle.
Параметр hsz2 зарезервований, для нього варто використати нульове значення.
При реєстрації сервісу через параметр afCmd варто передати значення DNS_REGISTER (реєстрація сервісу). Сервер DDEML у процесі своєї роботи може динамічно реєструвати й скасовувати види надаваного сервісу. Для скасування сервісу через параметр afCmd передається значення DNS_UNREGISTER.
Перед завершенням роботи сервер DDEML повинен скасувати весь зареєстрований їм раніше сервіс, викликавши функцію DdeInitialize з параметром afCmd, що має значення DNS_UNREGISTER.
Якщо реєстрація сервісу виконана успішно, функція DdeNameService повертає ненульове значення, а при помилці - нульове.
Одночасно з реєстрацією сервісу сервер звичайно створює ідентифікатори текстових рядків, що містять імена використовуваних розділів й елементів даних. Для цього викликається все та ж функція DdeCreateStringHandle.
Відзначимо, що реєстрацію сервісу виконує тільки сервер DDEML. Що ж стосується створення ідентифікаторів текстових рядків функцією DdeCreateStringHandle, те ця операція виконується як сервером, так і клієнтом. Отримані ідентифікатори використаються при створенні каналу й у процесі передачі даних.
Знаючи ідентифікатор рядка, додаток може одержати рядок, викликавши функцію DdeQueryString:
DWORD WINAPI DdeQueryString(
DWORD idInst,// ідентифікатор додатка
HSZ hsz, // ідентифікатор рядка
LPSTR psz,// адреса буфера для запису рядка
DWORD cchMax,// розмір буфера
int iCodePage); // кодова сторінка
Призначення параметрів зрозуміло з коментарів у прототипі функції.
Якщо ідентифікатор створеного текстового рядка використається у функції зворотного виклику (яку ми розглянемо трохи пізніше), за звільнення ресурсів, пов'язаних з текстовим рядком, відповідає система DDEML. У противному випадку додаток повинне самостійно знищувати створені їм ідентифікатори, викликаючи функцію DdeFreeStringHandle:
BOOL WINAPI DdeFreeStringHandle(
DWORD idInst, // ідентифікатор додатка
HSZ hsz); // ідентифікатор знищуваного рядка
У випадку успіху функція DdeFreeStringHandle повертає ненульове значення, при помилці - нульове.
Функція зворотного виклику DDEML
Коли сервер або клієнт реєструє себе в бібліотеці DDEML за допомогою функції DdeInitialize, він указує адресу переходника, створеного для функції зворотного виклику. Функція зворотного виклику призначена для обробки всіх подій, що виникають у процесі створення каналів зв'язку й передачі даних.
У найпростішому випадку функція зворотного виклику сервера DDEML може виглядати в такий спосіб:
HDDEDATA EXPENTRY _export DDEServerCallback(
WORD wType, // код транзакції
WORD wFmt,// формат даних
HCONV hConv, // ідентифікатор каналу
HSZ hsz1, // перший ідентифікатор рядка
HSZ hsz2, // другий ідентифікатор рядка
HDDEDATA hData, // ідентифікатор глобальної області даних
DWORD dwData1, // перше додаткове подвійне слово
DWORD dwData2) // друге додаткове подвійне слово
{
switch(wType)
{
// Створення каналу передачі даних
case XTYP_CONNECT:
{
...........
return((HDDEDATA)TRUE);
}
// Запит даних від сервера
case XTYP_REQUEST:
{
...........
return(hData);
}
// Запит на виконання команди
case XTYP_EXECUTE:
...........
break;
// Передача даних серверу
case XTYP_POKE:
{
...........
return((HDDEDATA)DDE_FACK);
}
// Підтвердження створення каналу
case XTYP_CONNECT_CONFIRM:
{
...........
break;
}
// Завершення роботи каналу
case XTYP_DISCONNECT:
{
...........
break;
}
// Помилка
case XTYP_ERROR:
{
...........
break;
}
}
return((HDDEDATA)NULL);
}
Функція зворотного виклику повинна бути визначена як експортована, тому ми вказали ключове слово _export.
Через перший параметр wType передається код транзакції. Подібно функції вікна, що обробляє повідомлення, функція зворотного виклику DDEML виконує обробку транзакцій. Залежно від коду транзакції й результату обробки функція зворотного виклику DDEML повертає те або інше значення.
Другий параметр задає код формату переданих даних. Для кодів формату використаються ті ж значення, що й для форматів Clipboard, наприклад, CF_TEXT. Через параметр hConv передається ідентифікатор каналу передачі даних .
Призначення інших параметрів функції зворотного виклику залежить від коду транзакції. У наведеному вище фрагменті коду використаються символічні імена кодів транзакцій, певні у файлі ddeml.h, і имеющие префікс імені XTYP.
Функція зворотного виклику для клієнта DDEML виглядає точно також, відрізняючись лише складом оброблюваних транзакцій.
При реєстрації додатка в бібліотеці DDEML функцією DdeInitialize можна вказати прапори, що забороняють або дозволяють надходження транзакцій деяких типів у функцію зворотного виклику. Заборонивши виклик функції зворотного виклику для необроблюваних транзакцій, можна прискорити роботу додатка.
Створення й знищення каналу
Останнє, що потрібно зробити перед початком передачі даних по каналі DDEML, - створити канал зв'язку (conversation).
Канал зв'язку між клієнтом і сервером створюється завжди з ініціативи клієнта. Після реєстрації в бібліотеці DDEML клієнт викликає функцію DdeConnect, що створює канал зв'язку:
HCONV WINAPI DdeConnect(
DWORD idInst, // ідентифікатор додатка
HSZ hszService, // ідентифікатор рядка сервісу
HSZ hszTopic, // ідентифікатор рядка роздягнула
CONVCONTEXT FAR* pCC); // адреса даних контексту
Через параметр idInst додаток повинне передати ідентифікатор, отриманий на етапі реєстрації додатка в бібліотеки DDEML функцією DdeInitialize.
Параметри hszService й hszTopic призначені для передачі ідентифікаторів рядків, що містять, відповідно, імена сервісу й роздягнула. Ці ідентифікатори були отримані нами раніше за допомогою функції DdeCreateStringHandle.
Останній параметр - покажчик на структуру типу CONVCONTEXT. Ця структура використається для вказівки інформації про національну мову й кодову сторінку, що відповідає переданим даним. У найпростішому випадку для даного параметра можна вказати значення NULL, при цьому буде використана кодова сторінка CP_WINANSI (що прийнятно в переважній більшості випадків).
Ідентифікатор каналу, отриманий від функції DdeConnect, варто зберегти для забезпечення можливості одержання даних від сервера.
Що ж відбувається, коли клієнт створює канал, викликаючи функцію DdeConnect?
Насамперед, бібліотека DDEML посилає транзакцію з кодом XTYP_CONNECT всім активним серверам, які зареєстрували сервіс, зазначений у другому параметрі функції DdeConnect.
Для транзакції XTYP_CONNECT параметри функції зворотного виклику приймають наступні значення:ПараметрЗначення
hsz1Ідентифікатор рядка, що містить ім'я розділу hsz2Ідентифікатор рядка, що містить ім'я сервісу dwData1Адреса структури CONVCONTEXT dwData2Якщо значення дорівнює TRUE, дана копія додатка є одночасно й клієнтом, і сервером. Якщо ж значення дорівнює FALSE, клієнт і сервер є різними додатками або різними копіями додатка Оброблювач транзакції XTYP_CONNECT, розташований у функції зворотного виклику сервера, повинен перевірити сервіс і розділ, ідентифікатори яких передані через параметри функції. Якщо сервер підтримує цей сервіс і розділ, можна створювати канал. У такому випадку функція зворотного виклику повинна повернути значення TRUE. Інакше варто повернути FALSE.
У випадку успішного створення каналу сервер одержує від системи DDEML транзакцію з кодом XTYP_CONNECT_CONFIRM. При обробці цієї транзакції сервер може зберегти ідентифікатор створеного каналу (який передається функції зворотного виклику через параметр hConv) для подальшого використання.
Приведемо призначення параметрів функції зворотного виклику для транзакції XTYP_CONNECT_CONFIRM:ПараметрЗначення
hsz1Ідентифікатор рядка, що містить ім'я розділу hsz2Ідентифікатор рядка, що містить ім'я сервісу dwData2Якщо значення дорівнює TRUE, дана копія додатка є одночасно й клієнтом, і сервером. Якщо ж значення дорівнює FALSE, клієнт і сервер є різними додатками або різними копіями додатка Коли канал зв'язку більше не потрібний, клієнт або сервер може знищити його, викликавши функцію DdeDisconnect:
BOOL WINAPI DdeDisconnect(HCONV hConv); Як єдиний параметр цієї функції передається ідентифікатор знищуваного каналу.
У процесі видалення каналу "партнер" додатка, що виступив ініціатором видалення каналу, одержує транзакцію XTYP_DISCONNECT. Відповідний оброблювач може при необхідності виконати дії по звільненню ресурсів, замовлених додатком для роботи з даним каналом зв'язку.
Передача даних через канал DDEML
Передача й прийом даних може виконуватися в трьох режимах: по явному запиті, через "теплий" канал, або через "гарячий" канал.
У першому випадку клієнт посилає серверу запит, указавши потрібний елемент даних. Сервер, одержавши такий запит, надає клієнтові потрібні дані. "Теплий" й "гарячий" канали зв'язку створюються в тих випадках, коли клієнт повинен постійно стежити за змінами даних, що зберігаються в пам'яті сервера.
В "теплом" режимі при зміні даних сервер посилає клієнтові відповідне повідомлення. Одержавши таке повідомлення, клієнт запитує в сервера нові дані.
В "гарячому" режимі при зміні даних сервер самостійно посилає клієнтові дані без додаткового запиту.
28$ Протоколювання подій.
Програма повинна вміти створювати й переглядати різні типи подій.
Пояснення і рекомендації
Event Logging
Багато додатків записують інформацію про помилки й події в різні власні протоколи. Ці протоколи розрізняються форматами й тому з ними незручно працювати. Windows надає додаткам стандартний і централізований шлях для запису важливих програмних й апаратних подій. Цей сервіс називається - Event Logging. Він дозволяє зберігати події з різних джерел у єдиному списку, називаному протокол подій (event log). Для перегляду протоколу використається додаток Event Viewer. Існує п'ять типів подій, які можуть бути запротокольовані. Кожна подія може належати тільки одному типу подій. В Event Viewer ці типи використаються для визначення іконки, пов'язаної з подією. ЗначенняОпис
InformationОперація виконана успішно. WarningПопереджає, що в ході виконання операції виникла проблема, але вона не вимагає негайного втручання, хоча в майбутньому може привести до серйозних збоїв. ErrorПовідомляє про значні проблеми. Події типу Error звичайно відбивають втрату функціональності або даних. Success auditПодія системи захисту. Інформує, що операція, що вимагає правий доступу, виконана успішна. Failure auditЗбій одержання прав на виконання операції. Оскільки протокол подій є загальнодоступним сервісом, то ви повинні подумати які повідомлення записувати в нього. Рекомендується зберігати в ньому повідомлення, які можуть виявитися корисними при рішенні апаратних або програмних проблем. Не рекомендується його використати як інструмент для трасування. Повідомлення повинне містити повну й зрозумілу інформацію, необхідну, для визначення ситуації, що викликала проблему й що необхідно зробити, щоб цю проблему виправити. У тексті повідомлень не используйтесимволы табуляція й кома. При використанні UNC імен, або інших посилань, які містять пробіли, містіть ім'я в кутові дужки. Наприклад: <\\sharename\servername>. Не забувайте, протоколювання подій споживає ресурси комп'ютера - дисковий простір і процесорний час. Сервіс протоколювання подій використає інформацію із системного реєстру, із ключа EventLog. Цей ключ містить трохи подключей, називаних logfiles. У цих logfiles утримується інформація про розташування ресурсів, необхідних сервісу, коли додаток пише або читає протокол. За замовчуванням визначені протоколи Application, System й Security. Додатки й служби використають протокол - Application. Драйвера - System. Система безопастности пише події до протоколу Security. Запису про події містять час, тип і категорію події. Кожен запис має структуру EVENTLOGRECORD. Записи нумеруються починаючи з 1. Але якщо в Event Viewer установити властивість Overwrite events as needed, те при досягненні максимального розміру файлу протоколу, нові записи будуть писатися поверху старих. Тому не факт, що самий старий запис буде мати номер 1. Для одержання самого старого запису треба використати функцію GetOldestEventLogRecord, а для одержання наступних - GetNumberOfEventLogRecords. Кожен протокол містить підключи, називані джерелами повідомлень (event source). Джерело повідомлень - це звичайне ім'я додатка або його компонента. Додатки й служби повинні реєструватися в протоколі Application. Драйвери пристроїв - в System. Джерелам повідомлень не можна давати імена, уже використовувані для іменування файлів протоколів. Джерела повідомлень містять следущую інформацію: ЗначенняОпис
CategoryCountВизначає кількість підтримуваних категорій подій. Тип - REG_DWORD. CategoryMessageFileВизначає шлях до файлу категорії. Цей файл повинен містити опису категорій. Тип - REG_EXPAND_SZ. DisplayNameFileWindows 2000/XP: указує файл, що містить ім'я джерела подій. Це ім'я буде відображатися в Event Viewer. Якщо це значення не зазначене, то як ім'я використається ім'я ключа реєстру, у якому записаний джерело подій. Тип - REG_EXPAND_SZ. DisplayNameIDWindows 2000/XP: Містить числовий ідентифікатор рядка з ім'ям джерела з файлу, зазначеного в поле DisplayNameFile. Тип - REG_DWORD. EventMessageFileУказує файл повідомлень подій. У цьому полі можна вказати кілька файлів, розділених крапкою з коми. Файл повідомлень містить рядки, що описують події. Тип - REG_EXPAND_SZ. ParameterMessageFileУказує файл параметрів повідомлень. Файл параметрів повідомлень містить параметри, які будуть уставлятися в рядки описів подій. Тип - REG_EXPAND_SZ. TypesSupportedКомбінація підтримуваних типів подій. Тип - REG_DWORD. Може містити одне або трохи зі следущих значень: EVENTLOG_ERROR_TYPE
EVENTLOG_WARNING_TYPE
EVENTLOG_INFORMATION_TYPE
EVENTLOG_AUDIT_SUCCESS
EVENTLOG_AUDIT_FAILURE
Коли додаток викликає функцію RegisterEventSource або OpenEventLog, для одержання покажчика на протокол, служба протоколювання шукає зазначене ім'я в реєстрі. Якщо джерело з таким ім'ям не знайдений, то використається джерело за замовчуванням Application. Але тому що він не містить файлу повідомлень, відбудеться помилка. Тому ви повинні додати своє джерело. Категорії дозволяють організувати події, у такий спосіб Event Viewer може їх фільтрувати. У кожнім джерелі можна визначити свої власні категорії. Категорії повинні бути пронумеровані послідовно починаючи з 1. Максимальне число категорій утримується в поле CategoryCount. Категорії можуть зберігатися в окремому файлі або ж у файлі з повідомленнями. Для кожного джерела повідомлень визначаються свої пронумеровані події і їхні текстові описи. Описи повинні бути доступні понимнию звичайного користувача. Розглянемо формат ідентифікатора події. 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-і-+-+-+-і-і-і-і-і-і-і-і-і-і-і-+-і-і-і-і-і-і-і-і-і-і-і-і-і-і-і-+
|Sev|C|R| Facility |Code|
+-і-+-+-+-і-і-і-і-і-і-і-і-і-і-і-+-і-і-і-і-і-і-і-і-і-і-і-і-і-і-і-+
Sev
Тип події. Може приймати одне з наступних значень:
00 - Success
01 - Informational
10 - Warning
11 - Error
C
Indicates a customer code (1) or a system code (0). (Вуж утрудняюся складно перевести.) R
Резерв. Facility
Категорія повідомлення. Може бути FACILITY_NULL або одним з наступних значень:
FACILITY_ACS
FACILITY_AAF
FACILITY_CERT
FACILITY_COMPLUS
FACILITY_CONTROL
FACILITY_DISPATCH
FACILITY_INTERNET
FACILITY_ITF
FACILITY_MEDIASERVER
FACILITY_MSMQ
FACILITY_RPC
FACILITY_SCARD
FACILITY_SECURITY
FACILITY_SETUPAPI
FACILITY_SSPI
FACILITY_STORAGE
FACILITY_URT
FACILITY_WIN32
FACILITY_WINDOWS
Code
Код події.
29$. Інструментальна бібліотека.
Пояснення і рекомендації
Бібліотека Tool Help для Windows
Дана бібліотека дозволяє одержати інформацію про стан працюючих на локальному комп'ютері додатків. Загалом кажучи, бібліотека призначена для написання всякого роду отладчиков, але область її застосування насправді помітно ширше.
Знімки системи
В основі THL лежить поняття "snapshot" - миттєвого знімка стану запущених додатків. Перед викликом всіх функцій бібліотеки необхідно створити snapshot. Це здійснюється викликом функції HANDLE WINAPI CreateToolhelp32Snapshot
(DWORD dwFlags, DWORD th32ProcessID );
Параметр dwFlags указує, яка саме інформація цікавить користувача. Його значення наведені в Таблиці 1.
Параметр th32ProcessID ідентифікує процес, стан якого досліджується. Він необхідний при використанні dwFlags зі значеннями TH32CS_SNAPHEAPLIST, TH32CS_SNAPMODULE й TH32CS_SNAP-THREAD. В інших випадках цей параметр ігнорується.
Так, наприклад, для одержання списку модулів процесу dwProcessID необхідний наступний виклик:
hSnapshot = CreateToolhelp32Snapshot
( TH32CS_SNAPMODULE,dwProcessID );
Знищується об'єкт snapshot стандартним викликом CloseHandle( hSnapshot );
Всі помилки, що виникають при виконанні функцій THL, повертаються стандартним викликом GetLast-Error й FormatMesssage.
Список процесів
У прикладі використаються дві функції:
BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe )
и
BOOL WINAPI Process32Next( HANDLE
hSnapshot, LPPROCESSENTRY32 lppe ).
В обох функціях два параметри. Перший - дескриптор, що повертає попереднім викликом CreateToolhelp-32Snapshot, а другий указує на структуру PROCESSENTRY32, у якій повертається результат виконання функції. Перед викликом Process32First необхідно встановити в поле dwSize структури PROCES-SENTRY32 значення, рівне розміру самої структури - sizeof(PROCES-SENTRY32), інакше функція поверне помилку. Список модулів, завантажених процесом
У процесі роботи практично будь-якого додатка Win32 завантажуються ті або інші бібліотеки, компоненти й інші програмні модулі. Природно, виникає бажання одержати список завантажених модулів. Для цього в THL також передбачений простий механізм.
У ньому, у першу чергу, викликається CreateToolhelp32Snapshot з параметрами TH32CS_SNAPMODULE й ідентифікатором процесу, модулі якого нас цікавлять. Далі, як й у попередньому прикладі, у полі dwSize, але тепер уже структури MODULEENTRY32, установлюється значення, рівне її розміру. До речі, це необхідно робити для всіх функцій перерахування THL. Цикл перебору нічим не відрізняється від розглянутого в попередньому прикладі. Хіба що він використає структуру MODULEENTRY32, у яку повертається інформація про модуль. Опис полів цієї структури втримується в Таблиці 2.
Потоки процесу
Механізм роботи з потоками процесу ідентичний механізму, описаному для модулів, тільки викликаються функції Thread32First й Thread32Next, а замість структури MODULEENTRY32 використається THREADENTRY32.
Доступ до пам'яті процесу
Відомо, що в Win32 одержати доступ до пам'яті одного процесу з іншого неможливо. Проте в THL є функція, що дозволяє одержати копію блоку пам'яті іншого процесу:
BOOL WINAPI Toolhelp32Read
ProcessMemory
( DWORD th32ProcessID, LPCVOID
lpBaseAddress, LPVOID lpBuffer, DWORD cbRead,
LPDWORD
lpNumberOfBytesRead );
Параметри в цієї чудової функції наступні:
th32ProcessID
[in] Ідентифікатор процесу, блок пам'яті якого необхідно скопіювати. Якщо цей параметр дорівнює нулю, то копіюється пам'ять із поточного процесу. lpBaseAddress
[in] Адреса в адресному просторі зазначеного процесу, починаючи з якого копіюється інформація. Перед виконанням операційна система перевіряє, чи вся зазначена пам'ять доступна для читання. Якщо це не так, повертається FALSE. lpBuffer
[out] Покажчик на буфер, у який повинні бути скопійовані дані. cbRead [in] Розмір буфера в байтах. lpNumberOfBytesRead [out] Кількість скопійованих байтів.
А як довідатися, де в адресному просторі процесу перебувають дані? У самому загальному випадку на це питання відповісти неможливо. Але для пам'яті, що розподіляє динамічно (heap) THL надає функції, цю проблему вирішальні.
Дослідження динамічної пам'яті процесу
Для виділення області у віртуальній пам'яті використається функція HeapCreate, що створює відповідний об'єкт (heap) в адресному просторі процесу. Для виділення пам'яті усередині цього блоку використається функція HeapAlloc. Всі інші функції роботи з динамічною пам'яттю (GlobalAlloc, malloc, calloc і т.д.) зводяться до використання цього механізму, що ми не будемо тут докладно розглядати.
Очевидно, що потрібно вміти знаходити список виділених областей віртуальної пам'яті й для кожного з них - список виділених блоків. Подивимося, як це робиться за допомогою функцій THL. У термінах THL область пам'яті називається heap, безліч областей пам'яті - heap list, а виділеному усередині її блоку відповідає термін "block of a heap".
Перший цикл за допомогою функцій Heap32ListFirst й Heap32ListNext повертає список блоків пам'яті, які були розподілені. Знаючи ідентифікатор процесу й ідентифікатор області віртуальної пам'яті, що повертається в поле th32HeapID структури HEAPLIST32, можна переглянути, як розподіляється пам'ять усередині цього блоку. Виділені області пам'яті можна одержати за допомогою функцій Heap32First й Heap32-Next. Вони повертають структуру HEAPENTRY32, опис полів якої наведено в Таблиці 3.
Знаючи значення полів dwAddress й dwBlockSize, за допомогою функції Toolhelp32ReadProcessMemory неважко одержати дані, що перебувають у динамічній пам'яті процесу.
Таблиця 1. Значення параметра dwFlags-функції. CreateToolhelp32Snapshot.TH32CS_INHERITпоказує, що snapshot був успадкований.
TH32CS_SNAPHEAPLIST одержує список блоків динамічно розподіленої пам'яті в адресному просторі процесу.
TH32CS_SNAPMODULE одержує список модулів, завантажених процесом.
TH32CS_SNAPPROCESS одержує список запущених на локальному комп'ютері процесів.
TH32CS_SNAPTHREAD одержує список потоків процесу.
TH32CS_SNAPALL містить у собі попередні чотири прапори, тобто snapshot буде містити списки процесів, потоків, модулів й областей динамічної пам'яті.
Таблиця 2. Опис полів структури MODULEENTRY32.DWORD dwSize розмір структури в байтах
DWORD th32ModuleID ID модуля
DWORD th32ProcessID ID процесу модуля
DWORD GlblcntUsage загальне для системи кількість звертань до модуля
DWORD ProccntUsage кількість звертань до модуля, зроблених з поточного процесу
BYTE * modBaseAddr адреса модуля в адресному просторі процесу
DWORD modBaseSize розмір модуля
HMODULE hModule дескриптор модуля в контексті процесу
TCHAR szModule [MAX_MODULE_NAME32 + 1] ім'я модуля
TCHAR szExePath[MAX_PATH] повний шлях до файлу
Таблиця 3. Опис полів структури HEAPENTRY32.SIZE_T dwSize розмір структури HEAPENTRY32 у байтах.
HANDLE hHandle дескриптор блоку пам'яті в адресному просторі процесу.
ULONG_PTR dwAddress адреса початку блоку пам'яті.
SIZE_T dwBlockSize розмір блоку пам'яті.;
DWORD dwFlags прапори, що описують область пам'яті. Цей параметр може приймати наступні значення:
LF32_FIXED область пам'яті не може бути переміщена операційною системою;
LF32_FREE блок пам'яті не використається;
LF32_MOVEABLE область пам'яті виділена й може переміщатися.
DWORD dwLockCount кількість викликів функції GlobalLock (в Win32 LocalLock синонім функції GlobalLock), які були зроблені для цього блоку пам'яті.
DWORD dwResvd зарезервовано.
DWORD th32ProcessID ID процесу, у контексті якого виділена пам'ять.
ULONG_PTR th32HeapID ідентифікатор блоку пам'яті, виділеного функцією HeapCreate.
Документ
Категория
Рефераты
Просмотров
230
Размер файла
2 090 Кб
Теги
301, укр, тематика
1/--страниц
Пожаловаться на содержимое документа