КУРСОВА РОБОТА НА ТЕМУ: “Основні принципи використання технології СОМ/СОМ+”

Зміст

Вступ    3

Сервер банківського рахунку    7

Структура СОМ серверу    8

Термінологія і концепція СОМ    13

Програмна модель клієнта СОМ    23

Консольна програма клієнт на Visual C++    24

Література    29

Додатки    31

Вступ

Модель компонентних об’.єктів, створена як базис для OLE 2.0, може розглядатися як третє покоління архітектури компонентів Microsoft. Першим поколінням компонентів були динамічно компоновані бібліотеки, що забезпечують інтерфейс викликами функцій С. Друге покоління, WOSA, надає велику кількість важливих сервісів, наприклад, таких як ODBC або Windows Sockets. Інтерфейс взаємодії при цьому все ще є викликом функцій С. Трете покоління вводить «Компонентні об’єкти». Як ми бачили, об’єкти інкапсулюють дані і поведінку в єдину суть. Без об’єктів необхідно підтримувати безліч змінних і передавати їх окремим відособленим функціям. Угрупуванням зв’язаних даних і функцій в один об’єкт створюється абстракція, яка спрощує програмування і дозволяє розширити модель програмування для включення нових типів даних.

СОМ розширює ці можливості структуризації об’єктів. Об’єкти можуть мати множинні інтерфейси, кожний з яких підтримує свої властивості і можливості через зв’язані групи функцій. Об’єкт може підтримувати множинні інтерфейси і, якщо клієнт має покажчик на один інтерфейс, одержувати, покажчик на інший інтерфейс за допомогою функції Querylnterface. Ця проста концепція множинних інтерфейсів і механізму запитів надзвичайно корисна, оскільки вона підтримує еволюцію програмних компонентів через введення додаткових інтерфейсів без порушення працездатності існуючих клієнтів. Наприклад, сервер складового документа OLE може як підтримувати, так і не підтримувати активізацію “на місці”. Початково сервер OLE цього не робить, оскільки така активізація не є властивістю OLE 1.0. Щоб сервер підтримував таку активізацію, він повинен забезпечувати додаткові інтерфейси, з якими може працювати нова версія серверного додатку. Це не приведе до порушення роботи старих клієнтських додатків, оскільки старі клієнти не запрошують нові інтерфейси. Нові клієнти зможуть працювати з новим сервером, оскільки їх запити до відповідних інтерфейсів будуть задоволені, і сервер активізуватиметься в контексті додатку-контейнера. Нові клієнти зможуть працювати і із старими серверами, оскільки при запиті нових інтерфейсів вони не зможуть їх знайти і запустять сервер як окремий додаток у власному вікні.

Інша важлива характеристика СОМ полягає у тому, що вона є бінарним стандартом, що дозволяє взаємодіяти програмам, створеним із застосуванням різних мов програмування. Таким чином, різні компоненти СОМ можуть бути створені на С, C++, Visual Basic і Java (версія Microsoft Visual J++) різними виробниками програмного забезпечення. Кожний з цих компонентів може використовуватися будь-якою мовою програмування, що підтримує СОМ. Цей бінарний стандарт є головним удосконаленням в порівнянні з об’єктно-орієнтованими мовами програмування, які визначають об’єкти на рівні початкових текстів, обмежуючи потенціал їх широкомасштабної взаємодії.

Разом з Windows NT 4.0 Microsoft випустила в світ DCOM, або розподілену (distributed) COM, розширюючи СОМ для використовування в мережі. Спочатку СОМ використовувала механізм віддаленого виклику процедур (RPC) для виходу за рамки процесу. Застосування DCOM розширює можливості RPC. Базова архітектура залишається при цьому незмінною. Незалежно від того, чи знаходиться цей об’єкт в тому ж процесі, в іншому процесі або на іншій машині, клієнтська програма звертається до нього однаково. Ця особливість СОМ відома як прозорість розміщення (location transparency).

Основна властивість СОМ полягає у тому, що вона не є проміжним програмним забезпеченням (middleware). COM не розташовується між клієнтом і сервером, як ORB (Object Request Broker) в CORBA або ODBC в WOSA. Час виконання СОМ включається в установку з’єднання між клієнтом і сервером. У випадку, якщо компонент СОМ запускається в тому ж процесі, що і клієнт, накладні витрати відсутні — вони в точність ту ж, що і при виклику віртуальної функції в C++. Якщо компонент запускається поза процесом або на іншій машині, накладні витрати — це тільки видалений виклик процедури, перетинаючий межі процесів або мережі.

СОМ+ можна розглядати як наступне покоління компонентної архітектури, розробленої Microsoft. COM+ интегрирует-MTS в СОМ і забезпечує альтернативу викликам СОМ, використовуючи механізм повідомлень, заснованих на MSMQ. Результат являє собою цілісною системою, в якій спрощується створення як серверних, так і клієнтських додатків. Крім того, є безліч сервісів, надання яких дозволяє створювати додатки, що високомасштабуються.

Загальна назва зв’язаних з СОМ+ технологій в Windows 2000 — Сервіси Компонентів (Component Services). Для того, щоб мати уявлення про те, що ж таке СОМ+, треба навчиться відділяти “компоненти” від “сервісів”. Безліч непорозумінь, пов’язаних з системною архітектурою Microsoft, можна уникнути, якщо відділити інфраструктуру ядра від забезпечуваних ними сервісів. Це розділення до певної міри умовно і не може служити визначенням компонентів або сервісів. Мета такого розділення — допомогти в розумінні питання.

Компоненти

Сервіси

DLL ODBC
COM OLE
MTS
MSMQ

Склалася тенденція вважати ключовою технологією саме OLE. OLE була і залишається неймовірно важкою технологією. Крейг Брокшмідт (Kraig Brockschmidt) героїчно спробував викрити те, що він називав міфом про труднощі OLE. Проблема полягає у тому, що насправді OLE — вельми складна технологія. Геометричне розташування компонентів в складовому документі, активізація додатків в контексті додатку-контейнера, злиття меню — ось тільки невеликий перелік проблем, які слід вирішувати при створенні складового документа. Головною гідністю OLE була інфраструктура компонентів, що використалася для підтримки всієї безлічі сервісів. Ця інфраструктура і є СОМ.

Найфундаментальнішим аспектом програмування СОМ є написання програми-клієнта, яка викликає об’єкти СОМ. Фактично, в середовищі Microsoft COM настільки поширена, що багато програмістів використовують СОМ, не реалізовуючи її. Так, Visual Basic побудований на основі СОМ. При використовуванні різних управляючих елементів Windows у формі ви також не використовуєте Win32 безпосередньо, а вдаєтеся до технології ActiveX, що є “оболонкою”, що розташовується поверх низькорівневих викликів управляючого елементу. Тому, навіть якщо ви і не розробляєте об’єкти СОМ, то використовуємо їх напевно. Спочатку розберемо моделі програмування СОМ на рівні абстракції, необхідному для реалізації програм — клієнтів СОМ. Розглянемо, яким чином створюються програми — клієнти СОМ як за допомогою Visual Basic, так і за допомогою Visual C++, а також як використовувати деякі інструменти, що допомагають кращому розумінню об’єктів СОМ.

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

Сервер банківського рахунку

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

Текст програмного коду серверу розташований в Додатки, bank.cpp, а клієнти – в BankClientVb і BankClientVc. Почнемо з побудови серверу, для чого двічі клацніть на файлі bank.dsw (відкривши проект в середовищі розробки Visual C++), а потім скористайтеся командою меню Build→Build Bank.dll. Запустіть программу-клієнт на Visual Basic (обидва клієнти володіють ідентичною функціональністю

Коли ми запускаємо програму, то спочатку повинні побачити вікно повідомлення з інформацією про створення і знищення об’єкту Greet. Цей об’єкт використовується для забезпечення вітального повідомлення, що виводиться як заголовок вікна. Об’єкт Account створюється по клацанню на кнопці Create. Потім відображається початковий стан рахунку — 100. Ви можете робити внески і вилучення з рахунку і проглядати поточний стан рахунку у вікні повідомлень. Якщо ви завершуєте роботу, то повинні клацнути на кнопці Destroy. Як і у разі об’єкту Greet, для об’єкту Account також є вікно повідомлення про його створення і знищення. Ці вікна повідомлень обecnечиваются сервером з метою допомогти нам зрозуміти життєвий цикл об’єктів СОМ.

Структура СОМ-серверу

Структуру СОМ-серверу можна розглядати як джерело бібліотечних функцій, придатних до негайного використовування. Мета СОМ-серверу — забезпечити повторне застосування коду для вашої програми, подібно тому, як ви робите це при використовуванні статичних бібліотек (наприклад, бібліотеки часу виконання С) або динамічних бібліотек (таких як DLL, що є частиною Windows і додатків). Функціональність СОМ-серверу може бути описана як бібліотека типів, яка визначає класи, інтерфейси і методи.

Слідуватимемо індуктивному підходу у вивченні структури СОМ-серверу. Аналізуючи сервер банківського рахунку, використовуватимемо певний інструментарій, а також розглянемо суті типу “клас” і “метод”. Розглянувши приклад серверу, системніше підійдемо до термінології і концепцій СОМ, після чого розберемо просту модель програмування програм-клієнтів, що викликають функції СОМ-серверу, і перейдемо до практичної її реалізації за допомогою мов програмування Visual Basic і Visual C++.

У Visual Basic вбудований інструмент проглядання структури сом-серверів, званий Object Browser, який може бути викликаний в меню View. Відкрийте проект BankClientvb і викличте Object Browser. У випадному списку виберіть BANKLib – бібліотеку типів для нашого прикладу серверу банківського рахунку. Виберіть метод Deposit класу Account. Після цього на нижній панелі буде представлений опис параметрів цього методу.

Object Browser відображає класи. Бачимо, що СОМ-сервер, як описано в його бібліотеці типів, реалізує класи. При виборі класу відображаються його методи і властивості. Метод можна розглядати як функцію, а властивості – як дані. Наприклад, клас Greet має властивість Greeting, а клас Account — методи Deposit, Withdraw і GetBalance. Метод, що повертає дані, може бути реалізований як властивість. Коли вибирається певний метод, Object Browser відобразить нам “підпис” методу, вказуючи параметри і їх типи даних. Наприклад, метод Deposit одержує один параметр типа Long. На жаль, Object Browser не указує, як саме передаються параметри, наприклад, як ByVal або ByRef.

Перегляд у вигляді дерева в лівій панелі – це зручний шлях для роботи з бібліотекою. Відповідний код IDL (Interface Definition Language — Мова визначення інтерфейсу), права панель, надає детальні специфікації. Якщо вам цікаво, як виглядатиме велика бібліотека типів, — відкрийте одну з об’єктних бібліотек Office, наприклад, таку як Msword8.olb (Microsoft Word) або бібліотеку ADO msadol5.dll

Мова визначення інтерфейсу (Interface Definition Language — IDL) є мовою для точної специфікації СОМ-серверу. Він не містить ніякої реалізації — це тільки специфікація, подібна заголовному файлу в програмі мови С або C++. Пізніше, при створенні СОМ-серверу, ми працюватимемо з IDL. Ми розглянемо IDL код для прикладу серверу банківського рахунку. При виборі коріння показаного дерева для BANKLib (Bank 1.0 Type Library) на правій панелі з’явиться повний IDL-код. Весь IDL-файл приведений нижче. Зараз немає необхідності розуміти все, що в ньому приведене. Деякі цікаві фрагменти цього файлу виділені напівжирним шрифтом.

// Generated .IDL file (by the OLE/COM Object Viewer)

//

// typelib filename: bank.dll

uuid(OFFBDAAl-FCA7-HD2-8FF4-00105AA45BDC),

version(1.0),

helpstring(“Bank 1.0 Type Library”)

]

Library BANKLib

//TLib: OLE Automation:

{00020430-0000-0000-COOO-000000000046}

importlib(“STDOLE2.TLB”);

// Forward declare all types defined in this typelib

interface lAccount;

interface IDisplay;

interface IGreet;

[

uuid(OFFBDAAE-FCA7-llD2-8FF4-00105AA45BDC)

helpstring(“Account Class”)

]

coclass Account {

[default] interface lAccount;

interface IDisplay;

};

[

odl,

uuid(OFFBDAAD-FCA7-llD2-8FF4-00105AA45BDC),

helpstring(“lAccount Interface”)

]

interface lAccount: lUnknown {

[helpstring(“method Deposit”)]

HRESULT _stdcall Deposit([in] int amount);

[helpstring(“method GetBalance”)]

HRESULT _stdcall GetBalance([out] int* pBalance)

[helpstring(“method Withdraw”)]

HRESULT _stdcall Withdraw([in] int amount);

};

[

odl,

uuid(42135DOO-2F41-llDl-AOlB-OOA024D06632),

helpstring(“IDisplay Interface”)

]

interface IDisplay: lUnknown {

[helpstring(“method Show”)]

HRESULT _stdcall Show();

};

[

uuid (7A5E6E82-3DF8-llD3-903D-00105AA45BDC)

helpstring(“Greet Class”)

]

coclass Greet {

[    default] interface IGreet;

};

[

odl,

uuid(7A5E6E81-3DF8-llD3-903D-00105AA45BDC),

helpstring (“IGreet Interface”)

]

interface IGreet: IUnknown {

[propget, helpstring(“property Greeting”)]

HRESULT _stdcall Greeting([out, retval] BSTR* pVal);

};

};

Спочатку слідує визначення самої бібліотеки типу, яке виконується трьома різними шляхами. Основний шлях ідентифікації будь-якого об’єкту в СОМ полягає у використовуванні універсально унікального ідентифікатора (universally unique identifier-UUID), який іноді називають глобально унікальним ідентифікатором (globally unique identifier- GUID). Гарантується повсюдна унікальність цієї 128-бітової величини, чим знімається проблема перетину імен. Проте для представлення інформації користувачу слід скористатися більш “дружнім” ім’ям, яке задається рядком helpstring (“Bank 1.0 Type Library”) і використовується для уявлення в програмах перегляду. Ви можете побачити, як це довге ім’я використовується в Visual Basic в діалоговому вікні References, яке викликається за допомогою команди меню Project→ References.

Якщо ми створюємо программу-клієнт СОМ на Visual Basic, то перше, що потрібно зробити, — додати посилання на всі сом-сервери, які використовуватимете.

Третє ім’я — то, під яким бібліотека відома програмі і яке виводиться в Object Browser. Ім’я бібліотеки використовується в програмі при необхідності дозволу неоднозначності імен. Припустимо, наприклад, що ми використовуємо дві різні бібліотеки, в кожній з яких є клас Account. Тоді для того, щоб однозначно іменувати цей клас з бібліотеки BANKLib, використовується ім’я BANKLib. Account.

Наявність трьох імен, як в даному випадку, — загальне правило в СОМ. Мі можемо розглядати UUID як “машинне ім’я”; довге ім’я, що задається оператором helpstring, — як “призначене” для користувача ім’я, а третє ім’я — як “програмне”. Ви ніколи не переплутаєте UUID з іншими іменами, але будьте обережні і не використовуйте друге ім’я замість третього або навпаки!

Далі йде визначення сом-класу, або “сокласса” (coclass). Їх в одній бібліотеці може бути декілька. Кожен клас також іменується трьома способами. Спочатку йде машинне ім’я, або UUID. У разі класу цей параметр часто називають ідентифікатором класу (CLSID). Потім слідує призначене для користувача ім’я (у нашому випадку — “аccount Class”) і ім’я, використовуване в програмі (“Account”).

Окрім класів, IDL визначає те, які інтерфейси підтримує клас. Інтерфейс є чимось специфічним для СОМ. У мовах, подібних C++, немає групування методів класу. Але в СОМ ми говоримо не про методи класу, а про методи інтерфейсу. Споріднені методи групуються в інтерфейси. Такий підхід дає велику логічну організацію підтримуваній класом функціональності. Клас Account підтримує два інтерфейси — IAccount і IDisplay. Зверніть увагу на угоду про імена, згідно якому імена інтерфейсів починаються з букви “I”.

Перший інтерфейс, IAccount, розроблений як інтерфейс за умовчанням. У програмі на Visual Basic ви дістаєте доступ до методів інтерфейсу за умовчанням, посилаючись на сам клас.

Наступні специфікації є точними “підписами” методів, що визначають кожен параметр, його тип даних і те, є параметр вхідним або виходним (або і тим, і іншим).

    Для властивостей існує спеціальний запис. Так, інтерфейс IGreet має одну властивість “тільки для читання” на ім’я Greeting.

Наш приклад серверу банківського рахунку має одну бібліотеку типів (як і все СОМ-серверу). Є два класа – Account і Greet. Клас Account підтримує два інтерфейси – IAccount (з трьома методами) і IDisplay (з одним методом), а клас Greet – один інтерфейс, IGreet, з однією властивістю.

Термінологія і концепції СОМ

Сподіваюся, наш розгляд прикладу серверу банківського рахунку з аналізом IDL став хорошим стартом для розуміння структури СОМ-серверу. Дамо короткий огляд термінології і ключових концепцій СОМ. Врахуйте, що є деяка несумісність і плутанина у використовуванні термінів в літературі про СОМ. Термін “об’єкт” іноді використовується там, де слід використовувати термін “клас”; те ж саме відбувається з терміном “компонент”.

Найфундаментальніша концепція в СОМ — це концепція інтерфейсів. Інтерфейс можна розглядати як точно певний контракт між сервером і його клієнтами. Будучи одного разу визначеним, інтерфейс повинен залишатися незмінним впродовж всього свого існування. Якщо зміни украй необхідні, слід визначити новий інтерфейс, але не змінювати старий. Інтерфейс складається з групи методів. Метод можна трактувати як функцію з параметрами певних типів Інтерфейс описується за допомогою мови визначення інтерфейсу (Interface Definition Language — IDL). Ось приклад IDL-коду для інтерфейсу lAccount.

interface lAccount: lUnknown {

HRESULT Deposit ([in] int amount);

HRESULT GetBalance ( [out] int* pBalance) ;

HRESULT Withdraw ( [in] int amount);

};

hresult є стандартним типом, що повертається, для методів інтерфейсу і указує код помилки або код успішного завершення операції. Програми Visual Basic не працюють з hresult безпосередньо. (У IDL, показаному OLE/COM Object Viewer і приведеному вище, є також параметр stdcall, який є стандартною угодою про виклик, визначаюче, яким чином аргументи передаються в стек, і т.п. Це директива для компілятора, і оскільки _stdcall є викликом за умовчанням, вона може бути опущена.)

Інтерфейс lUnknown

У СОМ всі інтерфейси походять від спеціального інтерфейсу lUnknown і, отже, на додаток до своїх власних методів мають три методи lUnknown. От як виглядає представлення lUnknown в IDL:

interface lUnknown {

HRESULT QueryInterface(REFIID iid, void** ppvObject);

ULONG AddRef () ;

ULONG Release () ;

};

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

Класи

Інтерфейс є абстрактною специфікацією. Інтерфейси реалізуються класами. Клас в СОМ подібний класу в об’єктно-орієнтованих мовах програмування. Клас інкапсулює дані і поведінку в єдину суть. Не маючи класів, повинні підтримувати величезну кількість змінних і передавати їх як параметри ізольованим функціям. Групуючи споріднені дані і функції в клас, ми створюємо абстракцію, що спрощує програмування і дозволяє розширити програмну модель нашими власними типами даних.

СОМ розширює цю програмну модель, дозволяючи класу мати декілька інтерфейсів, кожний з яких підтримує певні властивості за допомогою груп взаємозв’язаних функцій. Наприклад, в нашому сервері банківського рахунку клас Account реалізує інтерфейси lAccount і iDisplay, а також lUnknown, який підтримується будь-яким СОМ-об’єктом. . Клас Account реалізує три інтерфейси


lAccount

Account

1 —

IDisplay


lUnknown

1 —


Об’єкт є екземпляром класу. Клас можна розглядати як код, який забезпечує можливості, описувані інтерфейсами які підтримуються цим класом. Але для того, щоб виконати деяку роботу, нам необхідно створити як мінімум один екземпляр класу. Такий екземпляр об’єкту має деякі асоційовані з ним дані. Інший екземпляр об’єкту матиме інші власні дані. Розглянемо два одночасно працюючі екземпляра. програми-клієнта (для запуску двох екземплярів програми Visual Basic можна або запустити дві сесії Visual Basic). За допомогою кожного з екземплярів ми можемо створити об’єкт. У нашому прикладі новий об’єкт завжди починає роботу з балансом 100. Перший об’єкт може зробити два внески, а другий — двічі зняти гроші з рахунку. Зрозуміло, що ми маємо два різні екземпляри об’єкту, кожний з яких містить власні дані.

Створення екземпляра об’єкту

Програма-клієнт повинна мати нагоду створювати екземпляри об’єктів класу. Система часу виконання СОМ забезпечує функцію API CoCreateInstanceEx (або простішу і старішу функцію CoCreateInstance), яка може безпосередньо використовуватися в програмі на С++. Програма на Visual Basic може використовувати оператора New або функцію CreateObject. Ми розглянули всі три способи, оскільки вони приводять до розуміння іншого важливого питання, а саме: « Яким чином ідентифікуються класи і інтерфейси?»

Дуже важливим є також те, що створення екземпляра приводить не до посилання на об’єкт, а до посилання на певний інтерфейс. Ця відмінність має дуже важливе значення і може привести до плутанини — зокрема, в Visual Basic, де, як ми незабаром побачимо, не є безпосередньої підтримки інтерфейсів.

CoCreatelnstance (C++)

Спочатку давайте розглянемо процес створення екземплярів об’єктів в C++. Це приведе нас набагато ближче до розуміння того, як працює СОМ (в порівнянні із спрощеннями Visual Basic). Для простоти ми розглянемо старішу функцію -CoCreatelnstance. Пізніше, при розгляді DCOM, я поясню, як працює сучасна функція — CoCreatelnstanceEx.

Нижче приведений код для створення екземпляра об’єкту класу Account. (Повний код ви зможете знайти у функції CBankClientDlg::OnCreateObject проекту BankClientVc. Покажчик на інтерфейс описаний у файлі BankClientDlg.h.)

lAccount* m_pAccount;

HRESULT hr;

hr = CoCreatelnstance(CLSID_Account, NULL, CLSCTX_SERVER,

IID_IAccount, (void**) &m_jpAccount) ;

Зверніть увагу на те, що клас визначається ідентифікатором класу, а інтерфейс — ідентифікатором інтерфейсу.

New (Visual Basic)

Перший шлях створення екземпляра об’єкту в Visual Basic полягає у використовуванні оператора New. Спочатку ви повинні додати посилання на бібліотеку типу (пункт меню Project>References). При цьому буде викликана бібліотека ВАNKLib, що включає клас Account. Наступний код, узятий з проекту BankClientvb, ілюструє створення екземпляра об’єкту. Зверніть увагу на те, що клас визначається ім’ям.

Dim objAccount As Account

Set objAccount = New Account

ВVisual Basic мовиться, що ми одержуємо “посилання на об’єкт” (object reference) класу Account- Проте така термінологія невірна, оскільки, як ми бачили, COM працює з посиланнями на інтерфейси. Пригадаємо про те, що при розгляді IDL для серверу банківського рахунку lAccount був інтерфейсом класу Account за умовчанням. Це означає, що посилання на об’єкт Account насправді є посиланням на інтерфейс lAccount. Коли ми розглянемо повний код клієнта Visual Basic, то побачимо, як одержати і використати другий інтерфейс за допомогою Visual Basic.

CreateObject (Visual Basic)

Другий шлях створення екземпляра об’єкту в Visual Basic полягає у використовуванні функції CreateObject. Цей спосіб є первинним шляхом створення екземплярів об’єктів в Visual Basic і залишається єдиним способом створення об’єкту VBScript. Спочатку Visual Basic використовував тільки спеціальний вигляд СОМ, відомий як “автоматизація” (Automation), і міг обмінюватися інформацією тільки з серверами СОМ, чиї класи забезпечували диспетчерські інтерфейси. Автоматизація менш ефективна, ніж безпосередня робота з СОМ, але і більш гнучка, оскільки підтримує пізнє скріплення, при якому вид класу визначається під час роботи (вимога, істотна для мов сценаріїв в таких додатках, як Internet Explorer, коли немає можливості знати наперед про те, якого типу об’єкти можуть бути використані на HTML-сторінках).

Виявляється, що функція CreateObject може також забезпечувати раннє скріплення, якщо посилання на бібліотеку типів додається до проекту, як ми поступили вище. Нижченаведений код, також одержаний з проекту BankClientVB, створює екземпляр об’єкту класу Greet. Помітьте, що клас визначається ідентифікатором програми.

Dim objGreet As Greet

Set objGreet = CreateObject (“Bank. Greet.1”)

Приведені приклади проілюстрували застосування різних типів ідентифікаторів, використовуваних в СОМ. Тепер ми можемо пояснити, що є кожним з них.

Глобально унікальний ідентифікатор (GUID)

    Мови програмування використовують змінні, класи і інші елементи мов з іменами, придатними для читання користувачем. Конфлікт імен – явище можливе, але цілком вирішуване в рамках одного проекту. Класи COM реалізують бінарні компоненти, які повинні бути унікальні для дуже широкої області. Інтерфейси і інші елементи СОМ вимагають ідентифікаційного механізму, який міг би попередити наявність конфліктів імен. Distributed Computing Environment (DCE) з Open Software Foundation надало рішення у вигляді концепції універсально унікального ідентифікатора (universally unique identifier-UUID, який є 128-бітовою величиною, яка може алгоритмічно згенерувати так, щоб гарантувати віртуальну унікальність. Microsoft адаптувала цей механізм для СОМ, назвавши такий ідентифікатор менш “грандіозно” — глобально унікальним ідентифікатором (globally unique identifier – GUID).

Для різних елементів СОМ є різні типи GUID. Так, ми вже зустрілися з GUID для бібліотеки типів, класу (CLSID) і інтерфейсу (IID).

Програмуючи на C++, можемо безпосередньо звертатися до цих GUID у вашому коді. Звичайно для звернення до GUID використовуються явно визначені в заголовних файлах константи. У високорівневому оточенні, такому як Visual Basic, ваша програма користуватиметься легкими для читання іменами, але середовище Visual Basic застосовуватиме відповідні GUID при зверненні до COM від вашого імені.

Ідентифікатор програми (ProgID) тісно пов’язаний з CLSID. ProgID є рядком, який може використовуватися як заступник CLSID. ProgID часто має вид application.class або application.class.N, де application — певний додаток або сервер, а class — деякий клас, реалізовуваний цим сервером. До цього може також бути доданий номер версії. Прикладом ProgID може служити Bank. Greet.1.

IDL для серверу містить оператора coclass, використовуваного для того, щоб дати ім’я соклассу (класу СОМ). Двома соклассами в нашому прикладі серверу банківського рахунку є Account і Greet.

coclass Account {

[default] interface lAccount; interface IDisplay/ }; coclass Greet {

s28[default] interface IGreet; };

Visual Basic використовує ім’я сокласса, коли описуємо посилання на об’єкт за допомогою оператора Dim або New.

Всі згадувані вище ідентифікатори використовуються для програмування. Кінцевий користувач ніколи не зустрічається ні з GUID, ні з ProgID або ім’ям сокласса. Але, проте, існують елементи СОМ, які можуть бути показані кінцевому користувачу. Чудовим прикладом можуть служити класи складового документа в OLE. Кінцевий користувач клієнта OLE (або контейнера), такого як Microsoft Word, може вставляти в документ “об’єкти”, створені іншими додатками. Для цього використовується команда меню In-sert→Object, в діалоговому вікні, що з’являється, відображаються всі класи OLE системи. Імена, що відображаються в цьому діалоговому вікні, є “призначеними” для користувача іменами класів (іноді на зиваємиє типами об’єктів). Різні призначені для користувача імена створюються за допомогою оператора helpstring в IDL.

Час життя об’єктів

Однією з властивостей СОМ є те, що в управлінні часом життя об’єктів беруть участь як клієнт, так і сервер. Ніхто з них не в змозі управляти часом життя об’єкту самостійно. Ясно, що сервер не має права видалити об’єкт, поки клієнт з ним працює, тому клієнт повинен повідомити сервер про припинення роботи з об’єктом. Але це не означає, що сервер може видалити об’єкт, тому що з ним може працювати інший клієнт. Рішення полягає у тому, що сервер підтримує лічильник посилань для кожного об’єкту. Базовий інтерфейс I Unknown надає два методи управління лічильником посилань. AddRef збільшує його. Цей метод викликається при створенні екземпляра об’єкту, тому лічильник починає роботу із значення 1. Коли клієнт копіює посилання на інтерфейс, він повинен викликати AddRef, оскільки тепер на об’єкт є ще одне посилання. Після закінчення роботи з покащиком на інтерфейс клієнт викликає метод Release. Коли всі клієнти звільнять всі свої посилання на об’єкт, лічильник посилань стане рівним 0, і сервер зможе безпечно Видалити об’єкт.

Час життя об’єкту в Visual Basic

Одна з приємних властивостей Visual Basic полягає у тому, що у багатьох випадках управління часом життя об’єкту відбувається автоматично, не вимагаючи від вас додаткового коду. Якщо оператор Dim оголошує посилання на об’єкт як локальну змінну в процедурі або функції, то посилання на об’єкт буде звільнене при виході з області видимості цієї процедури або функції.

Якщо у вас є посилання на об’єкт в глобальній області видимості, то ви можете управляти нею самостійно, звільняючи її привласненням значення Nothing. Так, програма-клієнт на Visual Basic BankClientVb реалізує обробник кнопки Destroy таким чином:

Dim objAccount As Account ‘ глобальна одласть видимості

Private Sub cmdDestroy_Client()

Set objAccount= Nothing

txtBalance=” ”

End Sub

Узгодження інтерфейсів

Той факт, що клас СОМ може підтримувати декілька інтерфейсів означає, що є механізм, призначений для того, щоб клієнт, що має посилання на один інтерфейс, міг одержати посилання і на інший інтерфейс цього класу. Отримання іншого покажчика на інтерфейс виконується за допомогою процесу, відомого як “узгодження інтерфейсів”, і використовування третього методу lUnknown, а саме QueryInterface.

HRESULT Querylnterface(REFIID iid, void** ppvObject);

Перший параметр використовується для передачі ідентифікатора необхідного інтерфейсу; другий — для отримання покажчика на інтерфейс, якщо останній підтримується. Якщо запрошуваний інтерфейс не підтримується, значення hresult, що повертається, указує на помилку.

Виклик Querylnterface з Visual C++

Програма на Visual C++ викликає Querylnterface безпосередньо. Як приклад взглянім на реалізацію OnShow.

void CBankClientDlg::OnShow() {

//Запит другого інтерфейсу

HRESULT hr =

m_pAccount->QueryInterface(IID_IDisplay

(void**)&m_pDisplay);

if (FAILED(hr)) {

MessageBox(“Querylnterface failed”);

return; I

if (!m_pDisplay) return;

hr = m_pDisplay->Show();

if (FAILED(hr)) {

MessageBox(“Show failed”);

return; }

m__pDisplay->Release () ;

m_pDisplay = NULL;

Використовування Querylnterface в Visual Basic

Програма на Visual Basic не викликає Querylnterface безпосередньо. Насправді Visual Basic навіть не показує, що він працює з інтерфейсами, які в Visual Basic представлені як класи. Коли Visual Basic звертається до бібліотеки типу, для кожного інтерфейсу він відображає клас. Інтерфейс за умовчанням призначається класу з ім’ям, відповідним соклассу. Таким чином, класом Visual Basic, відповідним інтерфейсу lAccount, є клас Account. Для інших інтерфейсів Visual Basic призначає імена класів, такі ж, як і імена інтерфейсів. Отже, класом Visual Basic, відповідним інтерфейсу iDisplay, буде простий клас iDisplay. Тому код Visual Basic для роботи з Querylnterface дуже простий. Ви описуєте посилання для необхідного інтерфейсу і виконуєте привласнення початкового інтерфейсу. Ось код для обробки кнопки Show:

Private Sub cmdShow_Click()

if Not objAccount is Nothing Then

Dim ifcDis.play As IDisplay

Set ifcDisplay = objAccount

ifcDisplay.Show

End If

End Sub

Помітьте, що в цьому коді є невелика складність: замість того щоб просто привласнити значення новому посиланню, спочатку перевіряється коректність значення objAccount (тобто що воно не рівне Nothing).

За традицією ми іменуємо посилання на інтерфейс за умовчанням як objAccount і говоримо про неї, як про посилання об’єкт. Такі “розкаяння совісті” за невірне іменування у разі інтерфейсу IDisplay нас не мучать, оскільки приставка if з ясно показує, що змінна є посиланням на інтерфейс.

Сервер

Тепер опишемо, що є сервером в СОМ. Це — просто програмний модуль (у Windows це — DLL або ЕХЕ), який забезпечує виконуваний код для одного або декількох класів. Зверніть увагу на ієрархію, що використовується при цьому. Сервер може реалізовувати декілька класів, клас — підтримувати декілька інтерфейсів, інтерфейс — мати декілька методів. На рис показана загальна структура нашого прикладу серверу банківського рахунку (для простоти інтерфейс lunknown не показаний).

Ієрархічна структура серверу банківського рахунку

Бібліотека типів

Код серверу розташовується в програмному модулі, який звичайно є DLL. Окрім самого коду, СОМ інтенсивно використовує опис коду. Цей опис називається інформацією про тип (type information) і зберігається у вигляді бібліотеки типів (type library). Бібліотека сама по собі має GUID, ім’я (BANKLib — в нашому прикладі) і призначене для користувача ім’я (“Bank 1.0 Type Library”). Бібліотека зберігає описи класів, реалізовуваних сервером; інтерфейсів, підтримуваних класами; методів інтерфейсів і точну інформацію про параметри і типи даних цих методів. Визначається також інтерфейс кожного класу за умовчанням.

Програмна модель клієнта СОМ

Тепер ми можемо описати програмну модель клієнта СОМ. (Примітка по термінології: у C++ ми посилаємося на інтерфейси, використовуючи покажчики. Отже далі ми використовуватимемо нейтральний термін “посилання на інтерфейс”, а для інтерфейсу за умовчанням в Visual Basic — “посилання на об’єкт”. Переходячи до питань дійсного кодування, в розмові про C++ ми використовуватимемо термін “покажчик на інтерфейс”.) Існує шість основних кроків, які ви повинні виконати. Залежно від використовуваного вами програмного оточення деякі з поставлених задач можуть розв’язуватися автоматично.

  1. Ініціалізувати систему часу виконання СОМ. Для цього COM API надає можливість виклику Colnitialize (або CoInitializeEx). MFC-додатки можуть викликати AfxOleinit. Visual Basic виконує ініціалізацію автоматично.
  2. Одержати первинне посилання (або покажчик) на інтерфейс. У C++ це робиться за допомогою виклику CoCreatelnstance або CoCreatelnstanceEx. У Visual Basic використовується для цього оператор New або виклик CreateObject.
  1. За допомогою покажчика на інтерфейс викликати методи інтерфейсу.
    1. Якщо вам потрібен виклик методів іншого інтерфейсу, виконати Querylnterface. У C++ ви викликаєте Querylnterface за допомогою покажчика на інтерфейс, а в Visual Basic — за допомогою операції привласнення.
    2. Після роботи з покажчиком на інтерфейс в C++ викликати Release. У Visual Basic ви або виходите з області видимості природним чином, або явним чином привласнюєте відповідній змінній значення Nothing.
    3. Після закінчення роботи з СОМ дєініциалізувати СОМ шляхом виклику CoUninitialize. Цей крок виконується автоматично в Visual Basic і MFС.

Консольна програма-клієнт на Visual C++

Розглянемо тепер програму BankConsoleVc, що створює консольний додаток Win32. У нашій програмі C++ ми не працюватимемо з бібліотеками типу, а одержимо всю необхідну інформацію із заголовного файлу і файлу коду проекту. Наша програма має файли Bank.h і Bank_i.c, скопійовані з проекту серверу. Файл Bank.h містить описи інтерфейсів lAccount, IGreet. Даний файл згенерує за допомогою компілятора MIDL RPC. Цей файл необхідно включити в проект при використовуванні покажчиків на інтерфейси. Другий файл, Bank_i.c, містить визначення GUID. Він повинен бути включений тільки в один модуль компіляції і, отже, не повинен включатися в заголовний файл (якщо ви зробите це, то можете одержати повідомлення про помилку множинного визначення ідентифікатора). Оскільки цей проект має тільки один файл з початковим кодом, ми включимо обидва файли — і Bank.h, і Bank_i.c — в BankConsole. срр. Ключовий заголовний файл для включення сом-бібліотеки наступний: objbase.h.

Повний код приведений нижче. Один з нюансів полягає в обробці символьних рядків. Всі рядки в СОМ є рядками Unicode, а деякі рядки — окремий випадок рядків Unicode, відомий як BSTR. Клас Greet повертає рядок вітання як BSTR, яка повинна бути конвертована.

// BankConsole.срр

#include <stdio.h>

#include <objbase.h>

#include “bank.h”

#include “bank_i.c”

#include <comdef.h>

int main(int argc, char* argv[])

// Ініціалізація COM

HRESULT hr = CoInitialize(NULL);

if (FAILED(hr))

printf (“Colnitialize failedXn”);

return 0;}

// Створення екземпляра об’єкту Greet,

// отримання покажчика на інтерфейс

IGreet* pGreet;

hr = CoCreatelnstance(CLSID_Greet, NULL, CLSCTX_SERVER,

IID_IGreet, (void **) &pGreet);

if (FAILED(hr))

{

printf(“CoCreatelnstance failedXn”);

return 0;

//виведення вітання і звільнення BSTR bstr;

hr= pGreet->get_Greeting(&bstr);

if “(FAILED (hr))

{ printf(“get_Greeting failedXn”);

return 0;

} else

{ _bstr_t greeting(bstr);    i

printf(“%s\n”, (const char*Г greeting);

pGreet->Release();

}

// Створення екземпляра об’єкту Account,

// отримання покажчика на інтерфейс

lAccount* pAccount;

hr = CoCreatelnstance(CLSID_Account, NULL,

    CLSCTX_SERVER, IID_IAccount,

(void **) &pAccount);

if (FAILED(hr))

{

printf(“CoCreatelnstance failedXn”);

return 0;

}

// Використовування покажчика на інтерфейс

// для виклику методу

// Отримання і виведення початкового балансу

int balance;

hr = pAccount->GetBalance(&balance);

if (FAILED(hr))

{

printf(“GetBalance failedXn”);

pAccount->Release();

return 0;

}

printf(“balance = %d\n”, balance);
// Депозит 25

hr = pAccount->Deposit(25);

if (FAILED(hr))

printf (“Deposit failedXn”);

pAccount->Release () ; return 0;}

// Отримання інформації про баланс

hr = pAccount->GetBalance(&balance);

if (FAILED(hr))

printf(“GetBalance failedXn”);

else

printf(“balance = %d\n”, balance);

// Запит IDisplay, виклик Show і звільнення

IDisplay* pDisplay;

hr = pAccount->QueryInterface (IID__IDisplay,

(void **) &pDisplay)

if (FAILED(hr))

{

printf(“Querylnterface failed\n”);

pAccount->Release() ;

return 0;

}

 

hr = pDisplay->Show() ; if (FAILED(hr))

printf(“Show failed\n”);

else

pDisplay->Release() ;

pAccount->Release() ;

return 0;

}

Висновок

СОМ не є проміжним програмним забезпеченням (middleware). COM забезпечує механізм для зв’язку клієнта і серверу. Результатом цього є виключно гнучка архітектура компонентів, яка може використовуватися як для дуже швидких компонентів, що є вдосконаленням DLL, так і для серверів в мережі (у обох випадках застосовується одна і та ж модель). Проте для складних додатків рівня підприємства є міріади випадків, коли особливу цінність представляє саме проміжне програмне забезпечення. Чудовим прикладом може служити ODBC, і це — тільки вершина айсберга. Складна обробка даних, яка повинна виконуватися для реалізації розподілених додатків, включає обробку транзакцій, питання узгодженості безпеки, черг повідомлень, повідомлення про події і багато чого іншого. Дуже дорого реалізовувати все це для кожного додатку окремо, Отже, цю роль повинні зіграти системні сервіси.

Геніальність СОМ/СОМ+ полягає у тому, що вона надає архітектуру, так зване перехоплення, дозволяючи їй втручатися в роботу додатків тільки при необхідності, а не постійно. Компоненти СОМ+ працюють у тому, що відомо під назвою контекст. Контекст можна розглядати як набір обмежень часу виконання. Якщо компонент і його клієнт запускаються в одному і тому ж контексті виклик методу відбувається безпосередньо, без втручання СОМ+ і без яких би те ні було накладних витрат. Якщо ж вони запущені в різних контекстах, виклик пройде через “перехоплювач”, який зробить все необхідне для задоволення обмеженням часу виконання. Так само і повернення буде здійснене із залученням перехоплювача для виконання необхідних дій. В результаті ми одержуємо проміжне програмне забезпечення, яке вступає в гру тоді і тільки тоді, коли це необхідно.

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

При написанні СОМ-серверу і клієнта розглядалося безліч фундаментальних питань, які можуть бути розділені на дві основні групи. До першої групи відносяться питання, пов’язані з розробкою клієнтів СОМ за допомогою Visual Basic і Visual C++. Друга група – це питання, що стосуються розуміння того, що і як при цьому робиться. Найважливіший матеріал, який слідує запам’ятати, — базова програмна модель клієнта, що викликає СОМ-сервер. Ми повинні створити екземпляр об’єкту СОМ-класу, що в C++ можна зробити, викликавши CoCreatelnstance[Ex], а в Visual Basic— оператора New або функцію Createobject. При цьому ми одержимо посилання на інтерфейс. За допомогою цього посилання (покажчика в C++) можем викликати методи. Після закінчення роботи посилання слід звільнити, викликавши Release в C++; у Visual Basic звільнення відбувається або при виході області видимості, або шляхом безпосереднього привласнення глобальному посиланню Nothing. СОМ-клас може підтримувати декілька інтерфейсів, доступ до яких забезпечує механізм Querylnterfасе.

Література

  1. Архангельский А.Я. C++ Builder 6. Справочное пособие. Кн. 2: Классы и компоненты. – М.: Бином, 2002.– 528 с.
  2. Баженова И.Ю. Visual C++ 6. Уроки программирования.- М.: Диалог-МИФИ, 1999. – 416 с.
  3. Жарков В.А. Visual C#. NET в науке и технике. – М.: Жарков Пресс, 2002, –638 с.
  4. Климова Л.М. C++ практическое программирование. Решение типовых задач. – М.: Кудиц-образ, 2001. – 592 с.
  5. Лафоре Р. Объектно-ориентированное программирование в C++. Классика Computer Science. – С.-П.: Питер, 2002. – 928 с.
  6. Либерти Дж Освой самостоятельно С++ за 21 день 4-е изд. –С.-П. :Вильямс, 2001. – 832 с.
  7. Либерти Дж. C++. Энциклопедия пользователя. – С.-П.: ДиаСофт, 2000. – 584 с.
  8. Майерс Н. Наиболее эффективное использование C++. – М.:ДМК, 2000. – 304 с.
  9. Марченко А. Л. C++. Бархатный путь. 2-е изд. . – М.:Горячая линия, 2000. – 400 с.
  10. Павловская Т.А. Щупак Ю.А. C/C++. Структурное программирование. Практикум. – С.-П.: Питер, 2002. – 240 с.
  11. Павловская Т.А. C/C++. Программирование на языке высокого уровня. – С.-П.: Питер, 2001. 464 с.
  12. Секунов Н.О. Visual C++ 6.Самоучитель, BHV–Санкт-Петербург, 1999.– 960 с.
  13. Черносвитов А. Visual C++ 7: учебный курс. – С.-П.: Питер, 2001. – 528 с.
  14. Шишков С.Л. C++ Начала программирования . – М.: Бином, 2000. – 302 с.
  15. Браунси Кен Основные концепции структур данных и реализация в C++. –С.-П.:Вильямс, 2002. –320 с.
  16. Дейтел Х. Как программировать на С++. – М.: Бином, 2000. – 1024 с.
  17. Пол Айра Объектно-ориентированное программирование на C++.- М.: Бином, 1999, – 462 с.
  18. Роберт Дж. Оберг СОМ+ технология основы и программирование.:Пер.с англ.:Уч.пос.–М.”Вильямс”.–2000.–480с.
  19. Том Арчер, Эндрю Уайтчепел, Visual C++ .NET. Библия пользователя. – С.-П. Диалектика/Вильямс, 2003. – 1216 с.
  20. Франка П. C++ Учебный курс. – С.-П. Питер, 1999. – 528 с.
  21. Холлингвэрт Дж. C++Builder 5. Руководство разработчика. Т.2 Сложные вопросы программирования. – С.-П. Вильямс, 2001. – 832 с.
  22. Шуман Х.С. C++ для детей. – С.-П.: Интерэксперт, 2002. – 416 с.

Додатоки

Bank.cpp

// Bank.cpp : Implementation of DLL Exports.

// Note: Proxy/Stub Information

// To build a separate proxy/stub DLL,

// run nmake -f Bankps.mk in the project directory.

#include “stdafx.h”

#include “resource.h”

#include <initguid.h>

#include “Bank.h”

#include “Bank_i.c”

#include “Account.h”

#include “Greet.h”

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)

OBJECT_ENTRY(CLSID_Account, CAccount)

OBJECT_ENTRY(CLSID_Greet, CGreet)

END_OBJECT_MAP()

/////////////////////////////////////////////////////////////////////////////

// DLL Entry Point

extern “C”

BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)

{

if (dwReason == DLL_PROCESS_ATTACH)

{

_Module.Init(ObjectMap, hInstance, &LIBID_BANKLib);

DisableThreadLibraryCalls(hInstance);

}

else if (dwReason == DLL_PROCESS_DETACH)

_Module.Term();

return TRUE; // ok

}

/////////////////////////////////////////////////////////////////////////////

// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)

{

return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;

}

/////////////////////////////////////////////////////////////////////////////

// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)

{

return _Module.GetClassObject(rclsid, riid, ppv);

}

/////////////////////////////////////////////////////////////////////////////

// DllRegisterServer – Adds entries to the system registry

STDAPI DllRegisterServer(void)

{

// registers object, typelib and all interfaces in typelib

return _Module.RegisterServer(TRUE);

}

/////////////////////////////////////////////////////////////////////////////

// DllUnregisterServer – Removes entries from the system registry

STDAPI DllUnregisterServer(void)

{

return _Module.UnregisterServer(TRUE);

}

BankClientVc

// BankClient.cpp : Defines the class behaviors for the application.

//

#include “stdafx.h”

#include “BankClient.h”

#include “BankClientDlg.h”

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CBankClientApp

BEGIN_MESSAGE_MAP(CBankClientApp, CWinApp)

    //{{AFX_MSG_MAP(CBankClientApp)

        // NOTE – the ClassWizard will add and remove mapping macros here.

        // DO NOT EDIT what you see in these blocks of generated code!

    //}}AFX_MSG

    ON_COMMAND(ID_HELP, CWinApp::OnHelp)

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CBankClientApp construction

CBankClientApp::CBankClientApp()

{

    // TODO: add construction code here,

    // Place all significant initialization in InitInstance

}

/////////////////////////////////////////////////////////////////////////////

// The one and only CBankClientApp object

CBankClientApp theApp;

/////////////////////////////////////////////////////////////////////////////

// CBankClientApp initialization

BOOL CBankClientApp::InitInstance()

{

    if (!AfxOleInit())

    {

        AfxMessageBox(“Could not initialize OLE”);

        return FALSE;

    }

    // Standard initialization

    // If you are not using these features and wish to reduce the size

    // of your final executable, you should remove from the following

    // the specific initialization routines you do not need.

#ifdef _AFXDLL

    Enable3dControls();            // Call this when using MFC in a shared DLL

#else

    Enable3dControlsStatic();    // Call this when linking to MFC statically

#endif

    CBankClientDlg dlg;

    m_pMainWnd = &dlg;

    int nResponse = dlg.DoModal();

    if (nResponse == IDOK)

    {

        // TODO: Place code here to handle when the dialog is

        // dismissed with OK

    }

    else if (nResponse == IDCANCEL)

    {

        // TODO: Place code here to handle when the dialog is

        // dismissed with Cancel

    }

    // Since the dialog has been closed, return FALSE so that we exit the

    // application, rather than start the application’s message pump.

    return FALSE;

}

BankClientVb

Option Explicit

Dim objAccount As Account

Private Sub cmdCreate_Click()

Set objAccount = New Account

UpdateBalance

End Sub

Private Sub cmdDeposit_Click()

‘WARNING: We don’t check for valid object reference!!

‘See code for Withdraw and Show for correct pattern

objAccount.Deposit txtAmount

UpdateBalance

End Sub

Private Sub cmdDestroy_Click()

Set objAccount = Nothing

txtBalance = “”

End Sub

Private Sub cmdShow_Click()

If Not objAccount Is Nothing Then

Dim ifcDisplay As IDisplay

Set ifcDisplay = objAccount

ifcDisplay.Show

End If

End Sub

Private Sub cmdWithdraw_Click()

If Not objAccount Is Nothing Then

objAccount.Withdraw txtAmount

UpdateBalance

End If

End Sub

Private Sub Form_Load()

‘Note use of ProgId and CreateObject

Dim objGreet As Greet

Set objGreet = CreateObject(“Bank.Greet.1”)

Form1.Caption = objGreet.Greeting

txtAmount = 25

End Sub

Private Sub UpdateBalance()

Dim balance As Long

objAccount.GetBalance balance

txtBalance = balance

End Sub

BankClientDlg

// BankClientDlg.cpp : implementation file

//

#include “stdafx.h”

#include “BankClient.h”

#include “BankClientDlg.h”

#include “bank_i.c”

//#include <comdef.h>

#include <afxpriv.h>

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CBankClientDlg dialog

CBankClientDlg::CBankClientDlg(CWnd* pParent /*=NULL*/)

    : CDialog(CBankClientDlg::IDD, pParent)

{

    //{{AFX_DATA_INIT(CBankClientDlg)

        // NOTE: the ClassWizard will add member initialization here

    //}}AFX_DATA_INIT

    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32

    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

    m_pAccount = NULL;

    m_pDisplay = NULL;

}

void CBankClientDlg::DoDataExchange(CDataExchange* pDX)

{

    CDialog::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CBankClientDlg)

        // NOTE: the ClassWizard will add DDX and DDV calls here

    //}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CBankClientDlg, CDialog)

    //{{AFX_MSG_MAP(CBankClientDlg)

    ON_WM_PAINT()

    ON_WM_QUERYDRAGICON()

    ON_BN_CLICKED(IDC_WITHDRAW, OnWithdraw)

    ON_BN_CLICKED(IDC_SHOW, OnShow)

    ON_BN_CLICKED(IDC_DEPOSIT, OnDeposit)

    ON_BN_CLICKED(IDC_CREATE, OnCreateObject)

    ON_BN_CLICKED(IDC_DESTROY, OnDestroyObject)

    ON_WM_DESTROY()

    //}}AFX_MSG_MAP

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CBankClientDlg message handlers

BOOL CBankClientDlg::OnInitDialog()

{

    CDialog::OnInitDialog();

    // Set the icon for this dialog. The framework does this automatically

    // when the application’s main window is not a dialog

    SetIcon(m_hIcon, TRUE);            // Set big icon

    SetIcon(m_hIcon, FALSE);        // Set small icon


    // TODO: Add extra initialization here

    SetDlgItemInt(IDC_AMOUNT, 25);

    IGreet *pGreet;

    HRESULT hr;

    hr = CoCreateInstance(CLSID_Greet, NULL, CLSCTX_SERVER,

                IID_IGreet, (void **) &pGreet);

    if (FAILED(hr))

    {

        MessageBox(“CoCreateInstance failed”);

        return TRUE;

    }

    BSTR bstr;

    hr = pGreet->get_Greeting(&bstr);

    if (FAILED(hr))

        MessageBox(“get_Greeting failed”);

    else

    {

        // _bstr_t greeting(bstr);

        USES_CONVERSION;

        SetWindowText((const char*) OLE2CT(bstr));

        ::SysFreeString(bstr);

        pGreet->Release();

    }

    return TRUE; // return TRUE unless you set the focus to a control

}

// If you add a minimize button to your dialog, you will need the code below

// to draw the icon. For MFC applications using the document/view model,

// this is automatically done for you by the framework.

void CBankClientDlg::OnPaint()

{

    if (IsIconic())

    {

        CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

    // Center icon in client rectangle

    int cxIcon = GetSystemMetrics(SM_CXICON);

    int cyIcon = GetSystemMetrics(SM_CYICON);

    CRect rect;

    GetClientRect(&rect);

    int x = (rect.Width() – cxIcon + 1) / 2;

    int y = (rect.Height() – cyIcon + 1) / 2;

        // Draw the icon

        dc.DrawIcon(x, y, m_hIcon);

    }

    else

    {

    CDialog::OnPaint();

    }

}

// The system calls this to obtain the cursor to display while the user drags

// the minimized window.

HCURSOR CBankClientDlg::OnQueryDragIcon()

{

    return (HCURSOR) m_hIcon;

}

void CBankClientDlg::OnWithdraw()

{

    if (!m_pAccount)

    return;

    int amount = GetDlgItemInt(IDC_AMOUNT);

    HRESULT hr = m_pAccount->Withdraw(amount);

    if (FAILED(hr))

    {

    MessageBox(“Withdraw failed”);

    return;

    }

    UpdateBalance();

}

void CBankClientDlg::OnShow()

{

    // Query for second interface

    HRESULT hr = m_pAccount->QueryInterface(

        IID_IDisplay, (void**) &m_pDisplay);

    if (FAILED(hr))

    {

    MessageBox(“QueryInterface failed”);

    return;

    }

    if (!m_pDisplay)

    return;

    hr = m_pDisplay->Show();

    if (FAILED(hr))

    {

    MessageBox(“Show failed”);

    return;

    }

    m_pDisplay->Release();

    m_pDisplay = NULL;

}

void CBankClientDlg::OnDeposit()

{

    if (!m_pAccount)

    return;

    int amount = GetDlgItemInt(IDC_AMOUNT);

    HRESULT hr = m_pAccount->Deposit(amount);

    if (FAILED(hr))

    MessageBox(“Deposit failed”);

    else

    UpdateBalance();

}

void CBankClientDlg::UpdateBalance()

{

    if (!m_pAccount)

    return;

    int balance;

    HRESULT hr = m_pAccount->GetBalance(&balance);

    if (FAILED(hr))

    MessageBox(“GetBalance failed”);

    else

    SetDlgItemInt(IDC_BALANCE, balance);

}

void CBankClientDlg::OnCreateObject()

{

    // Use class factory to instantiate object

    HRESULT hr;

    hr = CoCreateInstance(CLSID_Account, NULL, CLSCTX_SERVER,

        IID_IAccount, (void **) &m_pAccount);

    if (FAILED(hr))

    {

    MessageBox(“CoCreateInstance failed”);

    return;

    }

    UpdateBalance();

}

void CBankClientDlg::OnDestroyObject()

{

    if (m_pAccount)

    {

    m_pAccount->Release();

    m_pAccount = NULL;

    }

    if (m_pDisplay)

    {

    m_pDisplay->Release();

    m_pDisplay = NULL;

    }

    SetDlgItemText(IDC_BALANCE, “”);

}

void CBankClientDlg::OnDestroy()

{

    if (m_pAccount)

    m_pAccount->Release();

    if (m_pDisplay)

    m_pDisplay->Release();

}

ЗАВАНТАЖИТИ

Для скачування файлів необхідно або Зареєструватись

СОМ+ (248.0 KiB, Завантажень: 0)

завантаження...
WordPress: 23.33MB | MySQL:26 | 0,390sec