![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
COM. Раннее связывание и ID-связывание. Есть ли реальное отличие?
Aleksey Pavlov © (2002-09-19 12:21:00)Приветствую Мастаков.
На днях, в связи с прочтением одной заметки, возник такой вопрос: какое отличие между ID и ранним связыванием (применительно к технологии COM)?
Я, вообще говоря, всегда думал, что раннее связывание и ID-связывание - это синонимы, т.к. при раннем связывании идентификаторы имён (ID of names) разрешаются на стадии компиляции и заносятся в VTable. Затем через интерфейс VTable осуществляется значительно более быстрый доступ к серверу автоматизации, чем через DispInterface, т.к. нам нет необхрдимости выполнять ф-ию GetIdOfNames интерфейса IDispatch.
В связи с этим, было высказанно мнение, что ID-связывание - это что-то промежуточное между ранним и поздним видом связывания, и что при использовании ID-связывании из Delphi необходимо применять DispInterface, т.к. в данном случае вызов производится через методы IDispatch.
Прошу прояснить ситуацию.
Заранее благодарен.
vuk (2002-09-19 18:53:00) [41]VTable (как и VMT) хранит адреса методов. В случае сервера вне процесса там находятся адреса функций - заглушек, при помощи которых осуществляется маршалинг вызовов в другой процесс.
Digitman (2002-09-19 18:47:00) [42]>vuk
Вопрос тебе "в лоб" : для чего предназначена VTable ? Что она хранит в run-time ?
Digitman (2002-09-19 18:44:00) [43]>Romkin
Ну, я ж просил не придираться) ... Ну пусть будет disp - это ничего не меняет, мало ли как можно обозвать интерфейс в TLB).. Видишь же, что приводим его к типу IDispatch - значит, речь идет именно о диспинтерфейсах) ... назови ты их хоть хреном)))
ну, давай так тогда - на огурцах определение дадим всей этой ботве :
- при выборе методологии интерпроцессного взаимодействия (однозначно ведущей к маршаллингу) раннее связ-е отличается от позднего тем, что при раннем разрешение имени в ID выполняет компилятор, а при позднем - код процесса, вынужденный вызывать GetIdsOfNames(); здесь без safecall-соглашения никак не обойтись;
- при выборе методологии интрапроцессного взаимодействия (где маршаллинг нужен как 5-е колесо у телеги) в принципе все выглядит точно так же, но раннее связ-е кр.того может быть организовано и посредством VTable, и тогда в реальности интерфейсные вызовы выглядят в объектном коде как вызовы обычных процедур/ф-ций (и ни какого, к черту, маршаллинга не нужно - все вызовы происходят непосредственно и, значит, максимально быстро/эффективно); и забудем как кошмарный сон этот самый safecall - исключения мы и так поймаем, а типы параметров-результатов нам известны из TLB
vuk (2002-09-19 18:40:00) [44]>ExcelAppDispIntf : _Application;
А теперь посмотрите как пойдут вызовы к этому интерфейсу (откройте CPU view). Через VTable, не так ли? И как это соотносится с Вашими прошлыми утверждениями?
Romkin (2002-09-19 18:25:00) [45]Вообще-то я всегда считал, что раннее связывание - это обращение к VTable, позднее - Invoke... Может быть, я ошибаюсь... Кстати, _Application & _WorkBook - это не dispinterface, dispinterface"s соотв _ApplicationDisp & _WorkBookDisp
Digitman (2002-09-19 18:10:00) [46]
> Romkin © (19.09.02 17:48)
> Если есть IDispatch, я всегда могу вызвать методы GetIDsOfNames
> & Invoke. При чем здесь dispinterface?
а это уже ты компилятор напрягаешь (зачастую - не по делу): раз ты заикнулся о IDispatch (преобразуешь некий интерфейсный тип к данному типу), компилятор правомерно считает, что при последующих обращениях к методам/св-вам такого интерфейса ему следует, обратившись к TLB-прототипу, взять оттуда соответствующий ID и подствить его в безусловно генерируемый код вызова Invoke(ID).
Digitman (2002-09-19 18:03:00) [47]давайте (бедный Эхел ! в который уже раз его...) "изнасилуем" незабвенный Excel)))
вот классич. пример (не придираемся к точным названиям интерфейсов и опускаем незначащие для сути параметры вызовов их методов):
var
Excel: OleVariant;
ExcelAppDispIntf : _Application; // ссылаемся на Excel_TLB.pas
Workbook: OleVariant;
WorkbookDispIntf: _Workbook; // ссылаемся на Excel_TLB.pas
Work
...
Excel := CreateOleObject("Excel.Application"); // для позднего связывания
ExcelAppDispIntf := IDispatch(CreateOleObject("Excel.Application")) as _Application; //для раннего связывания
...
WorkBook := Excel.ActiveWorkbook; // используем позднее связывание - не знаем заранее, есть ли данное св-во у нечто обозванного именем Excel
WorkBookDispIntf := ExcelAppDispIntf.ActiveWorkbook; // используем раннее связывание : знаем, что WorkBookDispIntf - диспинтерфейс существующего в ином АП объекта, а ActiveWorkbook превращается в вызов его метода Get_ActiveWorkbook посредством WorkBookDispIntf.Invoke(ID), где ID уже известен из Excel_TLB.pas
Romkin (2002-09-19 17:48:00) [48]Если есть IDispatch, я всегда могу вызвать методы GetIDsOfNames & Invoke. При чем здесь dispinterface?
Digitman (2002-09-19 17:42:00) [49]>vuk
О VTable (в принципе !) может идти речь только тогда. когда К. и С. находятся в едином ВАП. С каких это пор Ecxcel (как С.) стал присутствовать в АП твоего процесса (как К.) ? Возрази ...
>Romkin
> Если я импортирую библиотеку типов, то ВСЕ обращения к методам
> интерфейса будут проверены на этапе компиляции а между тем
> вызовы будут идти через invoke
Кто ж спорит ?) Но ведь никто не заставляет тебя в библиотеке типов держать диспинтерфейсы ! И как, позволь полюбопытствовать, в таком случае вызов интерфейсного метода из этой импортированной бтбл-ки сочетается с GeInvoke() и GetIDsOfNames() ? Как вот ты себе это видишь, если БТ не декларирует ни один диспинтерфейс ?
vuk (2002-09-19 17:16:00) [50]to Digitman:
>При интерпроцессном взаимодействии между К и С ни о каких
>VTable речи быть не может
Ну почему же, очень даже может. Не верите - проверьте скорость взаимодействия с тем же Excel c использованием раннего и позднего связывания. Более того, и между процессами и с использованием DCOM тоже возможно взаимодействие через чистые VTable интерфейсы (даже не dual). Другое дело, что Delphi этого сделать не позволяет, поскольку для этого нужен компилятор MIDL и компилятор С для генерации dll заглушки.
to Aleksey Pavlov:
Как я Вам писал ранее, я тоже не могу согласиться с тем, что ID связывание можно называть ранним. С его использованием взаимодействие хотя и осуществляется быстрее, но ранним(как в случае с vtable) оно не является, поскольку вызов все равно идет через Invoke, а не напрямую.
Romkin (2002-09-19 17:13:00) [51]Термином позднее связывание обозначается такая методика создания программы, при которой все обращения к методам интерфейса не проверяются на корректность передачи параметров до тех пор, пока не наступит время выполнения программы -
Вот что я имел в виду. Если я импортирую библиотеку типов, то ВСЕ обращения к методам интерфейса будут проверены на этапе компиляцииа между тем вызовы будут идти через invoke (сам постоянно так делаю, обращаюсь именно к dispinterface)
Да и далее в главе 4 он показывает тесты для VTable, Variant (а вместо него можно подставить IDispatch) и dispinterface... как сочетается у него проверка параметров методов по экспортированной библиотеке типов и это определение - не ясно
Digitman (2002-09-19 17:01:00) [52]>Romkin
> он объединил обращение к VTable и dispinterface в раннее связывание
Ты что-то путаешь) ... Где ты увидел слово dispinterface (или IDispatch) в его определении раннего связывания ? Он говорит о интерфейсе как таковом, что в случае раннего связывания (под Делфи - в том числе) следует интерпретировать не иначе как любого потомка IUnknown. В т.ч. - и IDispatch. При этом ни GetIDsOfNames() ни Invoke() попросту не задействуются никак, поскольку в этом нет необходимости : адреса ф-ций, реализующих интерфейсные методы, известны компилятору (благо их тела будут размещены в том же АП, что и АП процесса-контроллера) и он генерирует объектный код, способный после run-time-сборки вызывать эти ф-ции непосредственно через ссылки в VTable.
Romkin (2002-09-19 16:44:00) [53]Хм... Кажется, насчет раннего связывания не то сказал...
А вот с Эриком Хармоном я не совсем согласен, он объединил обращение к VTable и dispinterface в раннее связывание, а это все-таки очень разные вещи :-))
Мне больше нравится объяснение здесь
http://www.techvanguards.com/com/concepts/automation.asp
Digitman (2002-09-19 16:25:00) [54]Знаете ли, господа, коль скоро речь зашла о терминах и их знпении, не поленюсь привести вот такую цитатку с определениями раннего и позднего связывания :
"При управлении сервером автоматизации посредством интерфейса используется раннее связывание(early binding). Термином раннее связываниеобозначается такая методика создания программы, при которой все обращения к методам интерфейса проверяются на корректность передачи параметров еще на стадии компиляции.
...
При управлении сервером автоматизации посредством переменных типа variant используется так называемое позднее связывание(late binding). Термином позднее связываниеобозначается такая методика создания программы, при которой все обращения к методам интерфейса не проверяются на корректность передачи параметров до тех пор, пока не наступит время выполнения программы"
Цитировано из вот этого букваря :
(С) Эрик Хармон
"Разработка СОМ-приложений в среде Delphi"
Профессиональное руководство по созданию масштабируемых COM/DCOM-приложений в среде Delphi версий 3-5
P.S. Думаю, мои собственные определения и понимание данных терминов концептуально совпадают с процитированными авторскими. И совпадали еще до знакомства с этим автором.
Набережных С. (2002-09-19 15:49:00) [55]
> Romkin © (19.09.02 14:54)
> (с ранним связыванием - никогда),
Это еще почему?
Digitman (2002-09-19 15:05:00) [56]>Romkin
Вот и я о том же) ..
мало ли как обзывают сабж ! гораздо важней понимание, что и как работает... только при полном понимании можно делать выбор наиболее эффективного способа связывания для конкретных условий взаимодействия К. и С.
Ну и маршаллинг, конечно ... если он необходим, нужно понимать, зачем, почему и как он организован (штатными ли, собственными ли средствами) и работает в тех или иных условиях...
Romkin (2002-09-19 14:54:00) [57]Не путайтесь в понятиях. Все дело в том, что родоначальником интерфейсов в COM является IUnknown, у потомков которого есть только VTable, таблица адресов методов. Обращение к ней и является ранним связыванием. Пример - COM Object
Следующий интерфейс - IDispatch, требует type library и позволяет делать вызовы через GetIDsOfNames & Invoke, что позволяет выходить за границу процесса (с ранним связыванием - никогда), объявляя dispinterface
И есть промежуточный уровень, когда производится импорт библиотеки типов, и на этапе компиляции в вызовы Invoke подставляется ID нужного метода, таким образом, вызов GetIDsOfNames не производится, и проверка параметров вызова происходит на этапе компиляции. Но это также просто разновидность позднего связывания.
И возможны дуальные интерфейсы, одновременно имеющие VTable и IDs, например, Automation Object.
Быстрее всего работает VTable, по скорости практически неотличимо от вызова метода класса, но COM объект в этом случае должен находится в адресном пространстве процесса (в проекте, в dll, в ocx...)
Позднее связывание медленнее на порядок-два... Но есть возможность написать собственный маршаллинг, чем никто обычно не пользуется, и тогда все будет быстрее
Digitman (2002-09-19 13:38:00) [58]>Aleksey Pavlov
Я не читал соотв.терминологии и пояснений от Тейксейры и Пачеко, но как бы эти понятия не выглядели в чьих-либо устах, разделять и понимать их все же следует, наверно, именно так, как ты себе это представляешь на дан.момент. Суть остается сутью)...
Aleksey Pavlov (2002-09-19 13:15:00) [59]>>Digitman © (19.09.02 12:50):
Всё Вами сказанное верно. Но мне хотелось бы просто узнать, правомерно ли называть раннее связывание ID-связыванием, как это, к примеру, делается у Тейксейры и Пачеко или же всё-таки стоит разделять эти понятия?
Digitman (2002-09-19 12:50:00) [60]ID-связывание является более универсальным способом связывания контроллера (К) и сервера (С). При интерпроцессном взаимодействии между К и С ни о каких VTable речи быть не может, поскольку в частном случае К и С могут функционировать как самостоятельные процессы, следовательно - находиться в разных адр.пространствах (и даже - на разных машинах). В таких случаях, как правило и в первую очередь, и применяются диспинтерфейсы как реализующие интерфейсные вызовы с идентификацией по их ID. При раннем связывании ID известен (достаточно вызвать IDispatch.Invoke(ID,..) для вызова требуемого интерфейного метода), при позднем же требуется сначала получить этот ID, для чего IDispatch предусматривает обязательный к реализации метод GetIDsOfNames.
Позднее связывание наиболее универсально (на вызывающей стороне не требуется наличие ни type library в run-time ни ее прототипа в compile-time), но менее эффективно из-за необходимости всякий раз перед вызовом IDispatch.Invoke(ID..) получать нужный ID метода предварительным вызовом IDispatch.GetIDsOfNames(MethodName)
При раннем связывании К и С, работающих в едином АП, в принципе не требуется даже ID метода (необходимость маршаллинга отпадает), поскольку его адрес доступен непосредственно и может быть вычислен на этапе компиляции и при необходимости эффективно скорректирован загрузчиком на этапе load-time-компоновки модулей К и С в ВАП процесса.