Обмін даними в Windows

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

Буфер обміну (CLIPBOARD)

У Windows передбачений спеціальний механізм обміну даними між різними додатками, званий буфер обміну (clipboard). Буфер обміну є буфер, в який можуть бути поміщені дані жодна програма. Всі інші додатки Windows можуть прочитати ці дані або розмістити в цьому буфері свої.

Для того, що б не виникало плутанини при використанні буфера обміну, Windows передбачає застосування певних форматів даних, що розміщуються в буфері.

У деяких випадках буває зручно переглянути дані, розміщені в буфері обміну - для цих цілей Windows містить спеціальну програму перегляду вмісту буфера обміну, (Clipboard Viewer). Не треба змішувати між собою сам буфер обміну і програму його перегляду. Буфер обміну реалізований декількома функціями Windows і спеціальними даними.

При розгляді буфера обміну нам треба буде розглянути три питання:

  1. як можна самим класти або читати дані з буфера обміну

  2. як можна використовувати буфер обміну зі стандартним вікном-редактором

  3. як написати власну програму перегляду вмісту буфера обміну.

Попередньо ми розберемося з деякими основними поняттями, пов'язаними із застосуванням буфера обміну.

Як ми вже сказали, Windows передбачає використання певних форматів даних для передачі через буфер обміну. Звичайно, у Вас є можливість передавати дані у власному форматі, тільки для використання власним додатком, проте рекомендується дотримуватися загальноприйнятих стандартів, тому що можливість передачі даних між самими різноманітними додатками є дуже зручною.

Кожному застосовуваному формату даних буфера обміну в Windows поставлений у відповідність певний номер. Windows визначає кілька стандартних форматів і надає для них певні символічні імена:

CF_TEXT відповідає ASCIIZ тексту

CF_BITMAP звичайний бітмапами

CF_DIB бітмапами, незалежний від пристрою

CF_PALETTE палітра (зазвичай застосовується разом з CF_DIB)

CF_METAFILEPICT метафайл

При відображенні даних цих форматів в програмі перегляду буфера обміну не виникає жодних проблем, тому що Windows містить всі необхідні засоби для відображення цих даних. Однак Ви можете класти в буфер обміну дані у власному форматі. Якщо Ви хочете, що б їх відображала стандартна програма перегляду, то Ви повинні їх оголосити як

CF_OWNERDISPLAY дані, що відображаються користувачем

У цьому випадку програма перегляду буде посилати спеціальні повідомлення Вашому вікну для відображення цих даних у вікні.

Кілька додаткових форматів, будучи звичайними форматами даних, мають відмінні від них номери. У символічних іменах таких даних є абревіатура 'DSP'

CF_DSPTEXT відповідає ASCIIZ тексту

CF_DSPBITMAP звичайний бітмапами

CF_DSPMETAFILEPICT метафайл

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

Крім розглянутих, Windows додатково визначає велику кількість інших стандартних форматів даних, однак вони використовуються порівняно рідко. У більшості випадків це специфічні формати даних різних популярних програм, які було вирішено включити в стандарт Windows.

При необхідності використання власного формату даних для буфера обміну треба отримати номер цього формату. Для того, що б уникнути можливих накладок, Ви повинні зареєструвати Ваш формат даних за допомогою функції

UINT RegisterClipboardFormat (lpszFormatName);

для вже зареєстрованого формату Ви можете дізнатися його ім'я:

int GetClipboardFormatName (nFormat, lpsBuffer, nMaxCount);

Буфер обміну містить не більше одного блоку даних кожного формату, причому всі дані, що знаходяться в буфері повинні бути покладені одним вікном. Так як дані, що передаються в буфер обміну, повинні бути доступні всім додаткам, то для їх передачі використовуються тільки блоки глобальної пам'яті.

Запис і читання даних з буфера обміну

Загальні правила роботи з буфером обміну зводяться до наступного:

1) Вся робота з буфером обміну повинна проводитися за час обробки одного повідомлення. Під час роботи з буфером Ви не повинні викликати жодних функцій, які можуть передати управління іншому додатку. Тобто Ви не повинні використовувати функцій типу: DialogBox, MessageBox, GetMessage, PeekMessage.

Дані повинні розміщуватися лише у переміщуваний блоці глобальної пам'яті.

Коли буфер обміну отримує дані, він оголошує себе власником цих даних, так що додаток більше не повинно використовувати передані дані. Більш того, ці дані не можна видаляти при завершенні роботи Вашого застосування - коли буде треба, буфер обміну сам видалить їх.

Коли Ви читаєте дані з буфера обміну, то Ви отримуєте хендл блоку даних. Так як ці дані закріплені за буфером, то Ви не повинні з ними працювати, Вам необхідно скопіювати їх до себе.

При обміні даними з буфером обміну не можна передавати йому замкнені блоки даних, так само як не можна залишати їх замкнутими після читання.

2) Перед початком обміну даними з буфером обміну Ви повинні його відкрити. Робиться це за допомогою функції

BOOL OpenClipboard (hWnd);

якщо Ви покладете будь-які дані в буфер, то вікно, вказане Вами, буде вважатися власником всіх даних буфера обміну.

3) Потім Ви можете здійснити необхідні операції обміну даними. Якщо Ви збираєтеся покласти дані в буфер обміну, то Ви повинні заздалегідь видалити всі вже знаходяться в ньому дані:

BOOL EmptyClipboard (void);

і тільки потім покласти потрібні дані, скориставшись функцією:

HGLOBAL SetClipboardData (nFormat, hGlobal);

параметр nFormat задає ім'я формату даних, а hGlobal є хендлов глобального блоку даних. Функція повертає Вам новий хендл цього блоку даних, за допомогою якого Ви можете звертатися до цих даних до закриття буфера обміну.

Ви можете покласти в буфер обміну кілька блоків даних різного формату одночасно. Так як покладені в буфер дані зберігаються там або до його очищення, або до завершення роботи Windows, то передавати великі блоки може бути занадто накладно.

Для цього випадку в Windows передбачений механізм передачі даних із затримкою. Викликаючи функцію SetClipboardData Ви вказуєте замість хендла блоку даних NULL. Це означає, що дані для буфер обміну у Вас є, але передавати Ви їх будете тільки на вимогу. Для такої передачі Вам треба буде обробляти три повідомлення:

WM_RENDERFORMAT nFormat 0L

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

SetClipboardData (nFormat, hGlobal);

передавши їй хендл реального блоку даних.

WM_RENDERALLFORMATS, 0, 0L

повідомлення надсилається Вашому вікну коли воно знищується, а буфер обміну містить затримані дані Вашого вікна. Ви повинні звичайним чином (тобто відкрити-очистити-покласти-закрити) передати всі дані в буфер обміну.

WM_DESTROYCLIPBOARD, 0, 0L

повідомлення інформує Вас про те, що викликана функція EmptyClipboard, коли буфер обміну містить затримані дані Вашого вікна. Ваші дані більше не знадобляться, тому Ви можете звільнити використовувані структури даних.

Якщо Ви збираєтеся тільки читати дані з буфера обміну, то очищати його не треба, а отримати дані потрібного формату можна за допомогою функції

HGLOBAL GetClipboardData (nFormat);

Функція повертає хендл глобального блоку пам'яті, який Ви повинні скопіювати до себе.

4) Після завершення обміну з буфером обміну Ви повинні закрити його за допомогою функції

BOOL CloseClipboard (void);

На цьому закінчуються операції обміну даними з буфером обміну.

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

BOOL IsClipboardFormatAvailable (nFormat);

Ця функція повідомляє, присутні-ли дані потрібного формату в буфері обміну. Перевірку на наявність тих чи інших форматів даних можна виконати і іншим способом, за допомогою функції

UINT EnumClipboardFormats (nFormat);

Ця функція перебирає присутні формати даних в буфері обміну і повертає номер наступного у списку або 0. Приклад застосування:

UINT nFormat = 0;

while ((nFormat = EnumClipboardFormats (nFormat))! = 0) {/ / перебір форматів}

Можна дізнатися кількість присутніх в буфері обміну форматів даних за допомогою функції

UINT CountClipboardFormats (void);

5) Зараз ми розглянемо правила застосування форматів даних CF_DSP ... Ці дані призначені для використання тільки Вашим додатками.

Основна ідея полягає в тому, що додатки, які читають, скажімо, формат CF_TEXT, його й будуть запитувати у буфера обміну. При цьому, навіть якщо буфер обміну містить дані у форматі CF_DSPTEXT, він їх не передасть - номери форматів різні, тому вважається, що Ви можете передавати дані в форматі CF_DSP ... тільки для свого застосування, "заховавши" його від інших.

Проте може статися так, що одночасно два різних програми спробують використовувати приватні дані в такому форматі. Тому виникає необхідність навчитися розрізняти дані, покладені Вашим додатком, від даних, покладених чужим додатком. Для цього Ви можете скористатися функцією

HWND GetClipboardOwner (void);

яка поверне хендл вікна, поклав дані. Якщо вам важко з хендла визначити приналежність вікна до потрібного додатком, то Ви майже напевно знаєте, до якого класу вона повинна належати (так як і дані, і додаток, і клас вікон розроблені Вами). Тому Ви можете дізнатися ще й ім'я класу вікна:

int GetClassName (hWnd, lpsBuffer, nMaxCount);

Буфер обміну і стандартне вікно-редактор

Часто доводиться використовувати буфер обміну спільно зі звичайним вікном редактора. Великих труднощів тут немає, так як таке вікно автоматично підтримує операції обміну даними з буфером у форматі CF_TEXT. Єдине, що нам треба зробити - навчитися передавати редактору команди для здійснення цього обміну.

Це зазвичай доводиться робити для того, щоб додати меню, що містить пункти Cut-Copy-Paste. Тому що сам редактор, будучи дочірнім вікном, не має меню, то меню додається до нашого батьківському вікну. Тобто команди, отримані від меню, треба якось передати у вікно редактора.

При цьому заодно доводиться вирішувати / забороняти окремі пункти меню в залежності від наявності виділеного тексту у редакторі (Cut, Copy) і наявності потрібних даних в буфері обміну (Paste).

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

WM_PASTE 0 0L

вставити текст з Clipboard в поточну позицію каретки

WM_COPY 0 0L

скопіювати виділений текст з редактора в Clipboard

WM_CUT 0 0L

скопіювати виділений текст і видалити його з редактора

Ми повинні просто послати потрібне повідомлення редактору і він виконає все інше. Зверніть увагу на те, що ці повідомлення не є специфічними для редактора - вони мають префікс WM_ - тобто це повідомлення для звичайного вікна.

Якщо Ваше вікно буде підтримувати роботу з буфером обміну, то настійно рекомендується підтримувати ці повідомлення.

Для того, що б дозволяти або забороняти потрібні пункти меню можна скористатися функцією IsClipboardFormatAvailable для дозволу / заборони операції Paste і передачею повідомлення EM_GETSEL для з'ясування можливості операцій Cut і Copy.

Перегляд даних, що знаходяться в буфері обміну

Якщо Ви вводите нові формати даних в буфер обміну, то вам може знадобитися нова програма перегляду даних буфера обміну, яка дозволить переглядати Ваші дані. У Windows прийнято, що програма перегляду повинна оновлювати відображувану інформацію при зміні даних в буфері обміну. Для цього програма перегляду отримує повідомлення

WM_DRAWCLIPBOARD 0 0L

Однак одночасно може працювати кілька програм (вікон) перегляду. При цьому виникає необхідність десь утримувати список всіх таких програм (вікон) і посилати їм усім відповідні повідомлення. Для цього організується ланцюжок програм перегляду, яку вони самі підтримують в коректному стані.

У якійсь мірі це схоже на обробку переривань DOS - система утримує хендл тільки першого вікна перегляду, той - хендл наступного і так далі.

Коли Ви запускаєте свою програму перегляду, вона реєструється в якості програми перегляду буфера обміну за допомогою функції

HWND SetClipboardViewer (hWnd);

при цьому він стає на перше місце в ланцюжку, а функція SetClipboardViewer повертає хендл наступного за ним (або 0, якщо інших програм (вікон) перегляду немає). Цей хендл повинен бути збережений.

Далі, при оновленні даних в буфері обміну, Ваше вікно отримує повідомлення WM_DRAWCLIPBOARD. Звичайна обробка цього повідомлення:

case WM_DRAWCLIPBOARD: if (hWndNextViewer) PostMessage (hWndNextViewer, wMsg, wParam, lParam); InvalidateRect (hWnd, NULL, TRUE); return 0;

У результаті обробки цього повідомлення вікно програми перегляду перемальовувався, і таке ж повідомлення отримує інша програма.

Під час нормальної роботи будь-яка з програм перегляду закінчує функціонування перш інших. При цьому ланцюжок програм перегляду змінюється. Для того, щоб коректно змінити ланцюжок, застосовується функція

BOOL ChangeClipboardChain (hWndViewer, hWndNextViewer);

Що значить: замість вікна hWndViewer буде використовуватися вікно hWndNextViewer. Ця функція зазвичай викликається при обробці повідомлення WM_DESTROY. В результаті виконання цієї функції перша програма перегляду в ланцюжку отримає повідомлення (WM_CHANGECBCHAIN, hWndRemoved, hWndNext). Це повідомлення обробляється так:

case WM_CHANGECBCHAIN: if (wParam == hWndNextViewer) {hWndNextViewer = LOWORD (lParam);} else if (hWndNextViewer) {PostMessage (hWndNextViewer, wMsg, wParam, lParam);} return 0;

У результаті такої обробки, якщо наше вікно коштує в ланцюжку до видаляється, але не прямо перед ним, воно передасть повідомлення далі, якщо воно стоїть прямо перед видаляється, то воно виправить свій хендл наступного вікна перегляду і не пустить повідомлення далі, оскільки ланцюжок вже виправлена.

Відображення даних програмою перегляду відбувається також, як і їх звичайне читання (відкрив-прочитав-закрив).

Динамічний обмін даними (DDE)

Поки що ми розглянули тільки один спосіб обмінюватися даними між різними додатками - буфер обміну. Однак цей спосіб не завжди ефективний. У більшості випадків він обмежений застосуванням у редакторах.

Однак необхідність обміну даними між різними додатками реально існує, тому Windows містить ще один засіб для такого обміну - DDE (Dynamic Data Exchange).

DDE заснований на використанні повідомлень для передачі даних між двома додатками. При цьому один з додатків (зазвичай містить дані) називається сервером (server), а інше (зазвичай вимагає даних) - клієнтом (client). У більш загальному вигляді: клієнт виступає у ролі активного застосування, що вимагає, що б його запити були обслужені сервером. Сам процес обміну даними засобами DDE між клієнтом і сервером називається DDE-розмовою (DDE-conversation).

Зазвичай протокол обміну даними між клієнтом і сервером виглядає приблизно таким чином:

  • клієнт передає всім доступним додаткам повідомлення про те, що йому треба.

  • якщо в системі знаходиться відповідний сервер, він відповідає клієнтові щодо можливості обслуговування.

  • клієнт посилає запит (и) серверу для виконання вимог.

  • в деяких випадках сервер може інформувати клієнта про зміну або наявності тих чи інших даних. Для цього клієнт повинен підписатися (advise) на необхідні дані.

  • обмін між клієнтом і сервером може тривати до тих пір, поки один з них не зажадає припинення.

У процесі DDE-розмови відбувається обмін повідомленнями між двома додатками. Зрозуміло, що адресатами таких повідомлень є не самі додатки, а їхні вікна. У більшості випадків для нормального ведення DDE-розмови створюються спеціальні приховані (невидимі) вікна, які і здійснюють обмін повідомленнями між собою. Звичайно, в DDE можуть брати участь вікна однієї програми, не тільки різних, але треба врахувати, що DDE для обміну даними в межах однієї програми є неефективним. Так як процеси підготовки і прийому даних можуть займати значний час, то потрібно, щоб для обміну повідомленнями використовувалася функція PostMessage, а не SendMessage (крім встановлення DDE-розмови). Це, правда, створює додаткову складність - необхідність синхронізації дій різних вікон між собою.

У Windows входить все необхідне для організації DDE, причому в двох примірниках. Крім старого способу, який існує з перших версій Windows, у Windows версії 3.1 була додана спеціальна бібліотека DDEML (DDE Management Library), що є "надбудовою" над старим способом і призначена для формалізації протоколів обміну. Істотна різниця між цими способами пов'язана з тим, що для організації DDE старим способом ви повинні передбачити обробку спеціальних повідомлень необхідними Вам вікнами, а бібліотека DDEML сама створює необхідні приховані вікна і організовує обмін повідомленнями; при цьому для взаємодії з Вашим додатком DDEML буде викликати спеціально розроблену Вами CALLBACK процедуру (не являющую віконної процедурою).

Вважається, що при використанні DDEML дещо спрощується написання додатків, так як вона бере на себе питання синхронізації обміну. Однак на практиці не було помічено реального спрощення роботи у зв'язку із застосуванням бібліотеки. Більш того, вихідний текст може виявитися навіть більше, ніж при використанні старого методу.

Тому ми будемо розглядати тільки старий спосіб організації DDE. Попередньо ми введемо кілька термінів, які використовуються в DDE. Коли клієнт починає DDE або вимагає дані, він посилає серверу специфікацію того, що він вимагає.

Ця специфікація складається з 3 пунктів:

  • назва програми - application (у DDEML називається сервіс (service)); так як в більшості випадків у документації застосовується термін service, то його ми і будемо використовувати далі. Хоча, треба відзначити, цей параметр звичайно задає умовне ім'я програми-сервера.

  • тему DDE-розмови - topic

  • необхідні дані - item

Ім'я сервісу і тема DDE-розмови іспользуютсяпрі встановленні зв'язку. У результаті цього в системі має бути встановлений обмін даними між однією або декількома парами вікон, які підтримують дані сервіс та тему. Одне з вікон в кожній парі буде клієнтом, а інше - сервером. У процесі подальшого DDE-розмови може відбуватися обмін даними, ім'я яких задається окремо і є можливим ім'ям даних для даного сервісу і теми.

Ці три імені представлені у вигляді атомів (ATOM), тому нам треба розібратися з атомами і правилами їх застосування перед продовженням розмови про DDE.

Атоми

Атомами в Windows називаються нумеровані рядки тексту, що зберігаються в спеціальній таблиці. Максимальний розмір рядка 255 символів. При приміщенні рядки в таблицю їй присвоюється унікальний номер, який власне і використовується замість самої рядка. Часто атомом називають саме ці номери, а рядки - іменами атомів. Обидві платформи (Windows 3.x і Win32) використовують для завдання атомів 16-ти розрядні числа, що називаються ATOM, що дозволяє в одному подвійному слові передавати до двох атомів.

Якщо додавати дві однакові рядки в таблицю атомів, то вони отримають однаковий номер. На цьому заснований один зі способів порівняння рядків - додати обидва рядки в таблицю і порівняти атоми (не забудьте після цього видалити додану атом, навіть якщо це було треба тільки для перевірки).

Для кожної програми доступні дві таблиці атомів - локальна та глобальна. Так як DDE підтримується між різними додатками то, очевидно, повинна застосовуватися тільки глобальна таблиця атомів.

Для роботи з глобальними атомами призначені наступні функції:

ATOM GlobalAddAtom (lpszName); UINT GlobalGetAtomName (aAtom, lpsBuffer, nMaxCount); ATOM GlobalFindAtom (lpszName); ATOM GlobalDeleteAtom (aAtom);

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

Для роботи з локальними атомами використовуються аналогічні функції, але не мають на початку слова "Global". Крім цього для роботи з локальною таблицею атомів додана ще одна функція:

ATOM AddAtom (lpszName); UINT GetAtomName (aAtom, lpsBuffer, nMaxCount); ATOM FindAtom (lpszName); ATOM DeleteAtom (aAtom);

BOOL InitAtomTable (UINT cTableEntries);

Яка дозволяє вказати максимальну кількість рядків, які розміщені в локальну таблицю атомів. За умовчанням це число дорівнює 37 (розмір глобальної таблиці атомів змінити не можна). При роботі з локальною таблицею атомів видаляти атоми в кінці роботи програми необов'язково - по завершенні роботи всього програми таблиця буде автоматично видалена.

При обміні даними за допомогою DDE відбувається послідовна посилка повідомлень від одного додатка до іншого і навпаки. Одна DDE транзакція зазвичай складається з двох - трьох послідовних повідомлень. У цих повідомленнях часто передаються необхідні імена, представлені у вигляді глобальних атомів. При цьому виникає необхідність так організувати обмін повідомленнями, що б забезпечити видалення всіх знову додаються атомів. Для цього прийнято таке рішення: програма, що посилає атоми разом з повідомлення має ці атоми створити, а потім перевірити код завершення функції PostMessage - якщо посилка пройшла успішно, то атом повинен бути звільнений приймаючим додатком, а якщо при посилці виникла помилка, то атом повинне видалити посилющее додаток. У деяких випадках атом, отриманий додатком з повідомленням, може бути посланий разом з відповідним повідомленням тому (окрім сполучення WM_DDE_INITIATE, про що див. нижче). У цьому випадку не обов'язково атом видаляти а потім створювати знову, можна використовувати отриманий; однак необхідно переконатися, що він був посланий з відповідним повідомленням і, якщо посилка не відбулася, його треба видалити.

Особливості завдання параметра lParam повідомлень DDE

Слід врахувати деякі відмінності в застосуванні параметра lParam повідомлень DDE на 16-ти і 32-х розрядних платформах (Windows 3.x і Win32). Справа в тому, що спочатку цей параметр використовувався для передачі двох значень, часто атома імені даних і хендла глобального блоку. У разі 32-х розрядної платформи хендл блоку сам займає подвійне слово та розміститися в lParam спільно з атомом імені даних не може.

Для вирішення цієї проблеми були введені спеціальні функції, здійснюють "упаковку" двох вказаних значень (званих молодшим і старшим) в одне подвійне слово. Фактично можна вважати, що виділяється спеціальна структура, що містить пару полів, ідентифікатор якої використовується як lParam.

Так як різні повідомлення DDE використовують lParam різним чином, то і упаковка даних здійснюється не для кожного повідомлення, так що для кожного повідомлення треба перевіряти необхідність використання цього механізму, а для повідомлення WM_DDE_ACK треба ще перевірити, у відповідь на яке повідомлення він посланий. Для роботи з "упакованими" даними необхідно застосовувати спеціальні функції:

LONG PackDDElParam (UINT uMsg, UINT uLow, UINT uHigh); LONG UnpackDDElParam (UINT uMsg, LONG lParam, PUINT puLow, PUINT puHigh); LONG FreeDDElParam (UINT uMsg, LONG lParam); LONG ReuseDDElParam (LONG lParam, UINT uMsgIn, UINT uMsgOut , UINT uLow, UINT uHigh);

Функція PackDDElParam упаковує зазначені дані в проміжну структуру, хендл якої повертається у вигляді LONG; повертається значення може бути використано тільки для передачі даних за допомогою функції PostMessage і тільки для деяких повідомлень DDE. Функція UnpackDDElParam виконує зворотну операцію, але проміжна структура даних при цьому зберігається. Для того, що б її видалити необхідно використовувати FreeDDElParam. Остання функція ReuseDDElParam може застосовуватися для використання однієї структури при повторній передачі даних або відповіді на отримане повідомлення. Як параметр lParam функції PostMessage необхідно вказувати повертається, а не первісне значення.

В описі параметрів повідомлень ми зберігаємо колишній синтаксис, причому lParam може як і раніше задаватися у вигляді двох компонент старший & молодший. Єдина відмінність, що під старшим і молодшим компонентом не треба мати на увазі слова, а для отримання цих компонентом може знадобитися функція UnpackDDElParam, про що в описі повідомлення буде зроблено спеціальну застереження. У рідкісних випадках можуть даватися два описи параметрів, для Windows 3.x і для Win32. Для відмінності цих варіантів в описі параметрів повідомлення можуть бути додані спеціальні пояснення

(Packed Win32) - якщо параметр упаковується в разі платформи Win32

(Windows 3.x) - якщо опис застосовується тільки у випадку Windows 3.x

(Win32) - якщо опис застосовується тільки у разі Win32

DDE, початок обміну і його завершення

Коли ми говорили про загальному вигляді протоколу DDE, ми відзначили, що він заснований на обміні повідомленнями. У більшості випадків повідомлення надсилаються (post) а не передаються (send). При цьому додаток, що посилає повідомлення не має можливості дочекатися обробки цього повідомлення. Тому ті дані, які надсилаються одним додатком, повинні бути знищені іншим, що б вони не залишалися в пам'яті. Єдине виключення з цього правила - повідомлення WM_DDE_INITIATE і відповідь на нього (WM_DDE_ACK) які завжди передаються, а не посилаються.

При початку DDE-розмови надсилається повідомлення

WM_DDE_INITIATE hWnd aTopic & aService

це повідомлення використовується для початку DDE-розмови. Параметр wParam містить хендл послав вікна, а lParam в молодшому слові - атом aService, а в старшому - атом aTopic, що задають, відповідно, сервіс і тему DDE-розмови. Нульові значення атомів вказують відповідно на будь-яку підтримувану тему і будь-який сервіс. Передача: Коли клієнт викликає функцію SendMessage для встановлення DDE-розмови, він створює необхідні атоми, а після повернення з функції SendMessage він зобов'язаний ці атоми видалити. Отримання: сервер, що отримав це повідомлення , і підтримує зазначені сервіс і тему відповідає повідомленням WM_DDE_ACK. При цьому він зобов'язаний створити заново необхідні атоми - використовувати атоми, отримані з повідомленням заборонено. Значення атомів aTopic і aService рівні 0 вказують, відповідно, на яку підтримувану сервером тему і сервіс. У цьому випадку сервер повинен відповісти стільки разів, скільки відповідних тем і сервісів він підтримує.

Це повідомлення передається всіх програм (як хендла вікна-одержувача повідомлення використовується HWND_BROADCAST або -1). Той сервер, який підтримує дану тему і сервіс повинен відповісти на цей запит передачею повідомлення

WM_DDE_ACK hWnd aTopic & aService

повідомлення, яке підтверджує прийняте перш повідомлення. Параметр wParam містить хендл послав вікна, а lParam використовується різними способами, в залежності від того, яке повідомлення викликало це підтвердження. У залежності від значень параметрів повідомлення розглядають позитивні та негативні підтвердження. При відповіді на WM_DDE_INITIATE молодше слово lParam містить атом сервісу, а старше - атом теми, при цьому WM_DDE_ACK не надсилається, а передається за допомогою функції SendMessage, причому воно розглядається лише як позитивне, так як в якості негативної відповіді використовується відсутність цього повідомлення. Якщо WM_DDE_ACK отримано у відповідь на WM_DDE_INITIATE, то для обох платформ - Windows 3.x і Win32 воно використовується однаково, а якщо воно отримано у відповідь на будь-яке інше повідомлення, то параметр lParam буде використаний для передачі "упакованого" значення. Отримання: додаток-клієнт, отримала повідомлення WM_DDE_ACK зобов'язана видалити всі супроводжуючі його атоми. Передача: при відповіді на WM_DDE_INITIATE заборонено використовувати отримані атоми, сервер зобов'язаний створити необхідні для відповіді атоми і послати їх (так як на одне WM_DDE_INITIATE може відповісти кілька серверів, а при обробці WM_DDE_ACK клієнт зобов'язаний видаляти всі атоми).

Якщо WM_DDE_ACK передається у відповідь на повідомлення WM_DDE_INITIATE, то в lParam містяться такі-ж дані, що й у повідомлення WM_DDE_INITIATE. Це вважається позитивним підтвердженням. Якщо відповідь негативна, то підтвердження при ініціалізації просто не видається.

У більшості випадків сервер, який відповідає на WM_DDE_INITIATE, створює спеціальне вікно для кожного DDE-розмови. Це пов'язано з тим, що при подальшому обміні даними сервіс і тема вказуватися не будуть, а сам DDE-розмова визначається фактично вікном-клієнтом і вікном-сервером. Одна така пара вікон обмінюється даними тільки в рамках зазначених при встановленні DDE-розмови теми та сервісу. Тільки самі прості DDE-сервери можуть обходитися одним вікном, але при цьому вони в даний момент часу можуть працювати тільки з одним клієнтом.

Увага! При встановленні зв'язку з яким-небудь сервером Ви можете отримати декілька повідомлень WM_DDE_ACK від різних серверів, що підтримують зазначені сервіс та тему. Якщо в повідомленні WM_DDE_INITIATE ви вказали яку підтримувану тему та / або будь-який можливий сервіс, то практично завжди відповідають відразу кілька серверів. У цьому випадку Ви повинні створювати список всіх тем і сервісів, з якими встановлюється зв'язок даної операцією (не забудьте в кінці закрити всі розпочаті DDE-розмови), а якщо ви збираєтеся працювати тільки з одним із відповіли серверів, то з усіма іншими ви повинні завершити DDE-розмова посилкою WM_DDE_TERMINATE.

Зауваження. Згідно документації сервер, що отримав WM_DDE_INITIATE із зазначенням будь підтримуваної теми або сервісу, зобов'язаний відправити у відповідь стільки повідомлень WM_DDE_ACK, скільки тим і сервісів він підтримує. Насправді багато DDE сервери цього не роблять, як, наприклад, Microsoft Internet Explorer. Такі сервери відповідають тільки на точно зазначені сервіс та тему. Це може бути виправдано, якщо сервер підтримує значне число тих чи сервісів. При відповіді на WM_DDE_INITIATE вже устанавліваетя DDE-розмова, для якого сервер створює окреме вікно і, відповідно, витрачає ресурси. Якщо сервер відповідає на цілий список тем та / або сервісів, то створюється багато вікон, що, швидше за все, зайве. Строго кажучи, вказувати будь-яку тему або сервіс треба тільки в разі реальної необхідності, так як одним таким запитом можуть бути встановлені кілька десятків DDE-розмов відразу (так, наприклад, один тільки Netscape Navigator відповідає приблизно 3 десятками підтримуваних ним тем).

Початок DDE-розмови - це єдиний випадок, коли повідомлення DDE передаються за допомогою SendMessage, а не PostMessage. Це необхідно для нормального початку обміну.

Зараз ми розглянемо невеликий приклад взаємодії двох додатків, клієнта і сервера при початку DDE-розмови.

У даному прикладі розглянуто найпростіший випадок, коли зв'язок встановлюється тільки з одним сервером і для однієї теми. При цьому можна обійтися без створення списку сервісів і тим, з якими встановлюються з'єднання.

Вважається, що при ініціалізації DDE-розмови, клієнт повинен отримати відповідь (або переконається в його відсутності) негайно. Цього можна досягти тільки використовуючи передачу, а не посилку повідомлень. У цьому випадку підтвердження приходить в той час, поки клієнт чекає завершення роботи процедури SendMessage. Для нормального продовження DDE клієнт повинен запам'ятати хендл сервера (а сервер - хендл клієнта).

Так як можливий випадок, що на запит клієнта відповідять два і більше серверів, то треба передбачити, щоб або відмова від встановлення більш ніж одного з `єднання (як у наведеному прикладі), або організувати DDE відразу з декількома серверами.

Якщо клієнт отримав позитивну відповідь від сервера, то він може почати обмін даними. Цей обмін буде продовжуватися до тих пір, поки обидва додатки не обміняються повідомленнями

WM_DDE_TERMINATE hWnd 0L

повідомлення закінчує DDE-розмова. Параметр hWnd є хендлов послав вікна. При цьому знищуються допоміжні структури та обнуляються змінні.

Послати WM_DDE_TERMINATE може як клієнт, так і сервер. Обидва вони повинні бути готові до прийому такого повідомлення від напарника.

Обмін даними між клієнтом і сервером

Обмін даними між клієнтом і сервером може відбуватися за кількома різними сценаріями. Всього можна виділити три способи отримання даних від сервера і ще один спосіб, призначений для передачі даних від клієнта до сервера.

У DDE розрізняють три способи отримання даних від сервера, званих видами зв'язку. Ці три види зв'язку називаються холодна, тепла і гаряча. Коротко пояснимо відмінності цих видів зв'язку:

  • холодна зв'язок - cold link

обмін даними відбувається тільки по запиту клієнта. Сервер посилає у відповідь дані або негативне підтвердження.

  • гаряча зв'язок - hot link

клієнт "підписується" на періодичне отримання даних від сервера, після чого сервер починає передавати дані клієнта, як тільки в цьому виникає необхідність. Для завершення гарячої зв'язку клієнт повинен повідомити про це серверу.

  • тепла зв'язок - warm link

клієнт, як і при гарячому зв'язку, підписується на отримання оновлених даних. Однак сервер передає не дані, а лише повідомлення про те, що у нього є дані для клієнта. Клієнт може зажадати дані у сервера у будь-який зручний для нього момент.

Останні два види зв'язку (тепла і гаряча) називаються іноді постійної (permanent) зв'язком.

Передача даних від клієнта до сервера здійснюється тільки одним способом, з ініціативи клієнта, який передає серверу відповідне повідомлення, що містить їх посилають дані.

Коли два додатки обмінюються даними один з одним, вони передають один одному хендла блоків даних і атоми. Для того, що б ці дані були доступні обом додаткам, вони повинні бути глобальними. Однак треба врахувати, що зазвичай глобальні блоки даних пов'язані з тим додатком, що їх створила, тобто при завершенні програми ці дані автоматично знищуються. Так як процес DDE-розмови є асинхронним, то необхідно забезпечувати збереження даних незалежно від існування створив їх застосування. Для цього глобальні блоки даних повинні бути розділяються - при їх виділення треба вказувати прапор GMEM_DDESHARE (або GMEM_SHARE, який є синонімом).

У разі платформи Win32 використовуються колишні функції для виділення глобальних блоків даних, незважаючи на те, що блоки виділяються тільки в локальному для кожного процесу віртуальному адресному просторі. Система автоматично здійснює передачу даних з адресного простору одного процесу в адресний простір іншого процесу при передаще відповідних повідомлень.

Особливу увагу треба приділити питанню звільнення ресурсів, тому що атоми і передані блоки даних самі не знищуються, навіть якщо все що у DDE програми завершили роботу. Всі створені об'єкти повинні бути обов'язково знищені, незалежно від результату операції. Складнощі пов'язані з тим, що один і той-же об'єкт може бути знищений або клієнтом, або сервером, в залежності від протікання процесу обміну, а, крім того, в процесі обміну можуть прісходіт різні помилки (наприклад, функція PostMessage не може послати повідомлення) .

"Холодна" зв'язок - cold link

При холодній зв'язку обмін даними активується клієнтом. Для цього клієнт посилає спеціальне повідомлення, у відповідь на яке сервер відправляє дані (якщо може), або повідомлення про помилку:

WM_DDE_REQUEST hWnd aItem & cfFormat

це повідомлення є вимогою передачі даних. Параметр wParam є хендлов послав повідомлення вікна, а lParam містить в молодшому слові номер формату даних (номери те-ж, що використовуються буфером обміну), а в старшому - атом імені даних. Платформи Win32 і Windows 3.x використовують це повідомлення однаково.

У відповідь на це повідомлення сервер може відповісти або повідомленням WM_DDE_DATA, якщо він має необхідні дані і може їх послати, або "негативним" сполученням WM_DDE_ACK, що вказує, що сервер не може послати потрібні дані.

WM_DDE_DATA hWnd aItem & hData (Packed Win32)

Це повідомлення передає дані клієнта. Молодший компонент lParam містить хендл глобального розділяється блоку даних hData. Цей параметр може бути дорівнює 0, що реально зустрічається тільки у випадку "теплою" зв'язку (див. нижче). Рухаючись блок даних повинен починатися зі структури DDEDATA, прапор fAckReq якої вказує на необхідність відправлення підтвердження про отримання даних. Передача: сервер не може скидати одночасно обидва біти fRelease і fAckReq в 0. Тобто коректними є тільки три можливих комбінації fRelease і fAckReq, при яких можна визначити, хто має звільняти блок даних - клієнт або сервер. Звільнення ресурсів: атом aItem повинен видалятися, якщо тільки він не передається відповідним повідомленням WM_DDE_ACK (якщо біт fAckReq заголовка даних дорівнює 1, тобто потрібно відповідь). Звільнення блоку даних залежить від установки бітів fRelease і fAckReq заголовка даних:

fRelease

fAckReq


0

1

блок даних завжди звільняється сервером при отриманні відповіді від клієнта (вважається, що до моменту отримання сервером підтвердження клієнт вже прочитав необхідні дані і вони йому більше не потрібні).

1

0

блок даних завжди звільняється клієнтом, сервер відповіді не отримує (так клієнт може деякий час зберігати отримані дані незалежно від перебігу DDE-розмови).

1

1

при успішному читанні блок даних звільняється клієнтом, а в разі помилки (тобто при отриманні сервером негативного підтвердження) блок звільняється сервером. Цей варіант близький до попереднього, за винятком того, що за помилково отриманий блок клієнт відповідальності не несе.

Структура DDEDATA містить наступні дані:

typedef struct tagDDEDATA {WORD unused: 12, fResponse: 1, / / 1 у відповідь на WM_DDE_REQUEST, 0 - WM_DDE_ADVISE fRelease: 1, / / дані повинні бути видалені після отримання reserved: 1, fAckReq: 1; / / 1 - необхідно послати підтвердження про прийом short cfFormat; / / формат даних BYTE Value [1]; / / самі дані} DDEDATA;

Увага! В документації по Windows 3.x SDK і Win32 SDK містилася помилка - там були переплутані пояснення до полів fResponse і fAckReq, так що опис структури DDEDATA суперечило опису повідомлення WM_DDE_DATA. У документації, що супроводжує компілятор Microsoft Visual C + + v4.0 ця помилка була виправлена.

Зауважте, що сервер може вимагати підтвердження про прийом посланих даних. Для цього він встановлює біт fAckReq в заголовку переданих даних рівним 1. У такому випадку необхідно надіслати повідомлення у відповідь WM_DDE_ACK (позитивне чи негативне, якщо виникла якась помилка).



Якщо сервер не має потрібних даних, то замість WM_DDE_DATA клієнт отримає WM_DDE_ACK (негативне)



WM_DDE_ACK hWnd aItem & wStatus (Packed Win32)

Молодший компонент lParam містить інформацію про надсилайте підтвердження. Молодший байт цього слова містить код, що повертається додатком, старший байт містить два значущих біта 0x80 і 0x40. Часто цей параметр представляється у вигляді структури DDEACK. Старше слово lParam являє собою атом, що ідентифікує дані. Згідно з документацією цей атом обов'язково повинен бути видалений при обробці цього повідомлення. У разі платформи Win32 параметр lParam використовується для передачі "упакованого" значення; при цьому треба пам'ятати, що якщо WM_DDE_ACK приходить у відповідь на WM_DDE_INITIATE, то параметр lParam містить звичайні, не упаковані дані.

Структура DDEACK містить наступні дані:

typedef struct tagDDEACK {WORD bAppReturnCode: 8, / / код, який визначається додатком reserved: 6, fBusy: 1, / / 1 - сервер зайнятий і не може обробити запит fAck: 1; / / 1 - повідомлення було отримано додатком} DDEACK;

У залежності від значень бітів fBusy і fAck розрізняють наступні можливі види відповідей:

  • fAck = 1 fBusy = 0 - позитивне підтвердження

  • fAck = 0 fBusy = 0 - негативне підтвердження

  • fAck = 0 fBusy = 1 - сервер зайнятий, або не може обробити запит в розумні строки (що є "розумні" терміни - не пояснюється)

  • Згідно документації установка обох прапорів fAck і fBusy одночасно повинна бути виключена.

Замість структури DDEACK часто використовують просто бітові шаблони - 0x8000 відповідає позитивному підтвердження, 0 - негативному і 0x4000 - сервер зайнятий (взагалі кажучи, це теж негативне підтвердження, хоча запит можна спробувати повторити).

Додатково ситуація ускладнюється тим, що в процесі DDE-розмови застосовується посилка, а не передача повідомлень. Тобто між посилкою повідомлення сервером та отримати від нього відповіді може пройти чимало часу, протягом якого необхідно обробляти інші повідомлення, які надходять, система при цьому може перемикатися на інші програми і, що найнеприємніше, в цей час або клієнт, або сервер можуть взагалі завершити роботу. У цій ситуації доводиться вставляти спеціальні фрагменти коду, які здійснюють очікування того або іншого повідомлення протягом вказаного проміжку часу і приймати спеціальні заходи, якщо відповіді занадто довго немає.

/ / / / Wait for time out / / static BOOL TimeOut (HWND hWnd, UINT wMsg, UINT uTimeOut) {DWORD dwTime; MSG msg;

/ / FALSE design normal termination / / TRUE design time-out or negative WM_DDE_ACK dwTime = GetCurrentTime (); while (dwTime + uTimeOut> GetCurrentTime ()) {if (PeekMessage (& msg, hWnd, NULL, NULL, PM_REMOVE)) {/ / обробити отримане повідомлення TranslateMessage (& msg); DispatchMessage (& msg); if (msg.message == WM_DDE_ACK) {/ / check for. fAck return LOWORD (msg.lParam) & 0x8000? FALSE: TRUE;} else if (msg.message == wMsg) return FALSE;}}

return TRUE;}

Зазвичай процедура, яка чекає потрібне повідомлення, виглядає дещо інакше. Це пов'язано з бажаним відображенням курсору очікування (у вигляді пісочного годинника), а також з обробкою повідомлень, витягнутих з черги. У багатьох випадках нормальна обробка повідомлень не зводиться тільки до функцій TranslateMessage і DispatchMessage, але також включає підтримку акселераторів, немодальний діалогів та ін Крім цього слід врахувати, що в разі платформи Windows 3.x розмір черги повідомлень додатку порівняно невеликий - так що при вилученні повідомлень, спрямованих тільки конкретного вікна, черга може легко переповнитися.

Ще одне зауваження стосується часу очікування відповіді від сервера. Цей проміжок не треба призначати дуже великим, але в той же час він повинен бути достатнім, щоб навіть на повільних машинах впевнено отримувати відповідь від сервера. При надмірно великій часу очікування відповіді може виникати враження "зависання" програми, поки воно чекає відповіді. Зазвичай людина очікує від комп'ютера майже миттєвої реакції або, в гіршому випадку, явно видимої роботи. Якщо програма зупиняється на строк більше 10-15 секунд, то вже виникає занепокоєння про її роботу.

Практично прийнятним є інтервал порядку 5-30 секунд, тому що на середній машині відповідь може затримуватися до 5-10 секунд при нормальній роботі і, відповідно, на повільних машинах інтервал слід збільшити приблизно до тридцяти секунд. (Різниця між швидкими й повільними машинами вельми відносно - з ростом потужності комп'ютерів зростає навантаження на них, тому час виконання більш-менш значних операцій знижується порівняно повільно). Можливим рішенням є двоетапне очікування - перший етап до 3-6 секунд проходить як зазвичай, а після закінчення цього інтервалу відображається вікно діалогу з кнопкою "Cancel", після чого очікування триває або до отримання відповіді, або до скасування очікування користувачем. Це дозволяє користувачеві оцінити зайнятість системи і, при необхідності, продовжити очікування в розумних межах.

Задача визначення часу очікування відповіді від сервера є досить важливою через необхідність звільняти зайняті ресурси. Фактично, при обміні повідомленнями виникає наступна ситуація: при посилці яких або поділюваних об'єктів від однієї програми до іншого (це можуть бути атоми чи глобальні блоки даних) необхідно ці об'єкти видаляти після їх використання. Проте тут треба врахувати такі нюанси:

  • якщо функція PostMessage не може послати повідомлення, об'єкти можна видаляти відразу, так як другий додаток їх не отримає.

  • якщо повідомлення надіслано, то момент знищення об'єктів вибирається згідно з описом повідомлення в документації. Так, наприклад, атоми зазвичай видаляються за наступною схемою

    • при отриманні посланого запиту перевіряється необхідність відповіді, якщо відповідь потрібна, то атом передається разом з відповіддю і знищується додатком, хто послав запит

    • якщо відповідь потрібна, але його надіслати не вдається, то атом знищується отримав запит додатком

    • якщо відповідь не потрібен, то атом знову-ж знищується отримав запит додатком

  • надіслане повідомлення може "не дійти" до адресата (наприклад, якщо вікно-отримувач буде закрито вже після того, як до нього в чергу потрапить це повідомлення, але до його обробки). При цьому відповіді не буде, а додаток-отримувач звільнити ресурси не зможе.

  • надіслане повідомлення може бути успішно прийнята, а при його обробці, але вже після звільнення отриманих ресурсів, одержувач не зможе відправити назад необхідного підтвердження.

У двох останніх випадках питання звільнення ресурсів залишається відкритим, оскільки додаток, послали запит, не може отримати інформацію про результати його виконання, і, відповідно, про необхідність звільнення даних. При цьому можна злегка змінити розглянуте раніше "двоетапне" очікування, додавши в діалог кнопку або прапорець звільнення ресурсів. Зазвичай користувач може оцінити причину затримки відповіді - збій в роботі, або дострокове завершення роботи програми та момент виникнення цієї ситуації - до обробки чи після. Принаймні, якщо ситуація буде повторюваної, то користувач зможе підібрати найбільш "дешевий" вихід з неї.

"Гаряча" зв'язок - hot link

Протокол при гарячій зв'язку. Після ініціації обміну клієнт посилає запит WM_DDE_ADVISE, указуючи бажані дані. Сервер відповідає про можливості гарячої зв'язку, і, звичайно, відразу ж посилає перші дані, для ініціації клієнта. Таким чином осуществялется як-би "підписка" на отримання необхідних даних при їх зміні. Цей вид зв'язку зручний, якщо Вам необхідно взаємодіяти з будь-якою базою даних, вміст якої може динамічно оновлюватися, як наприклад база даних "товари на складі" для великих магазинів - доступ до цієї бази одночасно можуть мати різні відділи магазину і список товарів, що є на сладе, постійно змінюється. Використання гарячої зв'язку в цьому випадку дозволить створити локальні списки на комп'ютерах різних відділів і автоматично оновлювати їх, як тільки здійснюється зміна загальної бази даних.

WM_DDE_ADVISE hWnd aItem & hOptions (Packed Win32)

Молодший компонент параметра lParam містить хендл глобального розділяється блоку, що містить невелику структуру DDEADVISE, а старший компонент - атом, що описує дані на які здійснюється підписка. Структура DDEADVISE визначає тип зв'язку - гаряча або холодна, а також необхідність вимагати підтвердження про доставку даних від клієнта. У разі платформи Win32 параметр lParam використовується для передачі "упакованого" значення. Сервер, що отримав це повідомлення зобов'язаний відповісти на нього посилкою підтвердження WM_DDE_ACK, позитивного чи негативного. Звільнення ресурсів: Клієнт, що послав запит, повинен дочекатися відповіді від сервера. Якщо відповідь позитивна, то блок даних повинен бути вилучений сервером, а якщо негативний, то клієнт повинен сам його видалити.

Структура DDEADVISE містить наступні дані:

typedef struct {WORD reserved: 14, fDeferUpd: 1, / / 1 - "тепла" зв'язок, 0 - "гаряча" fAckReq: 1; / / 1-необхідно вимагати підтвердження, 0-не треба short cfFormat; / / бажаний формат даних } DDEADVISE;

При завданні біта fDeferUpd рівним 1 здійснюється так звана тепла зв'язок, при якій сервер тільки інформує про наявність у нього змінених даних, але не посилає їх клієнту безпосередньо. У разі гарячої зв'язку цей біт должет бути дорівнює 0. Біт fAckReq, рівний 1, вказує на необхідність вимагати від клієнта підтвердження про отримання даних. Пізніше, коли сервер почне посилати клієнту повідомлення WM_DDE_DATA він встановить у структурі DDEDATA біт fAckReq відповідним тому, який Ви вказали в структурі DDEACK і, відповідно, клієнт повинен буде підтверджувати отримання ним даних від сервера (треба врахувати, що біт fAckReq впливає на стратегію звільнення посилаються даних - докладніше див опис повідомлення WM_DDE_DATA).

Поле cfFormat вказує на формат даних, які клієнт очікує від сервера. Ви можете для одного виду даних в межах одного DDE-розмови підписатися на отримання даних різного типу одночасно, посилаючи WM_DDE_ADVISE стільки разів, скільки форматів даних Ви хочете отримувати. Однак така можливість дозволена тільки для гарячої зв'язку. Згідно з документацією підписка на одні й ті-самі дані у різних форматах для теплої зв'язку заборонена.

Сервер, при отриманні повідомлення WM_DDE_ADVISE, перевіряє можливість здійснити передплату на ці дані, створює необхідні йому структури даних і відповідає клієнтові повідомленням WM_DDE_ACK - позитивним чи негативним. Це повідомлення в даному випадку використовується так-же, як і при відповіді на WM_DDE_DATA.

Якщо підписка здійснена успішно, то далі сервер починає сповіщати клієнта про оновлення даних, здійснюючи щоразу пересилання цих даних клієнта з допомогою вже розглянутої повідомлення WM_DDE_DATA. Єдина відмінність від отримання даних у відповідь на WM_DDE_REQUEST полягає в тому, що біт fResponse структури DDEDATA дорівнює 0, тоді як при відправленні даних на запит він встановлюється в 1. Це дозволяє визначити причину отримання даних при обробці повідомлення.

Закінчується такий обмін посилкою WM_DDE_UNADVISE сервера. У цьому повідомленні вказується, від підписки на які дані клієнт відмовляється (так як теоретично один клієнт може підписатися відразу на кілька видів даних).

WM_DDE_UNADVISE hWnd aItem & cfFormat

Молодше слово параметра lParam описує формат даних, а старше слово - атом, що описує дані на які здійснюється підписка. У разі платформи Win32 параметр lParam використовується також, як і у випадку Windows 3.x. Якщо параметр cfFormat дорівнює 0, то скасовується підписка на зазначені дані для всіх тих форматів, для яких була здійснена передплата. Якщо параметр aItem дорівнює NULL, то скасовується підписка на всі види даних в межах даного DDE-розмови. Сервер, обробивши це повідомлення, повинен послати у відповідь підтвердження WM_DDE_ACK, позитивне чи негативне; при цьому якщо одна операція скасовує підписку на дані в різних форматах, то посилається тільки одне підтвердження.

Повідомлення WM_DDE_UNADVISE не еквівалентний WM_DDE_TERMINATE, після його обробки скасовується лише підписка, а сам DDE-розмова триває, так що клієнт може встановити потім новий вид зв'язку з сервером. Сервер зобов'язаний відповісти на повідомлення WM_DDE_ADVISE позитивним чи негативним підтвердженням.

"Тепла" зв'язок - warm link

Протокол при теплій зв'язку. Після ініціації обміну клієнт посилає запит WM_DDE_ADVISE, указуючи бажані дані. Сервер відповідає про можливість теплою зв'язку, і, звичайно, відразу ж посилає WM_DDE_DATA, сповіщаючи клієнта про можливість негайного отримання даних. Пізніше сервер посилає WM_DDE_DATA клієнту, коли дані оновлюються. Повідомлення WM_DDE_DATA в даному випадку не передають самих даних, а тільки сповіщають клієнта про їх наявність у сервера. Протоколи зв'язку і використовувані повідомлення при теплій зв'язку практично повністю збігаються з протоколами і повідомленнями гарячі зв'язку, тому ми просто розглянемо ті деякі відмінності, які є між двома цими видами зв'язку.

Як і гаряча зв'язок, тепла встановлюється за допомогою повідомлення WM_DDE_ADVISE з єдиним винятком - біт fDeferUpd структури DDEADVISE дорівнює 1, а не 0.

Далі сервер починає інформувати клієнта про оновлення (або наявності) у нього потрібних даних. Для цього використовується звичайне повідомлення WM_DDE_DATA, яке в даному випадку не передає жодних даних. Для цього параметр hData (молодше слово lParam або, у разі Win32, молодша компонента, яку видобувають за допомогою UnpackDDElParam) встановлюється рівним 0. Клієнт, одержуючи таке повідомлення без даних, може негайно запросити потрібні дані або просто запам'ятати, що дані змінилися і пізніше, при необхідності, отримати їх.

Зауважте, що у разі гарячої зв'язку ви можете підписатися відразу на отримання інформації про зміну одних і тих-же даних в різних форматах, а у випадку теплої зв'язку це заборонено. Така заборона пояснюється тим, що за підтримки таких видів зв'язку для різних даних або одних і тих-же даних, але в різних форматах, клієнт повинен вести список даних і форматів на які здійснена передплата. При отриманні WM_DDE_DATA, информирующего про зміну даних ім'я даних задається параметром aItem (упакований у lParam), а формат даних - полем cfFormat структури DDEDATA. Однак у випадку теплої зв'язку повідомлення WM_DDE_DATA не містить даних і, отже, інформація про формат даних недоступна, при цьому однозначно знайти запис в спік не представляється можливим, якщо тільки не накласти обмеження - при теплій зв'язку підписуватися можна на дані тільки в одному форматі, тоді запис списку буде однознано визначена ім'ям даних.

Для того, щоб отримати потрібні дані, клієнт просто посилає окремий запит WM_DDE_REQUEST, як у разі холодної зв'язку. Таким чином тепла зв'язок є "гібридом" двох інших видів зв'язку - холодною і гарячою.

Скасування підписки у випадку теплої зв'язку нічим не відрізняється від такого-ж дії для гарячої зв'язку - клієнт посилає повідомлення WM_DDE_UNADVISE сервера, який відповідає на це підтвердженням.

Робота з DDE у випадку теплої або гарячої зв'язку зазвичай не викликає значних труднощів, а проте треба звернути увагу на можливість використання декількох видів зв'язку одночасно. Зазвичай, при написанні програм використовуються припущення типу "У відповідь на надіслане WM_DDE_REQUEST має прийти або WM_DDE_DATA, або WM_DDE_ACK". Проте в житті ситуація може виявитися істотно складніше - якщо одне вікно використовує кілька з'єднань, то у відповідь на посланий WM_DDE_REQUEST спочатку може прийти повідомлення WM_DDE_DATA, що інформує про отримання даних (у випадку теплої або гарячої зв'язку) і тільки потім справжня відповідь на WM_DDE_REQUEST. Для того, щоб зменшити ймовірність таких ситуацій, для кожного DDE-розмови створюють спеціальне приховане вікно, що взаємодіє з сервером (або клієнтом). Однак при розробці власних програм таку можливість все-одно треба враховувати, тому що в процедурах обробки повідомлень доведеться перевіряти, у зв'язку з якою подією отримано те ​​чи інше повідомлення; а при розробці DDE-клієнтів треба постаратися розділити різні види обміну даними або за різними DDE -розмов, або за часом.



Передача даних від клієнта до сервера

При обміні даними між клієнтом і сервером можливо не тільки отримання клієнтом даних, що містяться на сервері, але і можливість передачі деяких даних від клієнта до сервера. Для цього передбачено спеціальне повідомлення WM_DDE_POKE, що надсилається клієнтом серверу для запису даних.

WM_DDE_POKE hWnd aItem & hData (Packed Win32)

Повідомлення WM_DDE_POKE надсилається клієнтом для передачі даних серверу. У разі платформи Windows 3.x молодший компонент параметра lParam містить хендл передаваного блоку даних, а старший - атом імені переданих даних. Рухаючись блок даних повинен бути глобальним та спільних і має починатися зі структури DDEPOKE. При посилці повідомлення WM_DDE_POKE використовується наступна стратегія звільнення блоку даних: він знищується хто послав додатком (клієнтом), якщо:

  • в заголовку блоку біт fRelease дорівнює 0

  • клієнт повернув негативну відповідь

  • посилка даних не відбулася

Отримало дані додаток (сервер) буде знищувати цей блок тільки якщо біт fRelease дорівнює 1 і сервер повертає позитивне підтвердження.

typedef struct {WORD unused: 13, fRelease: 1, / / 1 - блок звільняється клієнтом fReserved: 2; short cfFormat; / / формат даних (див. формати буфера обміну) BYTE Value [1]; / / передані дані} DDEPOKE;

Сервер, що отримав це повідомлення повинен зберегти отримані дані і повідомити клієнта про результат виконання цієї операції посилкою повідомлення WM_DDE_ACK, позитивного чи негативного. У цьому повідомленні може бути переданий отриманий атом імені даних, або отриманий може бути закритий, а для посилки підтвердження створено заново.

Виконання команд DDE

Крім механізму обміну даними між клієнтом і сервером DDE передбачає ще один спосіб взаємодії додатків - виконання сервером команд клієнта. Для того, щоб вказати серверу на необхідність виконання команди, клієнт повинен послати спеціальне повідомлення WM_DDE_EXECUTE, у відповідь на яке сервер намагається виконати команду і повідомляє клієнту про результат її виконання, посилаючи у відповідь повідомлення WM_DDE_ACK (позитивне чи негативне).

WM_DDE_EXECUTE hWnd hCmd & 0 (Windows 3.x)

WM_DDE_EXECUTE hWnd hCmd (Win32)

Параметр lParam описує команду, передану сервера. Для цього рядок, що містить команду, поміщається в блок даних, що виділяється за допомогою функції GlobalAlloc з прапором GMEM_DDESHARE. У разі платформи Windows 3.x старше слово параметра lParam містить хендл цього блоку, а молодше - просто 0; А в разі платформи Win32 параметр lParam безпосередньо містить цей хендл. Звільнення ресурсів: блок даних, що містить команду, повинен бути звільнений клієнтом при отриманні підтвердження від сервера. Разом з цим підтвердженням повертається хендл цього блоку, так що клієнт може вільно його звільнити.

У разі платформи Win32 рядок, що містить команду може бути представлена ​​UNICODE-рядком, але тільки в тому випадку, якщо обидва беруть участь у DDE-розмові вікна підтримують UNICODE, тобто обидва повертають TRUE при виклику функції IsWindowUnicode (hWnd).

У відповідь на повідомлення WM_DDE_EXECUTE сервер посилає повідомлення WM_DDE_ACK, що інформує про виконання команди (позитивне) або про невдачу (негативне). У цьому повідомленні повертається хендл блоку, що містить команду, так що сервер може цей блок видалити, а також в параметрі wStatus - у молодшому байті - може бути повернений додатковий код, уточнююче результат чи причину помилки (див. опис структури DDEACK, що представляє параметр wStatus в розділі, присвяченому холодної зв'язку).

WM_DDE_ACK hWnd hCmd & wStatus (Packed Win32)

Молодший компонент lParam містить інформацію про надсилайте підтвердження. Молодший байт цього слова містить код, що повертається додатком, старший байт містить два значущих біта 0x80 і 0x40. Часто цей параметр представляється у вигляді структури DDEACK. Старший компонент lParam представляє собою хендл блоку, що містить команду. Згідно з документацією цей блок обов'язково повинен бути звільнений при обробці цього повідомлення. У разі платформи Win32 параметр lParam використовується для передачі хендла "упакованих" даних.

При розробці власних DDE-серверів, виконуючих які-небудь команди, треба звернути увагу на формат запису цих команд. Розробники Microsoft припускають абсолютно певний формат запису команд, який, на жаль, часто не підтримується сторонніми розробниками. Звичайно, з точки зору сумісності, бажано дотримуватися цього формату.

Згідно з документацією Microsoft команда повинна представляти собою рядок, що закінчується нульовим символом і містить одну або декілька команд. Кожна команда повинна бути укладена в квадратні дужки [...]. Кожна команда складається з двох частин - імені команди і, можливо, списку параметрів. При цьому список параметрів полягає в круглі дужки, а параметри у списку розділяються комами. Ім'я команди не може містити пропусків, дужок, ком і лапок; параметри можуть містити ці символи, але в такому випадку такий параметр повинен бути укладений в лапки, а символ лапок у тексті параметра представляється як повторювана лапки. У колишніх версіях DDE - для Windows 3.x - було потрібно, що б символи () [], що зустрічаються в тексті параметра (у лапках) дублювалися; в той час як в Win32 це вже не потрібно. Проблема, однак, існує - в середовищі Win32 можуть бути запущені як 16-ти розрядні додатки, так і 32-х розрядні, при цьому можлива взаємодія таких додатків за допомогою DDE. У такій ситуації сервери повинні розпізнавати обидва варіанти подання дужок - як повторювані, так і не повторюється.

Приклади для Win32:

[Команда] [команда1] [команда2] [команда_с_параметрамі (параметр1, параметр2)] [командаN] [команда (параметр1, "параметр2 з пробілами, дужками [] () і" "лапками")]

У випадку Windows 3.x останній приклад буде виглядати так:

[Команда (параметр1, "параметр2 з пробілами, дужками [[]](()) і" "лапками")]

Якщо рядок містить кілька вкладених у квадратні дужки команд, то сервер повинен розглянути цей рядок за командами та обробити їх послідовно. Варіант з передачею кількох команд в одному рядку має одну незручність - повертається результат виконання всієї послідовності команд, причому зрозуміти де виникла помилка (скажімо, синтаксис команди або недолік вільних ресурсів) буде практично неможливо.

Тема DDE-розмови "System"

Згідно документації Microsoft всім що розробляються DDE-серверів рекомендується підтримувати в кожному сервісі спеціальну службову тему DDE-розмови "System". Ця тема призначена для отримання основної інформації про сервер. У межах цієї теми визначено декілька стандартних імен даних, до яких можна звернутися для отримання необхідної інформації.

На жаль самі розробники Microsoft не дуже строго дотримують це правило. Так, наприклад, Microsoft Word і Microsoft Excel підтримують цю тему, а Program Manager, Explorer, Internet Explorer - не підтримують. Однак документація залишається документацією, тому при розробці власних DDE-серверів варто дотримуватися запропонованих правил.

При обміні даними з серверами, що підтримують тему "System" треба дотримуватися наступних правил:

  • для отримання даних використовується холодна зв'язок

  • дані надаються тільки у форматі CF_TEXT

  • по багатьом запитуваною даними повертається список значень. Цей список поданий у вигляді рядка, що кінчається нульовим символом, окремі пункти якої відокремлюються символом табуляції (код - 9).

  • При використанні DDEML ім'я теми "System" визначено у файлі DDEML.H як SZDDESYS_TOPIC.

Крім теми "System" передбачено спеціальну назву даних, які повинні підтримуватися в усіх інших темах, за допомогою якого можна отримати інформацію про всіх підтримуваних іменах даних у цій темі.

При розробці власного сервера не обов'язково підтримувати всі наведені дані, так само як не обов'язково взагалі підтримувати тему "System" (хоча це бажано). Якщо Ви підтримуєте тільки деякі дані цієї теми, зверніть увагу на дані "SysItems", які дозволяють клієнтові дізнатися, які саме дані можна вимагати.

Назва теми для DDE Назва теми для DDEML

Опис

Formats SZDDESYS_ITEM_FORMATS

Повертає список підтримуваних сервером форматів даних. Зазвичай назви збігаються з назвами форматів буфера обміну, але перші три символи "CF_" опущені. Тобто формат CF_BITMAP буде представлений як BITMAP. Рекомендується так упорядковувати назви форматів в списку, що б формати передають більше інформації були першими. Тобто, наприклад, формат DIB повинен бути розташований до формату BITMAP.

Help SZDDESYS_ITEM_HELP

Пояснення до використання даного DDE сервера. Досить довільний текст, бажано пояснюючий, які дані можуть бути отримані, які команди можуть бути виконані які дані можуть бути передані сервера і т.д.

ReturnMessage SZDDESYS_ITEM_RTNMSG

Інформація про останні надіслані підтвердження WM_DDE_ACK. З допомогою цього типу даних можна передавати додаткові дані, що уточнюють результат виконання останньої транзакції.

Status SZDDESYS_ITEM_STATUS

У відповідь на цей запит сервер повинен відправити рядок "Busy" або "Ready", що інформує про стан сервера. Зауважте, що цей рядок сервер повинен посилати, навіть якщо він зайнятий і не може обробляти інші запити.

SysItems SZDDESYS_ITEM_SYSITEMS

Повертає список імен даних, які представлені в темі "System". Цей список може використовуватися клієнтом, що б дізнатися, які дані він може запитувати в рамках теми "System".

Topics SZDDESYS_ITEM_TOPICS

Повертає список тем, підтримуваних сервером в даний момент часу. У процесі роботи сервера цей список може змінюватися, так, наприклад, Microsoft Word відкриває окрему тему для роботи з кожним завантаженим документом.

TopicItemList SZDDE_ITEM_ITEMLIST

Аналогічно SysItems, повертає список даних, підтримуваних для даної теми. Цей запит може застосовуватися до тем, які не є темою "System". Цей список може змінюватися в процесі роботи сервера.

Program Manager як DDE-сервера

Program Manager є спеціалізованим DDE-сервером. Він дозволяє запущених застосунків керувати конфігурацією груп додатків, змінювати їх атрибути і т.д. Зазвичай програми установки додатків використовують DDE з Program Manager для створення необхідної групи й наповнення її елементами. У випадку з Windows-94 або Windows NT v4.0 і вище Program Manager зазвичай не використовується, але який працює при цьому Explorer підтримує ті-ж самі сервіс, тему і команди, що й Program Manager. При цьому замість груп створюються підміню в меню Пуск | Програми (Start | Programs).

У принципі Windows може бути налаштований так, що замість Program Manager використовується абсолютно інша оболонка, яка може не підтримувати описуваний DDE. Можливим виходом з цієї ситуації є спроба запуску Program Manager (PROGMAN.EXE), якщо з першого разу не вдається встановити DDE-розмова.

Для взаємодії з Program Manager необхідно встановити DDE-розмова з сервером, що підтримує сервіс і тему з однаковим ім'ям "PROGMAN", а потім можна передавати необхідні команди (повідомлення WM_DDE_EXECUTE). Команди, які підтримує Program Manager, дозволяють додатком створювати, відображати, видаляти і перезавантажувати групи, додавати, змінювати або видаляти елементи груп і завершувати роботу Program Manager. Для цього призначені наступні команди:

CreateGroup - створити групу програм

ShowGroup - показати групу в зазначеному стані

Reload - перезавантажити групу з файлу

DeleteGroup - видалити групу

AddItem - додати елемент

ReplaceItem - змінити елемент

DeleteItem - видалити елемент

ExitProgman - вийти з Program Manager

Program Manager обробляє команди у форматі, рекомендованому Microsoft. Тобто кожна команда полягає у квадратні дужки, а якщо вона має додаткові параметри, то список параметрів полягає в круглі дужки, причому параметри в списку розділені комами. В одному запиті до сервера можна вказати кілька команд відразу, наприклад:

[ShowGroup ("Accessories", 1)] [AddItem (myapp.exe, "My app", myapp.exe, 5)]

Цей приклад додає елемент "My app" до групи "Accessories".

Крім виконання цих команд Program Manager може виконувати обмін даними з клієнтом, завдяки чому можливе отримання інформації про наявні групах і елементах цих груп. Для цього використовуються ті ж імена сервісу та теми "PROGMAN", що і для виконання команд, але при цьому серверу передаються запити на отримання даних по холодній зв'язку у форматі CF_TEXT.

Для того, щоб отримати список гуртів, клієнт повинен прочитати дані з ім'ям "Group". У відповідь Program Manager поверне список гуртів, розділений символами переведення рядка.

Додаток може запросити у Program Manager інформацію про групу. Для цього воно повинно прочитати дані з ім'ям, відповідному імені групи. Повертається текст з кількох рядків, кожен рядок складається з кількох компонент, поділюваних комами. Перший рядок визначає інформацію про самій групі, а всі наступні - про елементи групи.

Інформація про групу складається з:

  • імені групи, укладеного в лапки

  • шляху до файлу групи (. grp)

  • кількість елементів у групі

Інформація про елемент групи:

  • команда, укладена в лапки

  • назву каталогу

  • шляху до файлу, який містить піктограму

  • індексом піктограми у файлі

  • "Гарячої клавіші" (у числовій формі)

  • прапору мінімізованого стану під час запуску програми

Примітка: у документації стверджується, що на кількість елементів у групі накладено обмеження - не більше 50 елементів на одну групу. Однак існує інша, набагато більш жорстке обмеження - розмір grp-файлу під Windows 3.x не може бути більше 64K. Цей розмір може бути легко перевищено, якщо використовуються відео-режими з великою кількістю квітів (Hi-Color або TrueColor), наприклад 16, 24 або 32 біта на піксель. Справа в тому, що grp-файл містить в собі зображення всіх піктограм, розмір яких збільшується зі збільшенням числа квітів. При цьому граничний розмір grp-файлу може бути досягнутий після десятка елементів (для TrueColor - приблизно 13 елементів).

Далі більш докладно описуються команди, що обробляються Program Manager. При цьому ми будемо дотримуватись наступного синтаксису - опис самої команди в квадратні дужки не укладається, а необов'язкові параметри виділяються з їх допомогою. Для кращої читаності параметри поділяються додатковим пропуском (при передачі команди цих прогалин бути не повинно).

Рекомендується додатково переглянути розділ "Виконання команд DDE" для отримання довідок про синтаксис запису команд і параметрів.

CreateGroup (GroupName [, CommonGroupFlag]) CreateGroup (GroupName [, GroupFile])

Команда CreateGroup створює зазначену групу, або робить активної вже існуючу.

Параметри:

GroupName рядок, що задає ім'я групи.

CommonGroupFlag параметр вказує, яка група створюється. Якщо він дорівнює 1, то створюється спільна група, а якщо 0, то персональна. Якщо працівник користувач не має адміністративних привілеїв, то спроба створити спільну групу не вдасться. За замовчуванням використовується 1 для користувачів, які мають привілеями адміністратора, і 0 для інших користувачів.

GroupFile в Windows 3.1 цей параметр ставив ім'я grp-файлу. У старших версіях Windows в якості другого параметра розглядаються тільки 0 і 1, інакше цей параметр ігнорується.

ShowGroup (GroupName, ShowCommand [, CommonGroupFlag])

Команда ShowGroup дозволяє показати групу в нормальному, максимізувати або мінімізованому вигляді.

Параметри:

GroupName рядок, що задає ім'я групи.

ShowCommand ціле число, яке вказує, в якому вигляді треба відобразити вікно групи.

1 - відобразити активним в нормальному стані

2 - відобразити активним у мінімізованому стані

3 - відобразити активним у максимізований стані

4 - відобразити вікно групи в нормальному стані; активне вікно залишиться активним

5 - робить вікно групи активним, не змінюючи її розміру і положення

6 - мінімізує вікно групи

7 - мінімізує вікно групи; активне вікно залишиться активним

8 - відображає в поточному стані

CommonGroupFlag параметр вказує, яка група створюється. Якщо він дорівнює 1, то створюється спільна група, а якщо 0, то персональна. Якщо працівник користувач не має адміністративних привілеїв, то спроба створити спільну групу не вдасться. За замовчуванням використовується 1 для користувачів, які мають привілеями адміністратора, і 0 для інших користувачів.

Reload (GroupName [, CommonGroupFlag])

Ця команда змушує Program Manager видалити опис групи з пам'яті і завантажити grp-файл знову. Така можливість може застосовуватися, якщо програма модифікує безпосередньо grp-файл, а потім хоче відобразити зроблені зміни.

Параметри:

GroupName рядок, що задає ім'я групи.

CommonGroupFlag параметр вказує, яка група створюється. Якщо він дорівнює 1, то створюється спільна група, а якщо 0, то персональна. Якщо працівник користувач не має адміністративних привілеїв, то спроба створити спільну групу не вдасться. За замовчуванням використовується 1 для користувачів, які мають привілеями адміністратора, і 0 для інших користувачів.

DeleteGroup (GroupName [, CommonGroupFlag]) DeleteGroup (GroupName [, GroupFile])

Команда DeleteGroup видаляє вказану групу.

Параметри:

GroupName рядок, що задає ім'я групи.

CommonGroupFlag параметр вказує, яка група створюється. Якщо він дорівнює 1, то створюється спільна група, а якщо 0, то персональна. Якщо працівник користувач не має адміністративних привілеїв, то спроба створити спільну групу не вдасться. За замовчуванням використовується 1 для користувачів, які мають привілеями адміністратора, і 0 для інших користувачів.

GroupFile в Windows 3.1 цей параметр ставив ім'я grp-файлу. У старших версіях Windows в якості другого параметра розглядаються тільки 0 і 1, інакше цей параметр ігнорується.

AddItem (CmdLine [, Name [, IconPath [, IconIndex [, xPos, yPos [, DefDir [, HotKey [, fMinimize [fSeparateMemSpace]]]]]]])

Команда AddItem дозволяє додати в активну групу програмний елемент.

Параметри:

CmdLine рядок, що задає команду, яка здійснює запуск програми. Цей рядок має містити назву програми, а також може містити повний шлях до програми та інші необхідні параметри.

Name задає ім'я елемента в групі. Якщо цей параметр опущений, то використовується назва програми.

IconPath задає повне ім'я файлу, що містить ресурс піктограми, яка буде відображатися у вікні групи. Це може бути виконуваний файл Windows (. Exe, . Dll) або окрема піктограма (. Ico). Якщо цей параметр опущений, то піктограма шукається у файлі програми, причому, якщо зазначений файл не є додатком, то використовується піктограма асоційованого з даним типом файлів додатку (асоціації задаються за допомогою реєстру Windows), а якщо і це не вдається, то використовується піктограма по замовчуванням.

IconIndex задає індекс піктограми у файлі, визначеному параметром IconPath.

xPos, yPos задають положення піктограми у вікні групи. Ці параметри представлені цілими числами. Якщо позиція не задана, то Program Manager знаходить перше невикористовуване місце.

DefDir задає ім'я робочого каталогу програми.

HotKey задає "гарячу" клавішу, використовувану для виклику програми.

fMinimize вказує, чи потрібно відображати вікно програми при запуску у вигляді піктограми.

fSeparateMemSpace вказує, чи потрібно запускати 16-ти розрядний додаток у самостійному адресному просторі (використовується на деяких платформах Win32).

ReplaceItem (ItemName)

Команда ReplaceItem використовується для заміни одного елемента іншим. При виконанні цієї команди зазначений об'єкт був видалений, а його позиція запам'ятовується. Подальший виклик AddItem створить новий елемент у цьому місці.

Параметри:

ItemName рядок, що задає ім'я видаляється елемента. На його місці буде розміщено наступний створюваний елемент.

DeleteItem (ItemName)

Команда DeleteItem здійснює видалення зазначеного програмного елемента.

Параметри:

ItemName рядок, що задає ім'я видаляється елемента.

ExitProgman (bSaveGroups)

Ця команда завершує роботу Program Manager.

Параметри:

bSaveGroups величина, що вказує треба чи ні зберігати поточний стан груп перед завершенням роботи Program Manager. При значенні 0 збереження груп не відбувається.

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Лекція
221.6кб. | скачати


Схожі роботи:
Обмін даними між програмами Office
Обмін даними між компонентами ППП MS Officе
Використання технологій обміну даними OLE в ос WINDOWS Призначення та використання технології
Єдність організму і навколишньогосередовища Обмін мінеральних речовин Обмін речовин і енергії
Єдність організму і навколишньогосередовища Обмін мінеральних речовин Обмін речовин і енергії 2
Робота з гарячими клавішами в ОС Windows Призначення гарячих клавіш в ОС WINDOWS -функції
Операційні системи WINDOWS NT NetWare UNIX Оперцiйна система Windows NT
Пошук даних в ОС Windows Основні прийоми роботи із пошуком в ОС WINDOWS
Налаштування локальної мережі і підключення до мережі інтернет для Windows XP і Windows 7
© Усі права захищені
написати до нас