Буфер Обміну
Введення
Одне із завдань програміста Windows-додатків полягає у створенні продуктів, які настільки подібні іншим програмам Windows, що користувач зможе легко перемикатися між ними, не відчуваючи відмінностей в їх поведінці і зовнішньому вигляді. У ідеалі користувач повинен сприймати однорідне середовище і не помічати розмежувань між програмами. У такому середовищі сама собою напрошується можливість перенесення даних між додатками.
Буфер обміну (clipboard) Windows забезпечує простий обмін даними між додатками.
Вміст буфера обміну в будь-який момент часу представляє собою набір об'єктів в пам'яті, створених програмою, яка помістила дані в буфер обміну. Функції API та повідомлення, що реалізують буфер обміну, управляють його вмістом. Буфер обміну служить єдиним протоколом доступу до збережених даних.
1. Формати буфера обміну
Додатки можуть обробляти одні й ті ж дані по-різному. Наприклад, текст може являти собою прості символи, згенеровані емулятором терміналу, або результат застосування сучасного повнофункціонального текстового процесора. Дані, що імпортуються будь-якою програмою з буфера обміну, необхідно представляти у найкращому з можливих форматів. З цією метою програми, які потрапляють дані в буфер обміну, експортують їх якомога більшою кількістю способів.
У результаті при зчитуванні з буфера обміну клієнту надається набір опцій. Різні способи представлення даних в буфері обміну називаються форматами буфера обміну.
Коли додаток використовує функцію SetClipboardData () для переміщення даних в буфер обміну в певному форматі, прийнято говорити, що додаток відтворює цей формат. Взагалі кажучи, чим більше форматів буфера обміну підтримується додатком, тим точніше воно здійснюється обмін даними з іншими Windows-додатками. Тому має сенс відтворювати як можна більше число форматів буфера обміну.
Додаток, який очищає буфер обміну через функцію EmptyClipboard (), а потім поміщає в нього дані будь-якого формату з використанням функції SetClipboardData (), називається власником буфера обміну (clipboard owner). (Термін не зовсім точний, оскільки після переміщення даних в буфер обміну вони більше не належать власнику. Ці дані належать середовищі Windows.)
Інші програми, що витягає дані, називаються клієнтами буфера обміну (clipboard readers). Вилучення даних з буфера обміну не робить клієнта його власником.
Програма Windows, єдиною метою якої є перегляд вмісту буфера обміну, називається вікном перегляду буфера обміну (clipboard viewer).
Для кожного формату буфера обміну, підтримуваного додатком, повинен виконуватися окремий виклик функції SetClipboardData (). Дані, записувані в буфер обміну в результаті кожного виклику, тільки заміщають дані, що зберігаються в буфері обміну для даного формату. З цієї причини перед приміщенням нових даних в буфер обміну, програма повинна викликати функцію EmptyClipboard (), щоб очистити всі збережені формати від невідповідних даних. В іншому випадку ці дані можу бути помилково збережені. Виклик функції EmptyClipboard () також дозволяє старому власнику буфера обміну очистити всі області пам'яті, виділені певним форматам, шляхом обробки повідомлення WM _ DESTROYCLIPBOARD.
На рис.1 показано взаємодію між власником і клієнтом буфера обміну. Зверніть увагу, що клієнт вибрав перегляд даних у форматі CF _ TEXT, хоча власник відтворює не менше трьох текстових форматів, включаючи GF _ TEXT, CF _ OEMTEXT і CF _ UNICODETEXT.
Клієнт буфера обміну може підтримувати й інші формати, але для вставки даних йому найбільше підходить формат CF _ TEXT.
У табл.1 перераховані деякі зумовлені формати і описані дані, а також тип дескриптора, переданого функції SetClipBoardData () для кожного типу. Найбільш поширеним типом записів буфера обміну є глобальний блок пам'яті, виділений з опцією GMEM _ DDESHARE. Для нього в таблиці застосовується просте позначення HANDLE. Постійні значення форматів буфера обміну описані у файлі заголовків winuser. H разом з прототипами функцій API, які визначають інтерфейс буфера обміну.
Таблиця 1
Попереднє визначення формати буфера обміну
Формат буфера обміну | Тип дескриптора | Опис даних |
CF_BITMAP | HBITMAP | Дані являють собою набір бітів. |
CF_DSPENHMETAFILE | HENHMETAFILE | Розширений метафайл, приватний для програми. |
CF_DSPMETAFILEPICT | HANDLE | Об'єкт пам'яті, що містить структуру METAFILEPICT, яка є приватною для програми. |
CF_DSPTEXT | HANDLE | Приватний для програми текст. |
CF _ DSPBITMAP | НВ I ТМАР | Растрове зображення, яке є приватним для програми. Цей формат може використовуватися, наприклад, для передачі даних між різними екземплярами однієї й тієї ж додатка. Для визначення власника буфера обміну слід використовувати функцію GetClipboardOwner (). |
CF_ENHMETAFILE | HENHMETAFILE | Розширений метафайл. |
CF _ GDIOBJFIRST | HGDIOBJ | Описані додатком формати наскрізного буфера обміну (throughclipboard), представлені об'єктами GDI (Graphic Device Interface - інтерфейсом графічних пристроїв). При виконанні функції EmptyClipboard () для знищення даних цього формату використовується функція DeleteObject (). |
CF_METFILEPICT | HANDLE | Об'єкт пам'яті, що містить структуру METAFILEPICT. |
CF_OEMTEXT | HANDLE | Об'єкт пам'яті, який містить завершальну нулем рядок символів набору OEM. Рядки розділяються послідовністю символів повернення каретки та переведення рядка (CR / LF). |
CF_OWNERDISPLAY | NULL | Вказує, що власник буфера обміну буде відповідати за відображення даних, а також оновлює вікна перегляду буфер обміну. Вікно перегляду буфера обміну відправляє власникові повідомлення WM _ ASKCBFORMATNAME, WM _ PAINTCLIPBOARD, WM _ HSCROLLCLIPBOARD, WM _ SIZECLIPBOARD і WM _ VSCROLLCLIPBOARD. |
від CF_PRIVATEFIRST до CF_PRIVATELAST | Цей діапазон позначає приватні формати буфера обміну. Windows не управляє цими форматами. Власник буфера обміну повинен управляти ресурсів »через повідомлення WM _ DESTROYCLBOARD. | |
CF_RBIFF | HANDLE | Складна підтримка звукових даних. Перевершує за складністю підтримку CF _ WAVE. |
CF_TEXT | HANDLE |
Об'єкт пам'яті, що містить рядок символів, завершальну нулем. Рядки розмежовуються послідовністю символів повернення каретки та переведення рядка (CR / LF). | ||
CF_TIFF | HANDLE | Формат дескриптора файлу зображення. |
CF_UNCODETEXT | HANDLE | Об'єкт пам'яті, що містить завершальну нулем рядок у форматі багатобайтні глобального коду символів Unicode. |
CF_WAVE | HANDLE | Стандартна підтримка Wave-файлів. |
2. Управління даними буфера обміну в середовищі Windows
Система Windows управляє вмістом і форматами буфера обміну. Windows зберігає дані для кожного формату окремо таким чином, що приміщення даних в один формат не зачіпає дані, записані в іншому форматі. Крім того Windows управляє видаленням даних, поміщених в буфер обміну. Дані, що зберігаються в буфері обміну для даного формату, повинні вилучатися при збереженні в форматі нових даних, а також при виконанні функції EmptyClipboard () для очищення всього вмісту буфера обміну.
Windows видаляє елементи даних буфера обміну шляхом виклику спеціальної функції видалення. Вибір функції залежить від типу даних, що зберігаються у форматі буфера обміну.
Наприклад, для об'єктів GDI Windows використовує функцію DeleteObject (), для об'єктів пам'яті застосовується функція GlobalFree () і т.д.
Винятком з цих правил служать приватні формати буфера обміну. Як буде показано, Windows не управляє даними, збереженими в згаданих форматах буфера обміну. Фактично, управляти. Збереженими в приватних форматах даними повинні програми, які ці формати створюють.
3. Відтворення даних буфера обміну з затримкою
Обробка великої кількості форматів даних буфера обміну пов'язана з витратами часу, особливо, якщо програма підтримує графічні формати, такі як bitmap-зображення або метафайли. Управління GDI-об'єктами вимагає великих витрат часу і пам'яті. Не має сенсу обробляти або зберігати дані, якщо їх формат взагалі не використовується.
На щастя, API-інтерфейси Win32 надають простий метод затримки приміщення даних в буфер обміну, поки не буде згенерований запит на вилучення даних у певному форматі. Це називається відтворенням із затримкою (delayed rendering). Для його задіяння достатньо передати значення NULL як дескриптора типу HANDLE на дані буфера обміну при установці даних за допомогою функції SetClipboardData (). Якщо додаток вимагає відтворення формату, відправляється повідомлення WM _ RENDERFORMAT зі змінною wParam, щоб вказати запитуваний формат.
Відтворення з затримкою економить час і ресурси. Єдиний недолік подібного підходу полягає в тому, що система може не відповідати негайно після запиту користувача на вставку, оскільки деякий час витрачається на форматування даних.
Тим не менш, зазвичай відтворення з затримкою вважається найкращим підходом у разі підтримки великої кількості форматів, або та відсутності часу на приміщення даних в буфер обміну. Пам'ятайте, часто буває, що дані вирізаються або копіюються, але не вставляються.
Прикладом відтворення з затримкою служать функції ClipboardFormatAvailable () і GetPriorityClipboardFormat ().
Формат CF _ OWNERDISPLAY
Унікальний формат буфера обміну CF _ OWNERDISPLAY покладає функції відображення даних буфера обміну на його власника. Власник буфера обміну приймає набір повідомлень, що описують зміни клієнтської області у вікні перегляду буфера обміну. Ці повідомлення зведені в табл.2.
Одним з наочних прикладів формату CF _ OWNERDISPLAY служить утиліта Clipbook - вікно перегляду буфера обміну Windows, яке розпізнає, здавалося б необмежену кількість форматів. Насправді Clipbook надає свою клієнтську область власникові даних буфера обміну, який, звичайно ж, здатний відображати вміст так само, як і в початковому документі. Єдина відмінність для власника буфера обміну складає вікно, де повинні виводитися дані. Однак це не вносить змін в логіку програми.
Таблиця 2
Повідомлення, що приймаються власником буфера обміну для відображення даних
Повідомлення | Значення |
WM _ ASKCBFORMATNAME | Відправляється, коли вікно перегляду буфера обміну запитує ім'я формату. Власник буфера обміну повинен скопіювати байти wParam в буфер, на який указує параметр lPrarm. |
WM _ PAINTCLIPBOARD | Відправляється, коли клієнтська область вікна буфера обміну вимагає оновлення. Параметр wParam є дескриптором вікна перегляду буфера обміну. Параметр lParam є покажчиком на PAINTSTRUCT. |
WM _ SIZECLIPBOARD | Відправляється при зміні розміру клієнтської області вікна перегляду буфера обміну. Параметр wParam є дескриптором вікна перегляду буфера обміну. Параметр lParam - Покажчик на структуру RECT. |
WM_HSCROLLCLIPBOARD і WM_VSCROLLCLIPBOARD | Відправляється при прокрутці клієнтської області вікна перегляду буфера обміну. Параметр wParam є дескриптором вікна перегляду буфера обміну. Молодше слово параметра lParam позначає тип запиту смуги прокручування (подібно параметру wParam в повідомленні WM _ HSCROLL або WM _ VSCROLL). Старше слово параметра lParam вказує позицію бігунка тоді і тільки тоді, коли смуга прокрутки запитує SB _ THUMBPOSITION. |
Формат буфера обміну CF _ OWNERDISPLAY забезпечує найвищу ступінь різноманітності всіх форматів оскільки за їх відображення відповідає джерело даних. Якщо програма підтримує незвичайний формат буфера обміну (наприклад, приватний або зареєстрований), існує тільки дві можливості відображення даних поза програми:
- Відображення власником;
- Написання вікна перегляду буфера обміну.
4. Вікна перегляду буфера обміну
Вікно перегляду буфера обміну представляє собою програму, призначену для перегляду вмісту буфера обміну. Зазвичай вікна перегляду підтримують безліч форматів, але не можуть правильно інтерпретувати приватні зареєстровані формати. У зв'язку з цим може знадобитися створити вікно перегляду буфера обміну самостійно.
Слід враховувати, що вікно перегляду буфера обміну не володіє даними, якими управляє. Вимоги до вікна перегляду:
Вікно перегляду ні в якому разі не повинно виконувати запис у вміст буфера обміну.
Вікно перегляду ніколи не повинна залишати будь-який елемент вмісту буфера обміну заблокованим.
Оскільки в будь-який момент часу можуть виконуватися кілька вікон перегляду та повідомлення передаються між ними за допомогою пов'язаного списку вікон перегляду, створювана програма повинна відповідати наступним угодам:
Зберігати значення повернення функції SetClipboardViewer (), яке вказує на наступне вікно перегляду буфера обміну в ланцюжку. Windows поміщає нові вікно перегляду перед уже встановленими.
Передавати повідомлення WM _ DRAWCLIPBOARD наступного вікна перегляду в ланцюжку. В іншому випадку інші вікна перегляду не будуть оновлюватися.
Передавати повідомлення WM _ CHANGECBCHAIN наступного вікна перегляду в ланцюжку. В іншому випадку ланцюжок вікон перегляду буде перервана.
Відслідковувати повідомлення WM _ CHANGECBCHAIN для видалення наступного вікна ланцюжка. Параметр IParam містить новий дескриптор наступного вікна у разі видалення наступного, вікна перегляду.
Для видалення вікна перегляду з ланцюжка слід викликати функцію ChangeClipboardChain () з використанням збереженого значення наступного вікна.