<?xml version='1.0' encoding="utf-8"?>
      <rss version='2.0'>
      <channel>
      <title>Форум на Исходниках.RU</title>
      <link>https://forum.sources.ru</link>
      <description>Форум на Исходниках.RU</description>
      <generator>Форум на Исходниках.RU</generator>
  	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=173129&amp;view=findpost&amp;p=1463972</guid>
        <pubDate>Sat, 24 Feb 2007 09:18:02 +0000</pubDate>
        <title>Интерфейсы и плагины</title>
        <link>https://forum.sources.ru/index.php?showtopic=173129&amp;view=findpost&amp;p=1463972</link>
        <description><![CDATA[Krid: <div class='tag-align-center'><strong class='tag-b'><span class='tag-size' data-value='14' style='font-size:14pt;'><span class='tag-u'>Интерфейсы и плагины.</span></span></strong></div><br>
<br>
Автор: <a class='tag-url' href='http://forum.sources.ru/index.php?showuser=21420' target='_blank'><strong class='tag-b'>Romkin</strong></a><br>
<br>
<ul class="tag-list"><li>Введение</li><li>Объекты COM<ul class="tag-list"><li>Реализация интерфейса </li><li>Тестовое приложение</li></ul></li><li>Автоматизация <ul class="tag-list"><li>Библиотека типов </li><li>Реализация массива </li><li>Реализация сортировки </li><li>Категория COM </li><li>Тестовое приложение</li></ul></li><li>Заключение</li><li>Приложения</li></ul><br>
<strong class='tag-b'><span class='tag-size' data-value='13' style='font-size:13pt;'>Введение</span></strong><br>
<br>
Пожалуй, каждому программисту со временем приходит в голову мысль, что его замечательной программе не хватает гибкости. И действительно, допустим, есть большая программа, и в нее понадобилось внести небольшие изменения. Не важно, по какой причине. Но для этого ее надо откомпилировать, проверить, а потом еще и поставить на рабочие места. Нет нужды говорить, что это достаточно долгое занятие. В другом случае может возникнуть желание, чтобы другие программисты могли расширять функциональность программы, а когда программа - единое целое, то им надо полностью поставить исходный код, еще и с описанием, где что находится, какие библиотеки нужны и так далее.<br>
<br>
И программист приходит к выводу, что очень желательно, чтобы его программа состояла из набора отдельных модулей (плагинов), каждый из которых был бы независим от других. Тогда при необходимости можно изменить и отладить только один модуль, а сторонним программистам дать шаблон интерфейса. Программ, построенных таким образом, множество, и остается только завидовать. Чаще всего модульность реализуется на основе механизмов dll, динамических библиотек. Вот только со стандартной dll возникают трудности: экспортируются только процедуры, да и ansistring не так просто передать. А хочется ведь передавать в модули и объекты. Конечно, это возможно, но... С этого момента плагины можно писать только на Delphi, и, мало того, именно на той версии, с которой начиналась разработка, никто ведь не гарантирует, что внутреннее представление объекта не поменяется с изменением версии среды разработки. <br>
<br>
И, как ни странно, мало кто обращает внимание на механизм, который есть в любой современной версии Windows, и который предназначен прежде всего именно для взаимодействия модулей и программ. Я имею в виду COM. И если учесть, что в Delphi работа с COM является достаточно простым делом, очень странным кажется тот факт, что в большинстве случаев применение этой технологии ограничивается, например, выдачей в Excel отчетов или работой с TWebBrowser.<br>
<br>
Но эта технология позволяет легко создавать модульные приложения, и с достаточно низкими затратами. Все, что нужно - реализовать интерфейсы к объектам, и обмениваться этими интерфейсами. И совершенно не важно, на какой версии Delphi написан плагин, фактически, он может быть написан вообще на другом языке программирования: COM не зависит от языка. При этом СОМ - объектная технология, тесно интегрированная в Delphi.<br>
<br>
Я хочу показать, как можно с помощью COM решить задачу создания плагинов, маленьких частей программы, которые могут быть изменены независимо от основной программы. В частности, здесь я рассматриваю, как создать несколько библиотек, по-разному реализующих одну и ту же функциональность. <br>
<br>
Итак, что же такое интерфейсы и с чем их едят? Говоря простым языком, интерфейс COM - это всего лишь структура, которая содержит объявления методов. И не более того. Ближайший аналог в Delphi - объявление класса.<br>
<br>
Для создания плагинов нужно понимать, что у интерфейса нет экземпляра, это просто декларация, которая может быть понята на любом языке программирования, в котором есть возможность работы с COM. Собственно код и данные должны содержаться в объекте, который реализует этот интерфейс, для него даже есть специальное название, кокласс (coclass). И может быть сколько угодно коклассов, реализующих один и тот же интерфейс. Единый интерфейс с различным наполнением - как раз то, что нужно.<br>
<br>
То есть, нужно просто создать ту реализацию интерфейса, которая нужна, а дальше просто общаться с ней через интерфейс, который везде одинаков.<br>
<br>
Давайте рассмотрим конкретный пример. Для этого я взял три метода сортировки массива, которые нагло утянул из проекта в demos&#092;threads. Это BubbleSort, SelectionSort и QuickSort. Потоки и визуализацию рассматривать не будем, по крайней мере, сейчас. Задача стоит в том, чтобы создать три плагина для сортировки массива, реализующих каждый свой метод, и подключать один из них по мере надобности. Разумеется, каждая реализация должна находиться в своей библиотеке (dll).<br>
<br>
<strong class='tag-b'><span class='tag-size' data-value='13' style='font-size:13pt;'>Объекты СОМ</span></strong><br>
<br>
Сначала рассмотрим простейший способ.<br>
<br>
Как можно догадаться, прежде всего нам понадобится интерфейс. Затем - библиотека, содержащая его реализацию. Я предлагаю начать с библиотеки, так удобнее. При этом я не буду выдавать подробные инструкции вроде &quot;мееедленно подведите указатель мышки к пункту меню File...&quot;, я просто покажу, что нужно:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/cXj69a1.gif' alt='user posted image'><br>
<br>
В технологиии COM используются библиотеки специального вида, и при выборе ActiveX Library как раз создается шаблон такой библиотеки. Далее, сразу создаем COM Object с этой же вкладки, при этом среда спрашивает, что именно нужно. Вот это:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/mnNpVfD.gif' alt='user posted image'><br>
<br>
Можно догадаться, что первым будет реализован метод пузырька. Обратите внимание, что Type Library не нужна. В библиотеку добавляется модуль, в котором уже объявлен класс, потомок TComObject. Не хватает только интерфейса. И, поскольку библиотеки типов нет, нужно объявить его вручную, и, желательно, в отдельном модуле для дальнейшего использования. Но предварительно надо сохранить то, что получилось. Я дал название библиотеке BubbleLib, а модулю кокласса - CBubble. Теперь добавим модуль с интерфейсом:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit SortIntf;</div><div class="code_line">&nbsp;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;ISortIntf = interface</div><div class="code_line">&nbsp;&nbsp;[&#39;{BE229ED0-BA7B-11DA-B665-00508B0973BE}&#39;]</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Sort(var A: Variant); stdcall;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">end.</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><br>
Каждый интерфейс идентифицируется своим номером, GUID, который написан в первой его строке. Чтобы его получить, достаточно нажать Ctrl+Shift+G. При этом среда вызывает функцию CoCreateGUID и вставляет его в позицию курсора. Разумеется, при каждом новом вызове выдается новый GUID. Часто GUID интерфейса называют IID, идентификатор интерфейса. Это делается для того, чтобы отличать идентификатор интерфейса от, например, идентификатора кокласса, который обычно называют CLSID. <br>
<br>
Можно заметить, что в метод передается почему-то Variant, а никак не массив. К сожалению, здесь действуют те же правила, что и с обычной dll: передавать только безопасные типы данных. Вот я и собираюсь передавать Safe array.<br>
<br>
Теперь в модуле CBubble дописываем его реализацию (не забыв добавить SortIntf в секцию uses):<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TBubbleSort = class(TComObject, ISortIntf)</div><div class="code_line">&nbsp;&nbsp;protected</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Sort(var A: Variant); stdcall;</div><div class="code_line">&nbsp;&nbsp;end;</div></ol></div></div></div></div><br>
И в разделе <strong class='tag-b'>implementation</strong>:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">uses ComServ, Variants;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TBubbleSort }</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TBubbleSort.Sort(var A: Variant);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;I, J, T: Integer;</div><div class="code_line">&nbsp;&nbsp;HighBound, LowBound: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;HighBound := VarArrayHighBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;LowBound := VarArrayLowBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;for I := HighBound downto LowBound do</div><div class="code_line">&nbsp;&nbsp; &nbsp;for J := LowBound to HighBound - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;if A[J] &#62; A[J + 1] then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;T := A[J];</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;A[J] := A[J + 1];</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;A[J + 1] := T;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;end;</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Я не стал вызывать VarArrayLock, хотя это ускорило бы работу. Весь этот пример создается как простейшая иллюстрация создания и реализации интерфейсов, и не более того. После написания кода остается только вызвать Register ActiveX server для регистрации библиотеки как сервера COM. При этом в моей версии Delphi 6 почему-то создается файл BubbleLib_TLB.pas и в файл проекта записывается строка {&#036;R *.tlb}. Одновременно создается библиотека типов, которая совершенно не нужна. Я так и не нашел причины, по которой она создается. Можно просто удалить файлы BubbleLib.tlb, BubbleLib_TLB.pas и убрать эту строку. Я так и сделал, причем перерегистрировать библиотеку не требуется. Регистрация требуется только если изменились идентификаторы интерфейсов или добавлены новые: все они должны быть записаны в реестре.<br>
<br>
Две оставшиеся библиотеки создаются абсолютно аналогично, только, разумеется, модуль SortIntf просто подключается к проекту. <br>
<br>
Для иллюстрации того, как можно использовать эти три компонента, я сделал маленькое приложение, которое назвал BubbleTest. Создал я его сразу после реализации первой библиотеки, а потом решил не менять название.<br>
<br>
Это обычное приложение с единственной формой, на которую я кинул TMemo, TSpinEdit, TListBox и кнопку. Мемо - для отображения массива, лист - для списка классов, ну а остальное - подручные стредства. Кроме этого, к проекту надо опять же подключить модуль SortIntf, чтобы программа знала, с каким интерфейсом придется работать. А для создания списка классов я просто перенес константы с CLSID (идентификаторами коклассов) из каждой библиотеки в модуль формы. После этого - несколько обработчиков:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">const</div><div class="code_line">&nbsp;&nbsp;Class_BubbleSort: TGUID = &#39;{CFB8F671-BA45-11DA-B665-00508B0973BE}&#39;;</div><div class="code_line">&nbsp;&nbsp;Class_SelectionSort: TGUID = &#39;{BE229ED3-BA7B-11DA-B665-00508B0973BE}&#39;;</div><div class="code_line">&nbsp;&nbsp;Class_QuickSort: TGUID = &#39;{BE229ED4-BA7B-11DA-B665-00508B0973BE}&#39;;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">uses COMObj, SortIntf;</div><div class="code_line">&nbsp;</div><div class="code_line">{$R *.dfm}</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.Button1Click(Sender: TObject);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Bubble: ISortIntf;</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">&nbsp;&nbsp;A: Variant;</div><div class="code_line">&nbsp;&nbsp;HighBound, LowBound: integer;</div><div class="code_line">&nbsp;&nbsp;Class_ID: TGUID;</div><div class="code_line">&nbsp;&nbsp;ElemCount: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;//Получаем нужный CLSID</div><div class="code_line">&nbsp;&nbsp;Class_ID := StringToGUID(ListBox1.Items[ListBox1.ItemIndex]);</div><div class="code_line">&nbsp;&nbsp;//Создаем класс по этому CLSID и сразу просим у него интерфейс ISortIntf</div><div class="code_line">&nbsp;&nbsp;Bubble := CreateCOMObject(Class_ID) as ISortIntf;</div><div class="code_line">&nbsp;&nbsp;ElemCount := SpinEdit1.Value;</div><div class="code_line">&nbsp;&nbsp;//Массив создаем и записываем в мемо</div><div class="code_line">&nbsp;&nbsp;A := VarArrayCreate([0,ElemCount-1], varInteger);</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Clear;</div><div class="code_line">&nbsp;&nbsp;HighBound := VarArrayHighBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;LowBound := VarArrayLowBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;for i := LowBound to HighBound do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;A[i] := random(300);</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(IntToStr(A[i]));</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;&nbsp;//А вот, собственно, и вызов метода плагина</div><div class="code_line">&nbsp;&nbsp;Bubble.Sort(A);</div><div class="code_line">&nbsp;&nbsp;//Остается показать результат</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Add(&#39;Sorted&#39;);</div><div class="code_line">&nbsp;&nbsp;HighBound := VarArrayHighBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;LowBound := VarArrayLowBound(A, 1);</div><div class="code_line">&nbsp;&nbsp;for i := LowBound to HighBound do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(IntToStr(A[i]));</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.FormShow(Sender: TObject);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;with ListBox1.Items do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;Clear;</div><div class="code_line">&nbsp;&nbsp; &nbsp;Add(GUIDToString(Class_BubbleSort));</div><div class="code_line">&nbsp;&nbsp; &nbsp;Add(GUIDToString(Class_SelectionSort));</div><div class="code_line">&nbsp;&nbsp; &nbsp;Add(GUIDToString(Class_QuickSort));</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;&nbsp;ListBox1.ItemIndex := 0;</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Сама форма у меня в результате получилась такая:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/ct8Rqmd.gif' alt='user posted image'><br>
<br>
Теперь можно выбрать любую из трех строк в списке, и при нажатии на кнопку будет загружен нужный плагин, и вызван его метод Sort для сортировки случайного массива из 10 элементов. Собственно, это и все. Освобождать интерфейс явне не надо, при выходе из обработчика интерфейс будет освобожден, и библиотека с коклассом выгрузится. Если нужно освободить интерфейс явно, достаточно присвоить Bubble := nil (интерфейс - это указатель, почти) или просто присвоить ему другой интерфейс. Список CLSID я создал попутно просто для иллюстрации, как обращаться с GUID. Можно догадаться, что в этот список можно загрузить строки, например, из текстового файла, и копирование констант из библиотек прямо в код приложения будет не нужно.<br>
<br>
Теперь посмотрим, что получилось. На первый взгляд, сделанное очень мало отличается от использования трех обычных библиотек, экспортирующих каждая свой метод Sort. Однако, есть и преимущества: во-первых, для работы теста не нужно знать где находится библиотека и как она называется. Нужен только идентификатор нужного класса. Во-вторых, не нужно заботиться о загрузке и выгрузке этой библиотеки, можете быть уверены, что она загрузится когда будет нужна и выгрузится, когда все интерфейсы освободятся. В-третьих, все-таки мы работаем с объектом, хоть и содержащим только один метод. В-четвертых, не нужно заботиться об уничтожении созданного объекта, Delphi делает это автоматически. Далее я рассмотрю, как можно использовать объекты и интерфейсы более полноценным образом.<br>
<br>
<strong class='tag-b'><span class='tag-size' data-value='13' style='font-size:13pt;'>Автоматизация</span></strong><br>
<br>
Разумеется, нельзя не заметить, что все еще имеются некоторые тонкости в использовании плагинов. Все еще нужен модуль с описанием интерфейса, который используется везде, где есть этот интерфейс. Это неудобно тем, что при создании плагина на других языках это описание придется переводить. О CLSID я не говорю: хотя он нужен для создания класса, его всегда можно передать в виде строки. Наконец, въедливый программист может спросить об обработке ошибок в методе Sort. Что будет, если подать на вход не массив, а обычный variant, например, со строкой? Будет выдано исключение, из которого можно понять только его тип, модуль, в котором оно произошло, и адрес. Это не слишком удобно. <br>
<br>
Потому я хочу предложить сделать все по-другому. Помните, я в свое время говорил, что type library не нужна? Использовать ее конечно не обязательно, но очень удобно. Фактически, библиотека типов выполняет почти ту же роль, что и файл SortIntf.pas, но при этом содержит в себе еще и дополнительную информацию, например, описание кокласса. При этом все объявления в библиотеке типов не зависят от языка программирования. Кроме всего сказаного, в VCL содержится множество классов, работающих именно с библиотекой типов и обеспечивающих дополнительные функции, в том числе и обработку исключений.<br>
<br>
Но перед тем, как начинать работу с бибиотекой типов, рекомендуется все хорошо обдумать. Дело в том, что библиотека типов будет использоваться подобно SortIntf.pas, то есть везде, где нужно описание интерфейса. И в случае, когда потребуется изменить эти описания, потребуется повторное подключение этой библиотеки. Прежде всего, надо определить хотя бы примерный состав объектов, которые нужны. Например, в первой части происходила сортировка простого массива чисел. Но на практике сортируют обычно не просто числа, а записи или строки. Да что угодно, лишь бы эти объекты были сравнимы. Поэтому в метод сортировки лучше передавать не массив чисел, а массив объектов, которые должны просто знать, как сравнить себя с другим таким же объектом. Поскольку у нас COM, то вместо объектов будут использоваться, разумеется, интерфейсы.<br>
<br>
Что мне хочется: есть три метода сортировки, значит, надо создать три класса. Если каждый класс будет содержать методы работы с массивом, причем одинаковые, то весьма желательно выделить эту часть в отдельный модуль. Далее все три класса будут использовать то, что в нем есть. Таким образом, нам нужен интерфейс для представления массива и интерфейс для его сортировки.<br>
<br>
Начнем. По аналогии с созданием файла описания интерфейса создадим библиотеку типов. Отдельную от всего, в ней будут храниться только описания интерфейсов. Создать ее можно из той же вкладки ActiveX, элемент так и называется Type Library. Откроется редактор, и в него надо внести наши интерфейсы. При добавлении интерфейса он сразу является потомком IDispatch, как раз то, что требуется. Вот что у меня получилось в итоге:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/ssdtHJp.gif' alt='user posted image'><br>
<br>
Я объявил два интерфейса: IVector, обеспечивающий работу с массивом, и ISort, у которого есть один метод, принимающий на вход массив и сортирующий его. Кроме этого, есть перечисление CompareResult. Дело в том, что методу Sort совсем не обязательно самому сравнивать и переставлять элементы вектора, эти операции можно поручить самому вектору. Поэтому я ввел два метода, которые обеспечивают именно сравнение и перестановку элементов, принимая на вход их индексы. Вот как выглядит метод Compare:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/cNsXPPq.gif' alt='user posted image'><br>
<br>
Кстати, если список параметров выглядит непривычно, то это всего лишь потому, что у меня установлено отображение библиотеки типов на IDL, языке определения интерфейсов. По умолчанию же в Delphi стоит отображение в нотации паскаля. Но в IDL видно, что на самом деле результат любого метода имеет тип HResult. И менять его так просто нельзя, это - соглашение, принятое для интерфейсов автоматизации. Каждый метод должен вернуть свой код завершения. Ничего страшного, параметр Result имеет модификаторы out, retval, и при отображении все будет нормально и привычно (если, конечно, действует соглашение safecall). Вот, кстати, мои настройки:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/zGECVSR.gif' alt='user posted image'><br>
<br>
Интерфейсы, кстати, создались именно дуальные, поэтому методы будут иметь модификатор safecall. <br>
<br>
Так вот, в результате работы метод Compare возвращает константу из набора CompareResult, которая показывает порядок элементов с индексами Index1 и Index2 относительно друг друга. Этого вполне достаточно для большинства методов сортировки.<br>
<br>
После того, как библиотека создана, ее надо сохранить и зарегистрировать. Регистрация - вторая справа иконка, с узнаваемым значком реестра. Собственно, все, что она делает - это именно регистрация библиотеки в реестре. После этого библиотеку можно закрыть и приступить к реализации этих интерфейсов.<br>
<br>
Путь прежний: создаем новую библиотеку типов. Вот только добавлять будем не COM Object, а Automation Object, который работает с библиотекой типов и реализует необходимую функциональность. Сначала создадим объект для интерфейса IVector:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/8XuNbp1.gif' alt='user posted image'><br>
<br>
Будет создан модуль, содержащий в себе класс с именем, как несложно догадаться, TVector, и реализующий интерфейс IVector. Вот только не тот интерфейс, что нам нужен. Дело в том, что для COM не так важны названия интерфейсов, как их идентификаторы. И вот IID этого интерфейса совсем другой. Поэтому открываем библиотеку типов этой ActiveX library (по-английски, а то какая-то тавтология получается) и нагло удаляем оттуда интерфейс IVector, оставив только кокласс Vector. Теперь, чтобы стал известен наш интерфейс, нужно сделать пару не таких уж тривиальных шагов: встать на корень дерева, открыть вкладку uses и в контекстном меню вкладки выбрать &quot;Show all type libraries&quot;. Затем долго листать список, пока не наткнешься на библиотеку SortIntf, и выбрать ее. Если ее там нет, то надо ее зарегистрировать (см выше). После этого снова выбрать в меню пункт &quot;Show selected&quot;. Все, интерфейс стал известен. <br>
<br>
Теперь нужно добавить этот интерфейс на вкладку Implements кокласса Vector:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/KfHwHJQ.gif' alt='user posted image'><br>
<br>
Снова воспользовавшись контекстным меню. Проект, кстати, я назвал VectorLib. Вот и все, остается только нажать на кнопку обновления, и в модуле будут автоматически сгенерированы шаблоны для методов. Кроме, того, в каталоге проекта будет создан файл SortIntf_TLB.pas, представляющий собой описание нашей библиотеки на паскале. Его нужно подключить к проекту, и записать в секцию uses модуля. После этого можно реализовывать методы интерфейса:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit CVector;</div><div class="code_line">&nbsp;</div><div class="code_line">{$WARN SYMBOL_PLATFORM OFF}</div><div class="code_line">&nbsp;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">uses</div><div class="code_line">&nbsp;&nbsp;ComObj, ActiveX, VectorLib_TLB, StdVcl, SortIntf_TLB;</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TVector = class(TAutoObject, IVector)</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;FArray: array of integer;</div><div class="code_line">&nbsp;&nbsp;protected</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Compare(Index1, Index2: Integer): CompareResult; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Get_Count: Integer; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Get_Elem(Index: Integer): OleVariant; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Exchange(Index1, Index2: Integer); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Set_Count(Value: Integer); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Set_Elem(Index: Integer; Value: OleVariant); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;{ Protected declarations }</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">uses ComServ;</div><div class="code_line">&nbsp;</div><div class="code_line">function TVector.Compare(Index1, Index2: Integer): CompareResult;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := crLower;</div><div class="code_line">&nbsp;&nbsp;if FArray[Index1] &#62; FArray[Index2] then</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result := crGreater;</div><div class="code_line">&nbsp;&nbsp;if FArray[Index1] = FArray[Index2] then</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result := crEqual; </div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TVector.Get_Count: Integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := Length(FArray);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TVector.Get_Elem(Index: Integer): OleVariant;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := FArray[Index];</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TVector.Exchange(Index1, Index2: Integer);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;T: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;T := FArray[Index1];</div><div class="code_line">&nbsp;&nbsp;FArray[Index1] := FArray[Index2];</div><div class="code_line">&nbsp;&nbsp;FArray[Index2] := T;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TVector.Set_Count(Value: Integer);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;if Value &#60;&#62; Length(FArray) then</div><div class="code_line">&nbsp;&nbsp; &nbsp;SetLength(FArray, Value);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TVector.Set_Elem(Index: Integer; Value: OleVariant);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FArray[Index] := Value;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">initialization</div><div class="code_line">&nbsp;&nbsp;TAutoObjectFactory.Create(ComServer, TVector, Class_Vector,</div><div class="code_line">&nbsp;&nbsp; &nbsp;ciMultiInstance, tmApartment);</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Немного поясню. Хотя Count и Elem объявлены как свойства (property), их все равно надо реализовывать через методы Get_... и Set_.... Просто потому, что у интерфейса, собственно говоря, данных нет, есть только методы. И, кстати, метод Compare приобрел вполне цивилизованный вид, как, впрочем, и все остальные. Это произошло именно из-за модификатора safecall.<br>
<br>
Реализацию я выбрал самую простую, через динамический массив. Не думаю что нужно комментировать код, все должно быть достаточно понятно. Свойство Elem имеет тип Variant, для универсальности.<br>
<br>
Теперь нужно опять зарегистрировать получившийся сервер, и у нас появится кокласс, который реализует вектор целых чисел.<br>
<br>
Вторая библиотека - реализация интерфейса ISort, который будет сортировать методом пузырька. Создается аналогично предыдущей, только на этот раз кокласс называем &quot;Sort&quot; и, подключив библиотеку SortIntf, реализуем интерфейс ISort:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit CSort;</div><div class="code_line">&nbsp;</div><div class="code_line">{$WARN SYMBOL_PLATFORM OFF}</div><div class="code_line">&nbsp;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">uses</div><div class="code_line">&nbsp;&nbsp;ComObj, ActiveX, SortLib_TLB, StdVcl, SortIntf_TLB;</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TSort = class(TAutoObject, ISort)</div><div class="code_line">&nbsp;&nbsp;protected</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Sort(const Vector: IVector); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;{ Protected declarations }</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">uses ComServ;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TSort.Sort(const Vector: IVector);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;I, J: Integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;for I := Vector.Count - 1 downto 0 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;for J := 0 to Vector.Count - 2 do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;if Vector.Compare(J, J + 1) = crGreater then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Vector.Exchange(J, J + 1);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">initialization</div><div class="code_line">&nbsp;&nbsp;TAutoObjectFactory.Create(ComServer, TSort, Class_Sort,</div><div class="code_line">&nbsp;&nbsp; &nbsp;ciMultiInstance, tmApartment);</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Как можно догадаться, это своеобразная реализация метода пузырька, несмотря на то, что модуль называется CSort. Так получилось, а переименовывать достаточно тяжело.<br>
<br>
И тут же возникает мысль, а дальше? Ну сделаем мы еще пару классов Sort, но понадобится же их как-то опознавать. А при подключении библиотеки можно было видеть, сколько всего есть библиотек типов в системе, не перебирать же их. Разумеется, способ выделить свои классы есть, и называется он Component Categories, то есть категории компонентов. Фактически, это просто способ записи группы интерфейсов в реестр, для которого есть встроенный механизм. И, разумеется, этот механизм основан на интерфейсах. Остается его только задействовать, и лучше всего регистрировать класс в категории одновременно с регистрацией сервера. На самом деле, это даже проще: при регистрации сервера ActiveX в системе вызываются методы UpdateRegistry всех имеющихся фабрик классов. Фабрика, разумеется, у нас создается, обратите внимание на секцию initialization выше. Нужно создать потомок этой фабрики, перекрыв метод UpdateRegistry:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit SortFactory;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">uses COMObj;</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TSortAutoObjectFactory = class(TAutoObjectFactory)</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure UpdateRegistry(Register: boolean); override;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">uses ActiveX, SysUtils, SortConsts;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TSortAutoObjectFactory }</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TSortAutoObjectFactory.UpdateRegistry(Register: boolean);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;CatReg: ICatRegister;</div><div class="code_line">&nbsp;&nbsp;CatInfo: TCATEGORYINFO;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;inherited;</div><div class="code_line">&nbsp;&nbsp;OleCheck(CoCreateInstance(CLSID_StdComponentCategoryMgr, nil,</div><div class="code_line">&nbsp;&nbsp; &nbsp;CLSCTX_INPROC_SERVER, ICatRegister, CatReg));</div><div class="code_line">&nbsp;&nbsp;if Register then</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;CatInfo.catid := CATID_SortServer;</div><div class="code_line">&nbsp;&nbsp; &nbsp;CatInfo.lcid := 19;</div><div class="code_line">&nbsp;&nbsp; &nbsp;StringToWideChar(Sort_CatDesc, CatInfo.szDescription,</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Length(Sort_CatDesc) + 1);</div><div class="code_line">&nbsp;&nbsp; &nbsp;OleCheck(CatReg.RegisterCategories(1, @CatInfo));</div><div class="code_line">&nbsp;&nbsp; &nbsp;OleCheck(CatReg.RegisterClassImplCategories(ClassID, 1, @CATID_SortServer));</div><div class="code_line">&nbsp;&nbsp;end else</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;OleCheck(CatReg.UnRegisterClassImplCategories(ClassID, 1, @CATID_SortServer));</div><div class="code_line">&nbsp;&nbsp; &nbsp;DeleteRegKey(Format(&#39;CLSID\%s\Implemented Categories&#39;, [GUIDToString(ClassID)]));</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Разумеется, категория тоже распознается по ее GUID, но кроме этого у нее есть и описание на нормальном языке. Обе этих константы я вынес в отдельный файл, по той причине, что все приложения должны знать, в какой категории искать классы сортировки:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit SortConsts;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">const</div><div class="code_line">&nbsp;&nbsp;CATID_SortServer: TGUID = &#39;{782D841E-BB21-11DA-B666-00508B0973BE}&#39;;</div><div class="code_line">&nbsp;&nbsp;Sort_CatDesc = &#39;Библиотеки сортировки. Тестовый пример&#39;;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Теперь остается подменить тип библиотеки в модуле CSort:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">initialization</div><div class="code_line">&nbsp;&nbsp;TSortAutoObjectFactory.Create(ComServer, TSort, Class_Sort,</div><div class="code_line">&nbsp;&nbsp; &nbsp;ciMultiInstance, tmApartment);</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Вот и все. Вторая библиотека, SelSortLib, создана аналогично, я покажу только метод Sort:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">procedure TSort.Sort(const Vector: IVector);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;I, J: Integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;for I := 0 to Vector.Count - 2 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;for J := Vector.Count - 1 downto I + 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;if Vector.Compare(I,J) = crGreater then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;Vector.Exchange(I,J);</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Если честно, третью мне создавать уже как-то и не хочется, тем более что для иллюстрации материала вполне достаточно. <br>
<br>
Перейдем сразу к тестовому приложению. Создаем обычное приложение с формой, и делаем Import Type library, выбрав из списка SortIntf... Хотя погодите. Для окончательной иллюстрации мне еще хочется реализовать вектор действительных чисел. Сделаю это я прямо в этом приложении. Впрочем, импорт ничему не мешает, сделал так сделал. Выбираем опять создание Automation Object, прямо в приложении, подключаем SortIntf, и реализуем IVector. Вот только создан-то объект, к которому можно обратиться снаружи, а мне это не нужно. Ничего, как говорится, нет Factory - нет кокласса (я немного переиначил). Но отключить ее не совсем правильно, нужно сделать еще пару действий: во-первых, поменять тип кокласса на TAutoIntfObject. Это класс, который реализует интерфейсы - потомки IDispatch (у нас именно такие), но при этом у него нет фабрики, то есть, кокласс должен создаваться непосредственно внутри приложения. Во-вторых, как ни странно, убрать type library с коклассом и все воспоминания о ней, то есть использование COMServ и использование модуля импорта библиотеки. И, разумеется, убрать строчку {&#036;R *.tlb} из проекта. Этого достаточно. Теперь есть класс вектора, который недоступен снаружи, но полностью доступен самому приложению. Вот какой кокласс у меня получился:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit CDVector;</div><div class="code_line">&nbsp;</div><div class="code_line">{$WARN SYMBOL_PLATFORM OFF}</div><div class="code_line">&nbsp;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">uses</div><div class="code_line">&nbsp;&nbsp;ComObj, ActiveX, StdVcl, SortIntf_TLB;</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TDVector = class(TAutoIntfObject, IVector)</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;FArray: array of Double;</div><div class="code_line">&nbsp;&nbsp;protected</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Compare(Index1, Index2: Integer): CompareResult; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Get_Count: Integer; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function Get_Elem(Index: Integer): OleVariant; safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Exchange(Index1, Index2: Integer); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Set_Count(Value: Integer); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Set_Elem(Index: Integer; Value: OleVariant); safecall;</div><div class="code_line">&nbsp;&nbsp; &nbsp;{ Protected declarations }</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure FillRandom(Size: integer);</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">function TDVector.Compare(Index1, Index2: Integer): CompareResult;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := crLower;</div><div class="code_line">&nbsp;&nbsp;if FArray[Index1] &#62; FArray[Index2] then</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result := crGreater;</div><div class="code_line">&nbsp;&nbsp;if FArray[Index1] = FArray[Index2] then</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result := crEqual; </div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TDVector.Get_Count: Integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := Length(FArray);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TDVector.Get_Elem(Index: Integer): OleVariant;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := FArray[Index];</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TDVector.Exchange(Index1, Index2: Integer);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;T: Double;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;T := FArray[Index1];</div><div class="code_line">&nbsp;&nbsp;FArray[Index1] := FArray[Index2];</div><div class="code_line">&nbsp;&nbsp;FArray[Index2] := T;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TDVector.Set_Count(Value: Integer);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;if Value &#60;&#62; Length(FArray) then</div><div class="code_line">&nbsp;&nbsp; &nbsp;SetLength(FArray, Value);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TDVector.Set_Elem(Index: Integer; Value: OleVariant);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FArray[Index] := Value;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TDVector.FillRandom(Size: integer);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;SetLength(FArray, Size);</div><div class="code_line">&nbsp;&nbsp;for i := Low(FArray) to High(FArray) do</div><div class="code_line">&nbsp;&nbsp; &nbsp;FArray[i] := Random;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Я просто скопировал все методы из CVector, поменяв пару слов. Кроме того, мне показалось скучным полностью повторять реализацию, объект-то доступен. Поэтому я добавил метод, который заполняет массив случайными числами, для иллюстрации того, что кокласс все же является обычным классом Delphi.<br>
<br>
Осталось сделать простейший интерфейс:<br>
<br>
<img class='tag-img' src='http://i.imgur.com/vthmgbT.gif' alt='user posted image'><br>
<br>
Это - форма уже работающего приложения. А вот ее код:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">unit FSort;</div><div class="code_line">&nbsp;</div><div class="code_line">interface</div><div class="code_line">&nbsp;</div><div class="code_line">uses</div><div class="code_line">&nbsp;&nbsp;Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,</div><div class="code_line">&nbsp;&nbsp;Dialogs, StdCtrls, Spin, SortIntf_TLB;</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;TForm1 = class(TForm)</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1: TMemo;</div><div class="code_line">&nbsp;&nbsp; &nbsp;ListBox1: TListBox;</div><div class="code_line">&nbsp;&nbsp; &nbsp;btnInt: TButton;</div><div class="code_line">&nbsp;&nbsp; &nbsp;btnDouble: TButton;</div><div class="code_line">&nbsp;&nbsp; &nbsp;SpinEdit1: TSpinEdit;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure FormCreate(Sender: TObject);</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure btnDoubleClick(Sender: TObject);</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure btnIntClick(Sender: TObject);</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure ShowSort(Vector: IVector);</div><div class="code_line">&nbsp;&nbsp; &nbsp;{ Private declarations }</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;{ Public declarations }</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Form1: TForm1;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">uses SortConsts, CDVector, COMObj, ActiveX, VectorLib_TLB;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure ListSortServers(List: TStrings);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;EnumGUID: IEnumGUID;</div><div class="code_line">&nbsp;&nbsp;Fetched: Cardinal;</div><div class="code_line">&nbsp;&nbsp;Guid: TGUID;</div><div class="code_line">&nbsp;&nbsp;CatInfo: ICatInformation;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;List.Clear;</div><div class="code_line">&nbsp;&nbsp;OleCheck(CoCreateInstance(CLSID_StdComponentCategoryMgr, nil,</div><div class="code_line">&nbsp;&nbsp; &nbsp;CLSCTX_INPROC_SERVER, ICatInformation, CatInfo));</div><div class="code_line">&nbsp;&nbsp;OleCheck(CatInfo.EnumClassesOfCategories(1, @CATID_SortServer, 0, nil, EnumGUID));</div><div class="code_line">&nbsp;&nbsp;while EnumGUID.Next(1, Guid, Fetched) = S_OK do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;//Переводим CLSID в более понятный вид</div><div class="code_line">&nbsp;&nbsp; &nbsp;List.Add(ClassIDToProgID(Guid));</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{$R *.dfm}</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.FormCreate(Sender: TObject);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;ListSortServers(ListBox1.Items);</div><div class="code_line">&nbsp;&nbsp;if ListBox1.Items.Count &#62; 0 then</div><div class="code_line">&nbsp;&nbsp; &nbsp;ListBox1.ItemIndex := 0;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.btnDoubleClick(Sender: TObject);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;TypeLib: ITypeLib;</div><div class="code_line">&nbsp;&nbsp;Vector: IVector;</div><div class="code_line">&nbsp;&nbsp;VecObj: TDVector;</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;//Нужна библиотека типов, в которой описан IVector</div><div class="code_line">&nbsp;&nbsp;OleCheck(LoadRegTypeLib(LIBID_SortIntf, 1, 0, 0, TypeLib));</div><div class="code_line">&nbsp;&nbsp;VecObj := TDVector.Create(TypeLib, IVector);</div><div class="code_line">&nbsp;&nbsp;VecObj.FillRandom(SpinEdit1.Value);</div><div class="code_line">&nbsp;&nbsp;Vector := VecObj as IVector;</div><div class="code_line">&nbsp;&nbsp;VecObj := nil; //От греха</div><div class="code_line">&nbsp;&nbsp;ShowSort(Vector);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.btnIntClick(Sender: TObject);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Vector: IVector;</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Vector := CoVector.Create;</div><div class="code_line">&nbsp;&nbsp;Vector.Count := SpinEdit1.Value;</div><div class="code_line">&nbsp;&nbsp;for i := 0 to Vector.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Vector.Elem[i] := Random(300);</div><div class="code_line">&nbsp;&nbsp;ShowSort(Vector);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TForm1.ShowSort(Vector: IVector);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">&nbsp;&nbsp;SortObj: ISort;</div><div class="code_line">&nbsp;&nbsp;ProgID: string;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Clear;</div><div class="code_line">&nbsp;&nbsp;for i := 0 to Vector.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(FormatFloat(&#39;##0.#####&#39;, Vector.Elem[i]));</div><div class="code_line">&nbsp;&nbsp;//Создаем выбранный класс сортировки и сортируем</div><div class="code_line">&nbsp;&nbsp;ProgID := ListBox1.Items[ListBox1.ItemIndex];</div><div class="code_line">&nbsp;&nbsp;SortObj := CreateComObject(ProgIDToClassId(ProgID)) as ISort;</div><div class="code_line">&nbsp;&nbsp;SortObj.Sort(Vector);</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Add(&#39;Sorted&#39;);</div><div class="code_line">&nbsp;&nbsp;for i := 0 to Vector.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(FormatFloat(&#39;##0.#####&#39;, Vector.Elem[i]));</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">end.</div></ol></div></div></div></div><br>
Здесь, думаю, необходимы пояснения, и довольно подробные. ListSortServers перечисляет все классы, зарегистрированные в нашей категории, и записывает не непонятную строку CLSID, а более-менее воспринимаемую строку ProgID. Это именно та строка, которая подается в функцию CreateOleObject. Да-да, все интерфейсы у нас дуальные, так что вполне возможно позднее связывание, без всякого импорта библиотеки типов. Конечно, при этом вызовы методов идут гораздо дольше, зато не надо делать импорт библиотеки типов. Здесь же я импортировал и подключил библиотеку VectorLib, для того чтобы создать и использовать вектор целых чисел.<br>
<br>
Вектор же действительных чисел я получаю прямо из объекта, причем достаточно непривычным образом. Рассмотрим код подробно:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">&nbsp;&nbsp;//Нужна библиотека типов, в которой описан IVector</div><div class="code_line">&nbsp;&nbsp;OleCheck(LoadRegTypeLib(LIBID_SortIntf, 1, 0, 0, TypeLib));</div><div class="code_line">&nbsp;&nbsp;VecObj := TDVector.Create(TypeLib, IVector);</div><div class="code_line">&nbsp;&nbsp;VecObj.FillRandom(SpinEdit1.Value);</div><div class="code_line">&nbsp;&nbsp;Vector := VecObj as IVector;</div><div class="code_line">&nbsp;&nbsp;VecObj := nil; //От греха</div></ol></div></div></div></div><br>
Чтобы создать объект типа TAutoIntfObject, в его конструктор нужно подать ссылку на интерфейс нужной библиотеки типов и IID того интерфейса, который он должен реализовать. Обычно в качестве первого параметра достаточно подать COMServer.TypeLib, но в данном случае это не подходит: библиотеки типов нет, а когда она была, все равно там не было описания IVector. Поэтому нужно непосредственно подать интерфейс SortIntf.tlb. К счастью, она зарегистрирована, и нужно ее просто загрузить с получением ее интерфейса. Это и делается в первой строке. Во второй строке создается экземпляр кокласса (компилятор преобразует интерфейс к его IID во втором параметре), и далее вызывается метод класса для заполнения массива. Затем уже из самого класса получается интерфейс IVector. VecObj := nil я написал специально. Дело в том, что при доступе через интерфейс кокласс, как правило, сам следит за тем, когда ему уничтожится, точнее, этим управляет интерфейс. Поэтому пользоватся одновременно и экземпляром класса, и его интерфейсом нужно крайне аккуратно.<br>
<br>
Остальные методы практически мало отличаются от первого примера, пожалуй, единственное отличие в том, что clsid получается из идентификатора программы. Если применяется позднее связывание, этого можно и не делать, вот, например, как можно переписать работу с массивом:<br>
<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">procedure TForm1.btnInt2Click(Sender: TObject);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;V: variant;</div><div class="code_line">&nbsp;&nbsp;Sort: Variant;</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;V := CreateOleObject(&#39;VectorLib.Vector&#39;);</div><div class="code_line">&nbsp;&nbsp;V.Count := SpinEdit1.Value;</div><div class="code_line">&nbsp;&nbsp;for i := 0 to V.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;V.Elem[i] := Random(300);</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Clear;</div><div class="code_line">&nbsp;&nbsp;for i := 0 to V.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(FormatFloat(&#39;##0.#####&#39;, V.Elem[i]));</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;Sort := CreateOleObject(ListBox1.Items[ListBox1.ItemIndex]);</div><div class="code_line">&nbsp;&nbsp;Sort.Sort(V);</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;Memo1.Lines.Add(&#39;Sorted&#39;);</div><div class="code_line">&nbsp;&nbsp;for i := 0 to V.Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Memo1.Lines.Add(FormatFloat(&#39;##0.#####&#39;, V.Elem[i]));</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Конечно, позднее связывание менее эффективно, но зато этот метод дает возможность использовать классы в языках программирования, которые не поддерживают раннего связывания, например, в VBScript. К тому же, не требуется импортировать библиотеку типов, что может быть весьма удобным, если нужно написать всего несколько строк с использованием интерфейса.<br>
<br>
<strong class='tag-b'><span class='tag-size' data-value='13' style='font-size:13pt;'>Заключение</span></strong><br>
<br>
Подведем итоги. Я показал пару способов организовать приложение в виде отдельных специализированных частей, достаточно активно взаимодействующих друг с другом. Конечно, можно было реализовать эти классы более эффективно и гибко, но я думаю, что и написанного вполне достаточно для понимания общих принципов. Все остальное, при желании, можно найти в интернете. Я также не коснулся вопроса о том, как делать визуальные плагины на основе ActiveX Form, по той причине, что это предмет для особого разговора.<br>
<br>
<em class='tag-i'>Роман Игнатьев, 2006, romkin[at]pochta.ru</em><br>
<br>
<strong class='tag-b'><span class='tag-size' data-value='13' style='font-size:13pt;'>Приложения</span></strong><br>
<ol class="tag-list" type="1"><li><a class='tag-url' href='http://forum.sources.ru/index.php?act=Attach&type=post&id=1463972&attach_id=40298' target='_blank'>Первая часть, COMObject</a> (7 K)</li><li><a class='tag-url' href='http://forum.sources.ru/index.php?act=Attach&type=post&id=1463972&attach_id=48783' target='_blank'>Библиотеки второй части, Automation</a> (12 K)</li><li><a class='tag-url' href='http://forum.sources.ru/index.php?act=Attach&type=post&id=1463972&attach_id=48784' target='_blank'>Тестовый клиент для Automation</a> (9 K)</li></ol><br>
<span class="b-attach" data-size="10570" data-hits="578" data-attach-id="50441" data-attach-post-id="1463972">
			<span class="b-attach__title"></span><a class='b-attach-link' href='https://forum.sources.ru/index.php?act=Attach&amp;type=post&amp;id=1463972&amp;attach_id=50441' title='Скачать файл' target='_blank'>activelib.gif</a> (, : 578)
		</span>]]></description>
        <author>Krid</author>
        <category>ActiveX, COM, DCOM, CORBA, MIDAS, интерфейсы, OLE, DDE</category>
      </item>
	
      </channel>
      </rss>
	