- •Розподілені системи обробки інформації
- •Передмова
- •Розділ 1. Огляд компонентних технологій створення розподілених програмних систем
- •1.1. Узагальнена архітектура і механізм функціонування об'єктних розподілених систем
- •1.2. Основні приклади технологій створення розподілених систем
- •1.3. Переваги використання розподілених технологій
- •Розділ 2. Розроблення розподілених систем на основі модели com/dcom у Delphi
- •2.1. Використання dll у Delphi
- •2.1.1. Поняття dll
- •2.1.2. Створення dll у середовищі Delphi (експорт)
- •2.1.3. Використання dll у Delphi (імпорт)
- •2.1.4. Створення динамічних бібліотек для редагування ресурсів
- •2.2. Основи сом-технології
- •2.2.1. Загальний опис
- •2.2.2. Базові поняття
- •2.2.3. Бібліотека сом
- •2.2.4. Бібліотека типів
- •2.3.2. Сервер сом у Delphi
- •2.3.3. Бібліотека типів у delphі
- •2.4. Створення системи клієнт-сервер на основі базового com-об’єкту у складі внутрішнього сервера
- •2.4.1. Створення сом-сервера
- •2.4.2. Створення сом-клієнта
- •2.4.3. Використання сом-об’єкту в клієнтській програмі
- •2.5. Механізм міжпроцесного обміну
- •2.6. Створення систем клієнт-сервер на основі зовнішнього базового сом-об’єкту
- •2.6.1. Основні поняття
- •2.6.2. Засоби організації потокової взаємодії клієнта і сервера
- •2.6.3. Методи формування екземпляра сом-об’єкту
- •2.6.4. Формування екземпляра зовнішнього сом-об’єкту
- •2.6.5. Створення сом-сервера
- •2.6.6. Створення сом-клієнта
- •2.7. Автоматизація
- •Створення сервера автоматизації;
- •2.7.1. Базові поняття
- •2.7.2. Сервер автоматизації
- •2.7.3. Контролер автоматизації
- •2.8. Створення системи клієнт-сервер на основі внутрішнього сервера автоматизації
- •2.8.1. Об'єкт автоматизації. Клас tAutoObject
- •2.8.2. Вбудований сервер автоматизації
- •2.8.3. Створення клієнта автоматизації
- •2.9. Зовнішній сервер автоматизації
- •2.9.1. Основні визначення
- •2.9.2. Виконання маршалінгу з рядками, шрифтами і зображеннями
- •2.9.3. Перетворення наявного застосунка в сом-сервер автоматизації
- •2.9.4. Створення клієнта автоматизації
- •2.10. Події в сом і зворотні виклики на основі інтерфейсів диспетчирування
- •2.10.1. Створення сервера автоматизації
- •3. Формування бібліотеки типів
- •4. Формування методів
- •5. Реєстрація сервера
- •2.10.2. Створення клієнтського застосунка
- •2.10.3. Підключення множини клієнтів до сервера
- •2.11. Інтерфейси зі зворотним викликом
- •2.11.1. Створення сервера
- •2.11.2. Створення клієнтського застосунка
- •2.12. Технологія ActiveХ
- •2.12.1. Використання готових елементів АctiveХ
- •2.12.2. Розроблення власних елементів АctiveХ
- •2.12.3. Поширення елементів керування ActiveХ і форм ActiveХForm у Web-середовище
- •2.14. Dcom технологія
- •2.14.1. Загальна схема взаємодії сом-клієнта і сом-сервера
- •2.14.2. Розроблення системи «клієнт-віддалений сом-сервер»
- •Розділ 3. Проектування розподілених систем на платформі Microsoft .Net
- •3.1.1. Здійсненя викликань з типів .Net до типів сом
- •3.1.2. Звернення клієнта сом до збірки .Net
- •3.2. Об’єктно-орієнтована архітектура .Net Remotіng – основа створення розподілених систем Mіcrosoft .Net.
- •3.2.1. Створення системи клієнт-сервер на основі технології Remoting
- •Розділ 4. Створення системи "клієнт - сервер" на основі технології corba
- •4.1. Загальні теоретичні відомості
- •4.2. Створення серверного застосунка
- •1. Створення файла опису інтерфейсу
- •Викликання конструктора створення corba сервера
- •Формуємо модуль Unit1
- •Формуємо реалізацію методу
- •4.3. Створення клієнтського застосунка
- •Викликання конструктора corba-клієнта
- •2. Формування форми
- •3. Запуск застосунка
- •Приклад програмних кодів сервера
- •4.4. Порівняльний аналіз технологій сом і соrва
- •4.4.1. Основні принципи об'єктних моделей
- •4.4.2. Об'єктні моделі
- •4.4.3. Підтримка операційних систем
- •4.4.4. Формальний опис архітектури і проблеми реалізації
- •4.4.5. Підсумки порівняння
- •Літературні джерела
3.1.1. Здійсненя викликань з типів .Net до типів сом
Для вирішения завдання взаємодії збірок .NET із серверами СОМ необхідно забезпечити деякий проміжний рівень, який був би здатний транслювати виклики .NET у відповідні виклики СОМ (подаючи типи СОМ у вигляді типів .NET). Роль подібного проміжного рівня в .NET виконують служби RCW (Runtime Callable Wrapper – оболонка, що викликається під час виконання).
Кожному класу СОМ (тобто сокласу), до якого здійснюються виклики з клієнта .NET, необхідна своя оболонка — свій RCW. Отже, якщо ми працюємо з одним застосунком .NET, який звертається до трьох сокласів СОМ, нам будуть потрібні три окремі RCW, які будуть перетворювати виклики .NET у виклики СОМ. Зазначимо, що кількість RCW не залежить від того, до якої кількості інтерфейсів у кожному з трьох со-класів будуть здійснюватися звернення. Крім того, модуль RCW використовується також для відстеження посилань на об’єкти СОМ, які створені в .NET збірці.
З агальна схема процесу функціонування RCW зображена на рис. 3.1.
Створюється RCW автоматично за допомогою утиліти tlbimp.exe (від type library importer — імпортер бібліотеки типів). Крім того, зазначимо, що для нормальної взаємодії з клієнтами .NET немає потреби змінювати СОМ-класи. Усю необхідну роботу беруть на себе модулі RCW.
Подання типів СОМ як типів .NET. Завдання подання типів СОМ як типи .NET виконують програмні модулі RCW. Розглянемо на прикладі. Припустимо, що у модулі СОМ визначено метод (такий, що входить до складу інтерфейсу), опис якого подано мовою IDL:
//Визначення методу СОМ в IDL
HRESULT DisplayThisString([in] BSTR msg);
Модуль RCW подає цей метод клієнту .NET наступним чином:
// Подання методу СОМ в С#
void DisplayThisString(string msg);
У більшості типів СОМ (включаючи oleautomation – сумісні типи) існують відповідні типи в .NET. Відповідність типів СОМ типам в .NET подано у таблиці 3.2.
Зазначимо, що при роботі із вказівниками в IDL (наприклад, int* замість Int) такий вказівник буде відображатися у відповідний базовий клас (System.Int32/int).
Таблиця 3.2
Таблиця відповідності типів СОМ типам в .NET
Тип COM (IDL) |
Тип .NET |
Псевдоним в С# |
1 |
2 |
3 |
char, boolean, small |
System.SByte |
sbyte |
wchar_t, short |
System.Intie |
short |
long, int |
System. Int32 |
int |
hyper |
System. Int64 |
long |
unsigned char, byte |
System.Byte |
Byte |
unsigned short |
System. UIntl6 |
ushort |
unsigned long, unsigned int |
System. UInt32 |
Uint |
unsigned hyper |
System.UInt64 |
ulong |
single |
System.Singte |
float |
Double |
System.Double |
double |
Таблиця 3.2 (закінчення)
-
1
2
3
VARIANT_BOOL
Heт
bool
HRESULT
System.Int32
int
BSTR
System.String
string
LPSTR или char*
System.String
string
LPWSTR или wchar_t*
System.String
string
VARIANT
System.Object
object
DECIMAL
System. Decimal
Немає
DATE
System. DateTime
Немає
GUID
System.Guid
Немає
CURRENCY
System. Decimal
Немає
IUnknown*
System.Object
Object
IDispatch*
System.Object
Object
Керування посиланнями на об’єкти. Ще одна важлива функція модуля RCW полягає у відстеженні посилань на соклас СОМ. Зазвичай, при використання сокласу у середовищі СОМ у цей процес залучені клієнти сокласу і власне сам соклас, а керування посиланнями виконується за допомогою викликань методів AddRef() та Release(). Класи СОМ самознищуються тоді, коли більше не існує зовнішніх посилань на них.
Однак у технології .NET задачу кеширування всіх посилань на інтерфейси, викликання методу Release() для сервера СОМ, на який відсутні активні посилання зі сторони клієнтів .NET, виконує модуль RCW. Як результат, клієнтам .NET не потрібно явним чином викликати методи AddRef(), Release() або QueryInterface().
Приховування низькорівневих інтерфейсів СОМ. Оскільки модуль RCW повинен подати типи СОМ для клієнта .NET так, ніби вони є звичайними типами .NET, він має вміти також приховувати низьковірневі інтерфейси СОМ.
Наприклад, коли створено клас СОМ, який підтримує інтерфейс IConnectionPointContainer (а також підлеглий об’єкт або два допоміжних інтерфейси IConnectionPoint), соклас має можливість пересилати повідомлення про події назад клієнтові СОМ.
При створені клієнта СОМ більша частина цього процесу повністю прихована (див. п. 2.10.2). Модуль RCW також приховує багато функцій СОМ при створенні клієнта .NET. Усі низьковірневі інтерфейси СОМ-сервера повністю приховані від клієнта СОМ, тому він може працювати лише з користувацькими інтерфейсами, які реалізовані в сокласі.
Деякі інтерфейси СОМ, які приховує модуль RCW, подано у таблиці 3.3.
Таблиця 3.3
Приховані інтерфейси СОМ
Інтерфейс |
Опис |
ICIassFactory |
Забезпечує незалежний від мови та місцезнаходження метод активації класу СОМ |
IConnectionPointContainer IConnectionPoint |
Забезпечує можливість відсилання сокласом подій клієнту (зворотні виклики) |
IDispatch IDispatchEx IprovideClassInfo |
Використовуються для реалізації пізнього зв'язування |
IEnumVariant |
Забезпечує можливість представлення сокласом власного набору внутрішніх типів |
IErrorInfo ISupportErrorInfo ICreateErrorInfo |
Забезпечують можливість для клієнтів та сокласів СОМ генерування повідомлень про помилки та реагування на такі повідомлення |
IUnknown |
Керує лічильником посилань та дозволяє клієнтові отримувати посилання на конкретний інтерфейс сокласу |
Розглянемо простий приклад для демонстрації здійснення викликань зі збірок .NET. Для цього необхідно створити вбудований СОМ-сервер автоматизації SimpleComServer (див. п. 2.8) з СОМ-класом СоСаlс і базовим інтерфейсом ІСоСаlс відповідно, який міст метод Add для знаходження суми двох цілих чисел. Опис цього класу мовою IDL подано нижче:
[odl, uuid(DDA5B80E-8DA4-45DF-B8FF-B6BFFFBCD9E6),
version(1.0), hidden, dual, nonextensible, oleautomation]
interface _CoCalc : IDispatch
{
[id(0x60030000)]
HRESULT Add([in] short x, [in] short y, [out, retval] short *);
};
[uuid(D1D1660X-88D9-4C40-961A-365121C43AF1), version(1.0)]
coclass CoCalc
{
[default] interface _CoCalc;
};
Примітка. Зазначимо, що в наведеному описі мовою IDL інтерфейс має ім’я, що складається із символу «_» +ім’я класу, тобто «_»+CoCalc=_ CoCalc. Це відрізняється від того, як формується ім’я інтерфейсу у середовищі Delphi (а саме: літера «I»+ім’я класу).
Імпорт бібліотеки типів. Перед тим, як звертатися до СОМ-серверу зі збірки .NET, необхідно створити проміжний клас, який буде містити всю необхідну інформацію для передавання запиту СОМ-серверу. Створення такого проміжного класу виконується за допомогою утиліти tlbimp.exe (type library importer — імпортер библиотеки типов). Для цього активізувати режим роботи з командного рядка, перейти в каталог, в якому міститься двійковий модуль СОМ-сервера (тобто DLL), а потім виконати команду такого вигляду:
tlbimp SimpleComServer.dll /out:SimpleAssembly
Тепер можна відкрити створену збірку за допомогою утиліти IDLasm.exe (рис. 3.2). Відзначимо, що всім елементам СОМ-сервера автоматично підібрані еквіваленти .NET.
Рис. 3.2. Типи створеної збірки
Раннє зв'язування з СОМ-класом. Головна задача створеної збірки SimpleAssembly.dll .NET – забезпечити передавання запитів з модулів .NET традиційному СОМ-серверові. Для демонстації її використання створимо СОМ-клієнта (консольний застосунок) на С# під назвою CSharpCalcClient. Потім додамо посилання на створену за допомогою tlbimp.exe збірку SimpleAssembly.dll. Посилання додається через діалогове вікно Add Reference (Додати посилання). Після додавання посилання збірка SimpleAssembly має з’явитися у вікні Solution Explorer у розділі Reference.
Після виконання вищезазначених дій можна скористатися раннім зв’язуванням. У наведеному нижче прикладі програмного коду видно, що для клієнта С# СоСаlс — це звичайний тип .NET, для звертання до якого нічого спеціального не потрібно. Насправді запити з CoCalс будуть передаватися COM-серверу SimpleAssembly.
namespace CSharpCalcClient
{
using System;
// Додано для спрощення доступу до ComCalc
using SimpleAssembly;
public class CalcClient
{
public static int Main(string[] args)
{
// Створюємо об’єкт CoCalc
CoCalc с = new CoCalc();
// Викликаємо метод СОМ-класу CoCalc
Console.WriteLine("30 + 99 is: " + c.Add(30, 99));
return 0;
}
}
}
Як видно, усі члени інтерфейсу за замовчуванням ([default]) сокласа представляються безпосередньо як члени класу CoCalc. Якщо нам буде потрібно явно звернутися до якого-небудь інтерфейсу, це можна зробити за допомогою наступного коду (у прикладі будемо звертатися до того самого _СоСаlс):
public class CalcClient
{
public static int Main(string[] args)
{
// Створюємо об’єкт CoCalc
CoCalc с = new CoCalc();
// Явним чином отримаємо посилання на інтерфейс
_CoCalc icalc = с;
Console. WriteLine(icalc.Add(9, 80));
return 0;
}
}
Раннє зв’язування за допомогою Visual Studio.NET. Раннє зв’язування можна виконати і засобами середовища Visual Studio.NET. Це середовище розроблення дозволяє нам просто додати СОМ-сервер за допомогою діалогового вікна Add Reference (Додати посилання) і закладинки СОМ (рис. 3.3).
У такому випадку автоматично буде викликана утиліта tLbimp.exe, що сторить нову збірку в каталозі Debug (або Release), і посилання на неї буде додано до проекту.
Пізнє зв’язування з СОМ-класом. У просторі імен System.Reflection передбачено способи одержання інформації про типи збірки безпосередньо під час виконання. У СОМ-технології аналогічні можливості реалізуються за допомогою набору стандартних інтерфейсів (ITypeLib, ITypeInfо тощо). Нагадаємо, що зв'язування клієнта із СОМ-сервером під час виконання програми (на противагу зв'язуванню під час компіляції) називається пізнім зв'язуванням.
Процес пізнього зв'язування починається з одержання клієнтом від сокласу посилання на інтерфейс IDispatch. Після чого працює така сама схема, що і при роботі з СОМ-сервером із звичайного Windows-застосунка (див. п. 2.7.3). Нагадаємо основні епати роботи через інтерфейс IDispatch, які будуть необхідні для розуміння алгоритму пізнього зв’язування, що реалізовано у .NET.
Метод GetIDsOfNames() дозволяє клієнтові, що застосовує метод пізнього зв'язування, одержувати числове значення (DISPID), яке використовується для ідентифікації методу, який клієнт збирається викликати. У COM IDL ідентифікатор DISPID для члена класу призначається за допомогою атрибута [Id]. Числове значення, позначене атрибутом [id], метод GetIDsOfNames() повертає клієнтові, який застосовує метод пізнього зв'язування. Отримавши це значення, клієнт може скористатися методом Invoke( ). Цей метод інтерфейсу IDispatch приймає декілька параметрів, один з яких — DISPID методу, що викликається. Крім того, метод Invoke() приймає масив типів COM VARIANT, який являє собою параметри, які треба передати методові, що викликається.
Для класу ComCalc з методом Add() – це масив, що буде містити два значення типу short. Останній параметр методу Invoke() — ще один тип VARIANT, який являє собою значення, що повертається клієнтові (у випадку методу Add() класу ComCalc – це одне значення типу short).
Клієнт .NET, що використовує пізнє зв’язування, застосовує схему викликання методу диспетчерського інтерфейсу через методи інтерфейсу IDispatch за допомогою RCW та типів із простору імен System.Reflection.
Нижче наведено приклад клієнта С#, який використовує пізнє зв’язування для виклику методу Add() СОМ-класу ComCalc. Звернемо увагу на те, що у такому випадку не треба мати проміжну збірку, яка генерується за допомогою утиліти tlbimp.exe:
using System;
using System.Reflection;
public class LateBinder
{
public static int Main(string[] args)
{
//Отримаємо посилання на інтерфейс IDispatch від сокласу
Type calcObj = Type.GetTypeFromProgID( "SimpleComServer.ComCalc") ;
object calcDisp = Activator.Createlnstance(calcObj);
// Створюємо масив параметрів
object[] addArgs = { 100, 34 };
// Викликаємо метод Add() і отримаємо результат
object sum = null ;
sum = calcObj.InvokeMember("Add", BindingFlags.InvokeMethod, null,calcDisp, addArgs);
// Виведення результату на екран
Console.WriteLine("Late bound adding:\nlOO + 24 is: (0}",sum);
return 0;
}
}
Зазначимо декілька особливостей створеної нами збірки, яка використовується для зв’язку з клієнтом .NET.
1. У маніфесті створеної збірки SimpleAssembly.dll у значенні атрибуту ImportedFromTypeLibAttribute прописано шлях до СОМ-сервера. Це означає, що при переміщенні або зміні назви СОМ-сервера, треба наново генерувати збірку SimpleAssembly.dll.
2. Створений на основі СОМ-класу відповідний клас технології .NET автоматично стає нащадком класу System.Object.
Обробка подій СОМ (зворотний виклик). Архітектура моделі подій .NET ґрунтується на делегуванні логіки виконання від однієї частини застосунка до іншої. Для подібного делегування використовуються типи, похідні від System.MulticastDelegate. Клієнт може додавати й видаляти приймачі подій із внутрішнього списку за допомогою перевантажених операторів += та -=.
Коли утиліта tlbimp.exe у процесі перетворення зустрічає інтерфейс [source] (тобто інтерфейс підтримки подій) у бібліотеці типів СОМ-сервера, вона створює набір типів .NET, які послужать оболонкою для низькорівневої архітектури точок з'єднання СОМ. Таким чином в проксі-збірці створюються еквіваленти .NET для подій СОМ.
Приклад. Розглянемо соклас Саr, який описує рух об’єкту автомобіль і в якому визначений наступний вихідний інтерфейс, що містить подію Exploded (опис подано мовою IDL):
dispinterface _ICarEvents {
properties:
methods:
[id(1), helpstring("method Exploded")]
HRESULT Exploded([in] BSTR deadMsg); };
Інтерфейс _ICarEvents використовується для реалізації механізму генерування подій на основі інтерфейсів диспетчирування (докладні інструкції щодо створення серверів автоматизації, що підтримують обробку подій на основі інтерфейсів диспетчирування, містяться у п. 2.10). Також клас СоСаr містить інтерфейс за замовчуванням [default], що має ім’я ІСаr, та метод SpeedUp, який генерує подію Exploded при перевищенні значення властивості швидкість (понад 150 км/год), яка повідомляє про вибух автомобіля. Текст методу мовою ObjectPascal середовища Delphi наведено нижче:
procedure TCar.SpeedUp(pSpeed: Integer);
begin
if pSpeed > 150 then
begin
FEvents.Exploded(“автомобіль вибухнув!”);
end;
end;
При генерування проміжної збірки для підключення розробленого сервера СОМ до збірки .NET утиліта tlbimp.exe створює набір типів для точного подання системи подій СОМ-сервера (архітектури точок з'єднання) у системі подій .NET. Ці типи наведені в таблиці 3.4.
Таблиця 3.4
Відповідність згенерованих елементів системи подій .NET елементам системи подій СОМ
Згенерований тип (для інтерфейсу _CarEvents [source]) |
Опис |
ICarEvents |
Інтерфейс .NET – аналог вихідного інтерфейсу СОМ-сервера. Зазвичай безпосередньо не використовується |
ICarEvents_Event |
Інтерфейс .NET, що визначає члени для додавання і видалення методів із вбудованого списку System.MulticastDelegate. Зазвичай також безпосередньо не використовується |
ICarEvents_ExplodedEventHandler |
Делегат .NET (тип, похідний від System.MulticastDelegate). Обробник події повинен обов'язково повертати значення типу int. Цей тип відповідає вихідній події СОМ |
ICarEvents_SinkHelper |
Цей згенерований клас реалізує вихідний інтерфейс в об'єкті-приймачі .NET. Цей клас привласнює значення cookie, що згенероване типом СОМ, змінній m_dwCookie. Крім того, в цьому класі передбачена внутрішня змінна m_ExplodedDelegate, що представляє вихідний інтерфейс (ICarEvents_ExplodedEventHandler) |
Крім генерованих типів, поданих у табл. 3.4 для системи подій вносяться зміни й у визначення класу .NET СагClass, у нього з’являється подія Exploded.
У класі .NET СагClass передбачено два методи, визначені як private, які обслуговують з'єднання із джерелом подій СОМ – add_Х і remove_Х (рис. 3.4).
Рис 3.4. Події СОМ обслуговуються за допомогою набору з двох функцій
У коді інструкцій IL для методу add_Exploded можна виявити, що при його виконанні створюється новий об'єкт ICarEvents_SinkНelper. Після цього проксі-збірка отримує посилання на інтерфейс IConnectionPoint з СОМ-сервера Cаг, викликає метод Advise() і кешує значення cookie, що повертається.
Захоплення події COM проксі-збіркою .NET. Розглянемо на прикладі як збірка .NET реагує на подію СОМ-сервера. Цей процес майже ідентичний процесу роботи з делегатами .NET:
//Підключаємо бібліотеку СОМ-севера, яка в нашому прикладі //зветься CarServer
using CarServer;
public class CoCarClient
{
// Цей mетод буде викликано при виникненні події на СОМ-
// сервері. Метод повинен мати набір параметрів ідентичний //тому, який визначено для події
public static int ExplodedHandler(String msg)
{
Console.Writeline("\nCar says:(COM Events)\n->"+msg+"\n");
return 0;
}
public static int Main(str1ng[] args)
{
CarСlass viper = new CarClass();
// Встановлюємо делегат для події Exploded
viper.Exploded += new ICarEventsЕxplodedEventHandler(ExplodedHandler);
// Наступні дії змушують СОМ-сервер згенерувати подію
for (int i=0; i < 5; i++)
{
try
{
//Метод SpeedUp генерує подію Exploded
viper.SpeedUp(150);
Console. WnteUne("->Curr speed is: " + viper. GetCurSpeedO);
}
catch (Exception ex)
{
Console. WnteLine("->CQM error!"+ex.Message+"\n");
}
}
}
}
Примітка. Делегат для події Exploded встановлюється за допомогою перевантаженого оператора += таким рядком:
viper.Exploded += new ICarEventsЕxplodedEventHandler(ExplodedHandler);
Зверніть увагу, що в дужках зазначається назва методу, який буде обробником події Exploded, тобто буде викликаний при виникненні цієї події. Опис параметрів такого методу має співпадати з описом параметрів події!
Результат роботи розробленого клієнта .NET подано на рис 3.5.
Рис. 3.5. Звернення з клієнта С# до сервера СОМ