На початку було слово
А за допомогою пізнього зв'язування, так:
рис.1 Інтерфейс IUnknown
рис.2 Інтерфейс IDispatch
Власне, у інтерфейсу IUnknown є три покажчика на інтерфейси, що є основою COM і дозволяють почати роботу з COM-об'єктом: QueryInterface (служить для запиту у об'єкта покажчиків на інші інтерфейси), AddRef (служить для збільшення лічильника посилань на інтерфейс) і Release (служить для зменшення лічильника посилань на інтерфейс). IDispatch включає в себе ці ж три основних покажчика інтерфейсів, плюс чотири власних покажчика інтерфейсів, головним з яких для нас є Invoke. тому завдяки йому у нас з'явиться можливість викликати на виконання методи, реалізовані в COM-об'єкт, не набагато складніше, ніж на мові високого рівня.
Що таке покажчик на інтерфейс? Для нас це не більше, ніж виклик функції COM-об'єкта. Тобто, кожен покажчик вказує нам на місце розташування викликається функції в COM-об'єкт. Так як кожен покажчик має розмір 4 байта (dword), то простим зміщенням від покажчика на IDispatch, можна викликати функцію, реалізовану в об'єкті. Але ми люди інтелігентні і тому будемо використовувати безпосередньо назви функцій. Так простіше і зрозуміліше.
Внутрішні локальні сервера
Отже, приступимо. Для початку створимо найпростіший COM-об'єкт. Напишемо Inproc-сервер у вигляді dll, який буде зареєстрований на одній машині з клієнтом. Для цього запускаємо Visual Basic з пакету Microsoft Visual Studio 6.0. В якості нового проекту вибираємо ActiveX Dll. Переходимо у вікно Project Explorer і даємо нашим проектом назву mycom. а Class1 перейменовуємо в myclass. У вікні нашого класу пишемо наступний код:
рис.3 Створення COM-сервера
Тепер привожу повний код програми на асемблері для доступу до нашого COM-об'єкту. Всі пояснення після.
Тепер по пунктах.
Ось опис функції з MSDN:
Функція приймає в якості вхідного параметра ProgID COM-об'єкта і повертає CLSID даного об'єкта. Якби ми мали GUID об'єкта, то змогли б використовувати іншу API-функцію -
яка дозволяє отримати CLSID об'єкта з покажчика на рядок GUID.
Ця пара функцій використовується для ініціалізації / деініціалізацію бібліотеки COM. Функція OleInitialize має єдиний (причому зарезервований параметр), який должет дорівнювати нулю. До речі практика показує, що замість цієї пари функцій цілком можна використовувати іншу пару:
які, за великим рахунком, призначені для тієї ж мети.
Головна функція створення екземпляра COM-об'єкта. Її опис в MSDN виглядає наступним чином:
Думаю тут все ясно. Другий параметр у нас NULL, тому що ми не потребуємо IUnknown. Третій параметр - це одна або кілька об'єднаних констант енумератора CLSCTX.
Четвертий параметр - це GUID запитуваної інтерфейсу (в даному випадку ми запитуємо покажчик на IDispatch). І, нарешті п'ятий параметр - змінна, в яку цей покажчик повернеться в разі вдалого виконання функції.
У разі успішного виконання, функція повертає S_OK (або попросту кажучи 0). А в разі невдачі - одну з трьох помилок (REGDB_E_CLASSNOTREG (клас не зареєстрований), CLASS_E_NOAGGREGATION (клас не може бути створений як частина викликає процесу), E_NOINTERFACE (інтерфейс відсутній)).
Функція без параметрів, що дозволяє отримати локальний ідентифікатор системи за замовчуванням (щось типу цього). Для нас він буде дорівнює 419. Але для чистоти експерименту, краще все-таки використовувати функцію.
А тепер увага. Якщо Ви не зрозумієте цей момент, не зрозумієте нічого взагалі. Ми в даному випадку викликаємо функцію GetIDsOfNames інтерфейсу IDispatch. Сама функція GetIDsOfNames служить для отримання покажчика на інтерфейс необхідного нам методу mymethod і вимагає п'ять параметрів:
Тому у нас в coinvoke вісім параметрів: три, обов'язкових для макросу, плюс п'ять для функції. Сподіваюся Вам це стало також зрозуміло, як мені недавно. -))
Після того як ми маємо dispid потрібного нам методу, ми просто викликаємо його з допомогою функції Invoke диспетчера. Invoke потрібно передати вісім параметрів:
З першими трьома параметрами я думаю все зрозуміло. Інші параметри вимагають пояснень. Що стосується четвертого параметра, уявіть собі ситуацію, коли у Вашому COM-об'єкт реалізовані відразу чотири функції з однаковою назвою (напр. Color): одна нормальна функція, друга для установки значення властивості, третя для установки значення властивості за посиланням і четверта, яка повертає значення властивості. Якщо ми передамо назву функції Invoke. як він визначить яку з функцій об'єкта ми викликаємо на виконання? Ось для цього як-раз і потрібен четвертий параметр, який може приймати одне з наступних значень:
П'ятий параметр - структура DISPPARAMS, що містить аргументи викликається функції COM-об'єкта. Ось її визначення:
Інакше кажучи, якщо нам для функції Color об'єкта необхідно передати аргумент Red, то цей аргумент ми передаємо в п'ятому параметрі функції Invoke. Так як зараз метод mymethod нашого COM-об'єкта не вимагає ніяких параметрів, то ми в перші два елементи структури передаємо NULL (у нас немає аргументів і іменованих аргументів), а в другі два елементи передаємо 0 (кількість аргументів і іменованих аргументів). Але запам'ятаємо цю структуру на майбутнє, тому що вона нам буде дуже потрібна при передачі аргументів функцій в майбутньому.
В останні три параметра функції Invoke ми передаємо NULL, тому що функція COM-об'єкта нічого нам не повертає (вона тільки показує вікно повідомлення), структуру EXCEPINFO ми використовувати поки не збираємося і, оскільки, ми не передаємо ніяких аргументів методу нашого COM-об'єкта, то не зацікавлені в інформації про те, який з аргументів неправильний .
Думаю зараз Ви вже насолоджуєтеся видом вікна повідомлення, що виводиться нашим COM-об'єктом. Але все це ще тільки квіточки. Далі нас чекають Outproc-сервера і віддалений доступ до COM-об'єктів. А зараз можете піти і попити пивка. -))