Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2002.10.03;
Скачать: [xml.tar.bz2];

Вниз

COM. Раннее связывание и ID-связывание. Есть ли реальное отличие?   Найти похожие ветки 

 
Aleksey Pavlov   (2002-09-19 12:21) [0]

Приветствую Мастаков.
На днях, в связи с прочтением одной заметки, возник такой вопрос: какое отличие между ID и ранним связыванием (применительно к технологии COM)?
Я, вообще говоря, всегда думал, что раннее связывание и ID-связывание - это синонимы, т.к. при раннем связывании идентификаторы имён (ID of names) разрешаются на стадии компиляции и заносятся в VTable. Затем через интерфейс VTable осуществляется значительно более быстрый доступ к серверу автоматизации, чем через DispInterface, т.к. нам нет необхрдимости выполнять ф-ию GetIdOfNames интерфейса IDispatch.

В связи с этим, было высказанно мнение, что ID-связывание - это что-то промежуточное между ранним и поздним видом связывания, и что при использовании ID-связывании из Delphi необходимо применять DispInterface, т.к. в данном случае вызов производится через методы IDispatch.

Прошу прояснить ситуацию.
Заранее благодарен.


 
Polevi   (2002-09-19 12:39) [1]

GetIdsOfNames не нужен


 
Digitman   (2002-09-19 12:50) [2]

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-компоновки модулей К и С в ВАП процесса.


 
Aleksey Pavlov   (2002-09-19 13:15) [3]

>>Digitman © (19.09.02 12:50):
Всё Вами сказанное верно. Но мне хотелось бы просто узнать, правомерно ли называть раннее связывание ID-связыванием, как это, к примеру, делается у Тейксейры и Пачеко или же всё-таки стоит разделять эти понятия?


 
Digitman   (2002-09-19 13:38) [4]

>Aleksey Pavlov

Я не читал соотв.терминологии и пояснений от Тейксейры и Пачеко, но как бы эти понятия не выглядели в чьих-либо устах, разделять и понимать их все же следует, наверно, именно так, как ты себе это представляешь на дан.момент. Суть остается сутью)...


 
Romkin   (2002-09-19 14:54) [5]

Не путайтесь в понятиях. Все дело в том, что родоначальником интерфейсов в 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 15:05) [6]

>Romkin

Вот и я о том же) ..

мало ли как обзывают сабж ! гораздо важней понимание, что и как работает... только при полном понимании можно делать выбор наиболее эффективного способа связывания для конкретных условий взаимодействия К. и С.

Ну и маршаллинг, конечно ... если он необходим, нужно понимать, зачем, почему и как он организован (штатными ли, собственными ли средствами) и работает в тех или иных условиях...


 
Набережных С.   (2002-09-19 15:49) [7]


> Romkin © (19.09.02 14:54)


> (с ранним связыванием - никогда),

Это еще почему?


 
Digitman   (2002-09-19 16:25) [8]

Знаете ли, господа, коль скоро речь зашла о терминах и их знпении, не поленюсь привести вот такую цитатку с определениями раннего и позднего связывания :

"При управлении сервером автоматизации посредством интерфейса используется раннее связывание (early binding). Термином раннее связывание обозначается такая методика создания программы, при которой все обращения к методам интерфейса проверяются на корректность передачи параметров еще на стадии компиляции.
...
При управлении сервером автоматизации посредством переменных типа variant используется так называемое позднее связывание (late binding). Термином позднее связывание обозначается такая методика создания программы, при которой все обращения к методам интерфейса не проверяются на корректность передачи параметров до тех пор, пока не наступит время выполнения программы"


Цитировано из вот этого букваря :

(С) Эрик Хармон
"Разработка СОМ-приложений в среде Delphi"
Профессиональное руководство по созданию масштабируемых COM/DCOM-приложений в среде Delphi версий 3-5

P.S. Думаю, мои собственные определения и понимание данных терминов концептуально совпадают с процитированными авторскими. И совпадали еще до знакомства с этим автором.


 
Romkin   (2002-09-19 16:44) [9]

Хм... Кажется, насчет раннего связывания не то сказал...
А вот с Эриком Хармоном я не совсем согласен, он объединил обращение к VTable и dispinterface в раннее связывание, а это все-таки очень разные вещи :-))
Мне больше нравится объяснение здесь
http://www.techvanguards.com/com/concepts/automation.asp


 
Digitman   (2002-09-19 17:01) [10]

>Romkin


> он объединил обращение к VTable и dispinterface в раннее связывание


Ты что-то путаешь) ... Где ты увидел слово dispinterface (или IDispatch) в его определении раннего связывания ? Он говорит о интерфейсе как таковом, что в случае раннего связывания (под Делфи - в том числе) следует интерпретировать не иначе как любого потомка IUnknown. В т.ч. - и IDispatch. При этом ни GetIDsOfNames() ни Invoke() попросту не задействуются никак, поскольку в этом нет необходимости : адреса ф-ций, реализующих интерфейсные методы, известны компилятору (благо их тела будут размещены в том же АП, что и АП процесса-контроллера) и он генерирует объектный код, способный после run-time-сборки вызывать эти ф-ции непосредственно через ссылки в VTable.


 
Romkin   (2002-09-19 17:13) [11]

Термином позднее связывание обозначается такая методика создания программы, при которой все обращения к методам интерфейса не проверяются на корректность передачи параметров до тех пор, пока не наступит время выполнения программы -
Вот что я имел в виду. Если я импортирую библиотеку типов, то ВСЕ обращения к методам интерфейса будут проверены на этапе компиляции а между тем вызовы будут идти через invoke (сам постоянно так делаю, обращаюсь именно к dispinterface)
Да и далее в главе 4 он показывает тесты для VTable, Variant (а вместо него можно подставить IDispatch) и dispinterface... как сочетается у него проверка параметров методов по экспортированной библиотеке типов и это определение - не ясно


 
vuk   (2002-09-19 17:16) [12]

to Digitman:
>При интерпроцессном взаимодействии между К и С ни о каких
>VTable речи быть не может
Ну почему же, очень даже может. Не верите - проверьте скорость взаимодействия с тем же Excel c использованием раннего и позднего связывания. Более того, и между процессами и с использованием DCOM тоже возможно взаимодействие через чистые VTable интерфейсы (даже не dual). Другое дело, что Delphi этого сделать не позволяет, поскольку для этого нужен компилятор MIDL и компилятор С для генерации dll заглушки.

to Aleksey Pavlov:
Как я Вам писал ранее, я тоже не могу согласиться с тем, что ID связывание можно называть ранним. С его использованием взаимодействие хотя и осуществляется быстрее, но ранним(как в случае с vtable) оно не является, поскольку вызов все равно идет через Invoke, а не напрямую.



 
Digitman   (2002-09-19 17:42) [13]

>vuk

О VTable (в принципе !) может идти речь только тогда. когда К. и С. находятся в едином ВАП. С каких это пор Ecxcel (как С.) стал присутствовать в АП твоего процесса (как К.) ? Возрази ...

>Romkin

> Если я импортирую библиотеку типов, то ВСЕ обращения к методам
> интерфейса будут проверены на этапе компиляции а между тем
> вызовы будут идти через invoke


Кто ж спорит ?) Но ведь никто не заставляет тебя в библиотеке типов держать диспинтерфейсы ! И как, позволь полюбопытствовать, в таком случае вызов интерфейсного метода из этой импортированной бтбл-ки сочетается с GeInvoke() и GetIDsOfNames() ? Как вот ты себе это видишь, если БТ не декларирует ни один диспинтерфейс ?


 
Romkin   (2002-09-19 17:48) [14]

Если есть IDispatch, я всегда могу вызвать методы GetIDsOfNames & Invoke. При чем здесь dispinterface?


 
Digitman   (2002-09-19 18:03) [15]

давайте (бедный Эхел ! в который уже раз его...) "изнасилуем" незабвенный 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



 
Digitman   (2002-09-19 18:10) [16]


> Romkin © (19.09.02 17:48)
> Если есть IDispatch, я всегда могу вызвать методы GetIDsOfNames
> & Invoke. При чем здесь dispinterface?


а это уже ты компилятор напрягаешь (зачастую - не по делу): раз ты заикнулся о IDispatch (преобразуешь некий интерфейсный тип к данному типу), компилятор правомерно считает, что при последующих обращениях к методам/св-вам такого интерфейса ему следует, обратившись к TLB-прототипу, взять оттуда соответствующий ID и подствить его в безусловно генерируемый код вызова Invoke(ID).


 
Romkin   (2002-09-19 18:25) [17]

Вообще-то я всегда считал, что раннее связывание - это обращение к VTable, позднее - Invoke... Может быть, я ошибаюсь... Кстати, _Application & _WorkBook - это не dispinterface, dispinterface"s соотв _ApplicationDisp & _WorkBookDisp


 
vuk   (2002-09-19 18:40) [18]

>ExcelAppDispIntf : _Application;
А теперь посмотрите как пойдут вызовы к этому интерфейсу (откройте CPU view). Через VTable, не так ли? И как это соотносится с Вашими прошлыми утверждениями?


 
Digitman   (2002-09-19 18:44) [19]

>Romkin

Ну, я ж просил не придираться) ... Ну пусть будет disp - это ничего не меняет, мало ли как можно обозвать интерфейс в TLB).. Видишь же, что приводим его к типу IDispatch - значит, речь идет именно о диспинтерфейсах) ... назови ты их хоть хреном)))

ну, давай так тогда - на огурцах определение дадим всей этой ботве :

- при выборе методологии интерпроцессного взаимодействия (однозначно ведущей к маршаллингу) раннее связ-е отличается от позднего тем, что при раннем разрешение имени в ID выполняет компилятор, а при позднем - код процесса, вынужденный вызывать GetIdsOfNames(); здесь без safecall-соглашения никак не обойтись;

- при выборе методологии интрапроцессного взаимодействия (где маршаллинг нужен как 5-е колесо у телеги) в принципе все выглядит точно так же, но раннее связ-е кр.того может быть организовано и посредством VTable, и тогда в реальности интерфейсные вызовы выглядят в объектном коде как вызовы обычных процедур/ф-ций (и ни какого, к черту, маршаллинга не нужно - все вызовы происходят непосредственно и, значит, максимально быстро/эффективно); и забудем как кошмарный сон этот самый safecall - исключения мы и так поймаем, а типы параметров-результатов нам известны из TLB


 
Digitman   (2002-09-19 18:47) [20]

>vuk

Вопрос тебе "в лоб" : для чего предназначена VTable ? Что она хранит в run-time ?


 
vuk   (2002-09-19 18:53) [21]

VTable (как и VMT) хранит адреса методов. В случае сервера вне процесса там находятся адреса функций - заглушек, при помощи которых осуществляется маршалинг вызовов в другой процесс.


 
Набережных С.   (2002-09-19 18:54) [22]

Весело тут у вас:)))

> Digitman © (19.09.02 17:42)
> >vuk
>
> О VTable (в принципе !) может идти речь только тогда. когда
> К. и С. находятся в едином ВАП. С каких это пор Ecxcel (как
> С.) стал присутствовать в АП твоего процесса (как К.) ?
> Возрази ...

Можно я?:)))
Клиент ведь ВСЕГДА обращается к методам, находящимся в его АП, где бы ни находился сервер - хоть в том-же контексте, хоть на луне :) Просто в последнем случае это будет метод не самого объекта, а его представителя. Proxy - обычный COM-объект, выставляющий интерфейс(или несколько - это детали), семантически эквивалентный одному из интерфейсов замещаемого объекта. В нем точно также используется VTbl. Пройдя через механизмы RPC, вызов попадает в заглушку, которая через интерфейс уже обращается к объекту(опять-же - VTbl). И, кстати, методы IDispatch вызываются так-же - а как же иначе?.


 
Набережных С.   (2002-09-19 18:56) [23]

Быстро, однако, посты добавляются:)


 
Набережных С.   (2002-09-19 19:04) [24]


> Digitman © (19.09.02 18:44)


> - при выборе методологии интрапроцессного взаимодействия
> (где маршаллинг нужен как 5-е колесо у телеги)

Ты, вероятно, имел в виду внутриконтекстный вызов?


 
Digitman   (2002-09-19 19:04) [25]

Вообще говоря, господа, MS дает вот такие пояснения :

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/office97/html/SF7A4.asp

и здесь я, видимо, расхожусь с ним во мнении

>vuk
> VTable (как и VMT) хранит адреса методов. В случае сервера
> вне процесса там находятся адреса функций - заглушек, при
> помощи которых осуществляется маршалинг вызовов в другой
> процесс.

Да, заглушки, это верно. Но каким образом (в принципе !) маршаллинг может осуществляться без ID ? Вот как ты себе это мыслишь ?


 
vuk   (2002-09-19 19:08) [26]

>Но каким образом (в принципе !) маршаллинг может осуществляться
>без ID ? Вот как ты себе это мыслишь ?
1. Известно смещение метода в VTable
2. Определен формат вызова методов.
3. Известо количество и типы параметров (tlb)

Мне кажется, что достаточно.


 
vuk   (2002-09-19 19:10) [27]

Да, забыл. Если нет tlb, то маршалингом занимается dll-заглушка.


 
Набережных С.   (2002-09-19 19:26) [28]


> vuk © (19.09.02 19:10)
> Да, забыл. Если нет tlb, то маршалингом занимается dll-заглушка.

А если есть, то кто? Иван Федорович Крузенштерн?:)


 
vuk   (2002-09-19 19:31) [29]

Если есть tlb и используется Automation, то маршалингом занимается подсистема COM Automation. Под dll-заглушкой я имел в данном случае в виду специальную dll, исходник которой формируется MIDL компилятором по idl-описанию интерфейса.


 
Набережных С.   (2002-09-19 20:01) [30]


> подсистема COM Automation

Согласись, это несколько расплывчато. Маршаллингом в любом случае занимается Proxy/Stab DLL. Просто в случае Automation для этого используется обычно(но не обязательно) стандартный маршаллер, использующий в работе информацию из библиотеки типов и находящийся в файле oleaut32.dll. Он же используется, если интерфейс помечен как совместимый с автоматизацией.


 
vuk   (2002-09-19 20:04) [31]

>Согласись, это несколько расплывчато.
Соглашаюсь. :o) Просто в лом это все писать было. :o)


 
Набережных С.   (2002-09-19 20:19) [32]


> vuk © (19.09.02 20:04)

:)))
Я и сам такой:))


 
Digitman   (2002-09-20 08:20) [33]

>Набережных С.

Да, внутриконтекстный.

>All
Вот и получается : какой бы и где бы ни был маршаллер, в любом случае на этом этапе трансляции интерфейсного вызова без ID никак не обойтись. Получается, что обращение К. непосредственно к VTable есть суть раннее связывание, в то время как последующаяя трансляция вызова (после VTable-преобразования) все равно приводит к маршаллингу (пусть и стандартному), использующему так или иначе internal-механизм работы диспинтерфейса (а это как бы, по MS, уже есть суть позднее связывание).
Так - чему же верить, господа ?


 
Romkin   (2002-09-20 10:54) [34]

2Digitman как я уже постил, полностью согласен с
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/office97/html/SF7A4.asp
однако в Windows SDK нашел следующее определение:
late binding:
The ability to bind names to IDs at run time, rather than compile time.
В принципе, согласуется, речь ведь идет о возможности, но трактовать можно по-разному.
А насчет механизма dispinterface, так interface & dispinterface - разные вещи, и не надо их путать. В ссылке на techvanguards, которую я дал выше, ни слова об dispinterface...


 
vuk   (2002-09-20 11:19) [35]

to Digitman:
>Вот и получается : какой бы и где бы ни был маршаллер, в любом
>случае на этом этапе трансляции интерфейсного вызова без ID
>никак не обойтись.
Не получается. Потому, что если используются не automation интерфейсы, то, как я уже написал, маршалингом занимается dll-посредник, которая никаких механизмов автоматизации внутри не использует. Потому, что использовать просто нечего.

>в то время как последующаяя трансляция вызова (после VTable-
>преобразования) все равно приводит к маршаллингу (пусть и
>стандартному), использующему так или иначе internal-механизм
>работы диспинтерфейса
Вот на этот счет я очень сильно не уверен. Далее будут только мои предположения, т.к. я не знаю внутренних механизмов COM. Скорее всего дело обстоит следующим образом: когда клиент создает объект автоматизации вне процесса, подсистема на основании информации из tlb динамически генерирует для каждого интерфейса заглушку, которая на каждой стороне предоставляет возможность работы через VTable интерфейс. Что, в общем-то подтверждается более всокой скоростью работы при использовании VTable доступа к дуальным интерфейсам. На самом деле все это довольно-таки легко проверить. Достаточно посмотреть - приходят ли вызовы в метод Invoke при клиентских вызовах методов через VTable.



 
DiamondShark   (2002-09-20 12:29) [36]


> vuk © (20.09.02 11:19)


Не приходят.


 
Digitman   (2002-09-20 12:55) [37]

>vuk

>если используются не automation интерфейсы, то.. маршалингом занимается dll-посредник

Это и не вызывает сомнения. Вопрос лишь в том, КАК она это делает) ... если С. работает в ином ВАП ...
И пусть это будет даже не ID-механизм в "чистом" виде (как обязательный как бы атрибут ole automation), все равно взаимодействие между Proxy и Stub (в случае их нахождения в разных ВАП) на этапе маршаллинга происходит с использованием неких предопределенных тем или иным образом уникальных идентификаторов интерфейсных методов: DLL-посредник, отрабатывая вызов Контроллером конкретной своей Stub, формирует маршал-поток, содержащий в т.ч. и в 1-ю очередь некий идент-р метода, и передает его (средствами OLE/COM API) адресату - экземпляру Proxy в ВАП Сервера. Последний же есть по сути обычный маршал-интерпретатор : из потока извлекается тот самый ID, извлекаются параметры, ID"у ставится в соответствие актуальный адрес метода в собственном же ВАП, в TLS настраиваются точки фиксации исключений, параметры и "заготовка" для результата помещаются в стек, и управление передается непосредственно методу. Обратный ход практически не отличается от прямого хода - теперь Proxy и Stub меняются местами в этой цепочке : Proxy выполняет маршаллинг результата, результирующий поток теми же средствами передается в ВАП Stub"а, а Stub выполняет демаршаллинг-интерпретацию результирующего потока, либо возвращая через стек результат и управление коду, вызвавшему Stub-процедуру DLL-посредника, либо генерируя исключение.

Вот так я себе это вижу.


 
vuk   (2002-09-20 13:56) [38]

to DiamondShark:
Что и требовалось доказать.

to Digitman:
>Вот так я себе это вижу.
На самом деле там кроме RPC ничего нет.


 
Digitman   (2002-09-20 15:16) [39]

>vuk
а RPC ,по-твоему, не имеет никакого отношения к маршаллингу ?)


 
Набережных С.   (2002-09-20 15:21) [40]

Здравия желаю!

Когда вызов приходит в заглушку, в ней вызывается метод IRpcStubBuffer.Invoke(var msg:TRPCOLEMESSAGE, Channel:IRpcChannelBuffer), в котором первый параметр - отправленный из прокси через метод IRpcChannelBuffer.SendReceive.
А в структуре TRPCOLEMESSAGE есть поле iMethod, фамилия говорит сама за себя. Но это чисто маршаллинговый внутренний идентификатор, не имеющий отношения к тем, с которыми мы имеем дело в клиенте и сервере. При самостоятельной реализации вы вольны заполнить структуру как вам заблагорассудится - лишь бы потом смогли это понять в заглушке:)

Теперь по поводу стандартного маршаллера. Не имел случая разобраться досконально, поэтому стопроцентно утверждать не могу. Но есть довольно обоснованное подозрение, что он генерит oicf - строки на основании инфы из TLB, которые и используются при маршаллинге VTbl-вызовов. ИМХО.



Страницы: 1 2 вся ветка

Форум: "Основная";
Текущий архив: 2002.10.03;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.59 MB
Время: 0.009 c
1-7967
Сергиус
2002-09-24 12:26
2002.10.03
поиск в TListView


1-7989
Melnyk
2002-09-20 14:53
2002.10.03
Как изменить имя логического диска


1-7931
Осирис
2002-09-20 16:20
2002.10.03
ExtractAssociatedIcon


3-7807
ОлегТ
2002-09-11 21:42
2002.10.03
как програмно задать псевдоним и путь в BDE


14-8162
Нуфрик
2002-09-09 22:19
2002.10.03
Здрасти, Помогите...=))))





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский