что такое сом объект

Модель COM

Модель COM — это независимая от платформы, распределенная объектно-ориентированная система для создания двоичных компонентов программного обеспечения, которые могут взаимодействовать. COM — это базовая технология для Microsoft OLE (составных документов), ActiveX (компоненты с поддержкой интернета), а также другие.

Чтобы понять модель COM (и, следовательно, все технологии на основе COM), важно понимать, что это не объектно-ориентированный язык, а стандартный. И COM не указывает, как должно быть структурировано приложение. сведения о языке, структуре и реализации оставлены разработчику приложения. Вместо этого COM указывает объектную модель и требования к программированию, позволяющие COM-объектам (также называемым COM-компонентами или просто объектами) взаимодействовать с другими объектами. Эти объекты могут находиться в одном процессе, в других процессах и даже быть на удаленных компьютерах. Они могут быть написаны на разных языках, и они могут быть структурно непохожими, поэтому COM называется двоичным стандартом. стандарт, который применяется после преобразования программы в двоичный код машинного кода.

Единственное требование к языку для COM заключается в том, что код создается на языке, который может создавать структуры указателей, явно или неявно, вызывайте функции с помощью указателей. Объектно-ориентированные языки, такие как C++ и Smalltalk, предоставляют механизмы программирования, упрощающие реализацию COM-объектов, но такие языки, как C, Java и VBScript, можно использовать для создания и использования COM-объектов.

COM определяет важный характер COM-объекта. Как правило, программный объект состоит из набора данных и функций, управляющих данными. COM-объект — это тот, в котором доступ к данным объекта осуществляется исключительно через один или несколько наборов связанных функций. Эти наборы функций называются интерфейсами, и функции интерфейса называются методами. Кроме того, модель COM требует, чтобы единственный способ получить доступ к методам интерфейса — через указатель на интерфейс.

Помимо указания базового двоичного объекта, COM определяет некоторые основные интерфейсы, которые предоставляют функции, общие для всех технологий на основе COM, и предоставляет небольшое количество функций, требуемых для всех компонентов. COM также определяет, как объекты работают вместе в распределенной среде и имеют новые функции безопасности, помогающие обеспечить целостность системы и компонентов.

В следующих подразделах этого раздела описываются основные проблемы COM, связанные с проектированием COM-объектов.

Источник

Основы технологии COM

Одним из главных достоинств Delphi является поддержка технологий СОМ и ActiveX. В этой главе мы рассмотрим, что представляет собой технология СОМ, в чем различие между технологиями COM, ActiveX и OLE.

Технология СОМ применяется при описании API и двоичного стандарта для связи объектов различных языков и сред программирования. СОМ предоставляет модель взаимодействия между компонентами и приложениями.

Технология СОМ работает с так называемыми СОМ-объектами. СОМ-объекты похожи на обычные объекты визуальной библиотеки компонентов Delphi. В отличие от объектов VCL Delphi, СОМ-объекты содержат свойства, методы и интерфейсы.

Обычный СОМ-объект включает в себя один или несколько интерфейсов. Каждый из этих интерфейсов имеет собственный указатель.

Технология СОМ имеет два явных плюса:

— создание СОМ-объектов не зависит от языка программирования. Таким образом, СОМ-объекты могут быть написаны на различных языках;

— СОМ-объекты могут быть использованы в любой среде программирования под Windows. В число этих сред входят Delphi, Visual C++, C++Builder, Visual Basic, и многие другие.

Хотя технология СОМ обладает явными плюсами, она имеет также и минусы, среди которых зависимость от платформы. То есть, данная технология применима только в операционной системе Windows и на платформе Intel.

Все СОМ-объекты обычно содержатся в файлах с расширением DLL или OCX. Один такой файл может содержать как одиночный СОМ-объект, так и несколько СОМ-объектов.

Ключевым аспектом технологии СОМ является возможность предоставления связи и взаимодействия между компонентами и приложениями, а также реализация клиент-серверных взаимодействий при помощи интерфейсов.

Технология СОМ реализуется с помощью СОМ-библиотек (в число которых входят такие файлы операционной системы, как OLE32.DLL и OLE-Aut32.DLL). СОМ-библиотеки содержат набор стандартных интерфейсов, которые обеспечивают функциональность СОМ-объекта, а также небольшой набор функций API, отвечающих за создание и управление СОМ-объектов.

В Delphi реализация и поддержка технологии СОМ называется каркасом Delphi ActiveX (Delphi ActiveX framework, DAX). Реализация DAX описана в модуле Axctris.

Одной из важнейших задач, которые ставила перед собой фирма Microsoft, когда продвигала операционную систему Windows, была задача по обеспечению эффективного взаимодействия между различными программами, работающими в Windows.

Самыми первыми попытками решить эту непростую задачу были буфер обмена, разделяемые файлы и технология динамического обмена данными (Dynamic Data Exchange, DDE).

После чего была разработана технология связывания и внедрения объектов (Object Linking and Embedding, OLE). Первоначальная версия OLE 1 предназначалась для создания составных документов. Эта версия была признана несовершенной и на смену ей пришла версия OLE 2. Новая версия позволяла решить вопросы предоставления друг другу различными программами собственных функций. Данная технология активно внедрялась до 1996 года, после чего ей на смену пришла технология ActiveX, которая включает в себя автоматизацию (OLE-автоматизацию), контейнеры, управляющие элементы, Web-технологию и т. д.

Всякая новая технология приносит с собой новые термины для ее описания.

СОМ-объект представляет собой двоичный код, который выполняет какую-либо функцию и имеет один или более интерфейс.

СОМ-объект содержит методы, которые позволяют приложению пользоваться СОМ-объектом. Эти методы доступны благодаря СОМ-интерфейсам. Клиенту достаточно знать несколько базовых интерфейсов СОМ-объекта, чтобы получить полную информацию о составе свойств и методов объекта. СОМ-объект может содержать один или несколько интерфейсов. Для программиста СОМ-объект работает так же, как и класс в Object Pascal.

Пользователем СОМ-объекта называется приложение или часть приложения, которое использует СОМ-объект и его интерфейсы в своих собственных целях. Как правило, СОМ-объект находится в другом приложении.

Файлы библиотеки типов имеют расширение TLB.

Операционные системы Windows NT 4 и Windows 98 имеют встроенную поддержку DCOM.

Каждый СОМ-объект имеет счетчик ссылок. Данный счетчик содержит число процессов, которые в текущий момент времени используют СОМ-объект. Под процессом здесь подразумевается любое приложение или DLL, которые используют СОМ-объект, т. е. пользователи СОМ-объекта. Счетчик ссылок на СОМ-объект нужен для того, чтобы высвобождать процессорное время и оперативную память, занимаемую СОМ-объектом, в том случае, когда он не используется.

Часть данных, использующаяся совместно несколькими приложениями, называется OLE-объектом. Те приложения, которые могут содержать в себе OLE-объекты, называются OLE-контейнерами (OLE container). Приложения, имеющие возможность содержать свои данные в OLE-контейнерах, называются OLE-серверами (OLE server).

Документ, включающий в себя один или несколько OLE-объектов, называется составным документом. Приложение, которое может содержаться внутри документа, называется ActiveX-документом (ActiveX document).

Остальные термины, присущие технологии СОМ, мы рассмотрим в следующих разделах данной книги.

При создании СОМ-приложения необходимо обеспечить следующее:

Рассмотрим эти три составляющие СОМ-приложения более подробно.

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.1. СОМ-интерфейс

По правилам обозначения СОМ-объектов, интерфейсы СОМ-объекта обозначаются кружками справа или слева от СОМ-объекта. Базовый интерфейс lUnknown рисуется кружком сверху от СОМ-объекта.

Для примера, каждый СОМ-объект всегда поддерживает основной СОМ-интерфейс lUnknown, который применяется для передачи клиенту сведений о поддерживаемых интерфейсах.

Как уже говорилось выше, СОМ-объект может иметь несколько интерфейсов, каждый из которых обеспечивает какую-либо свою функцию.

Ключевыми аспектами СОМ-интерфейсов являются следующие.

— Однажды определенные, интерфейсы не могут быть изменены. Таким образом, вы можете возложить на один интерфейс определенный набор функций. Дополнительную функциональность можно реализовать с помощью дополнительных интерфейсов.

— По взаимному соглашению, все имена интерфейсов начинаются с буквы I, например IPersist, IMalloc.

— Каждый интерфейс гарантированно имеет свой уникальный идентификатор, который называется глобальный уникальный идентификатор (Globally Unique Identifier, GUID). Уникальные идентификаторы интерфейсов называют идентификаторами интерфейсов (Interface Identifiers, IIDs). Данные идентификаторы обеспечивают устранение конфликтов имен различных версий приложения или разных приложений.

— Интерфейсы не зависят от языка программирования. Вы можете воспользоваться любым языком программирования для реализации СОМ-интерфейса. Язык программирования должен поддерживать структуру указателей, а также иметь возможность вызова функции при помощи указателя явно или неявно.

— Интерфейсы не являются самостоятельными объектами, они лишь обеспечивают доступ к объектам. Таким образом, клиенты не могут напрямую обращаться к данным, доступ осуществляется при помощи указателей интерфейсов.

— Все интерфейсы всегда являются потомками базового интерфейса Iunknown.

Основной СОМ-интерфейс IUnknown

Базовый интерфейс lunknown достаточно подробно был рассмотрен во второй главе книги. В дополнение ко всему вышесказанному, добавим, что интерфейс lunknown обеспечивает механизм учета ссылок (счетчик ссылок на СОМ-объект). При передаче указателя на интерфейс выполняется метод интерфейса lunknown AddRef. По завершении работы с интерфейсом приложение-клиент вызывает метод Release, который уменьшает счетчик ссылок.

При вызове метода Querylnterface интерфейса Iunknown в метод передается параметр IID, имеющий тип TGUID, т. е. идентификатор интерфейса. Параметр метода out возвращает либо ссылку на запрашиваемый интерфейс, либо значение NH. Результатом вызова метода может быть одно из значений, перечисленных в табл. 3.1.

Таблица 3.1. Значения, возвращаемые методом Queryinterface

Интерфейс не поддерживается

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.2. Схема работы указателя СОМ-интерфейса

Наглядное представление работы указателей СОМ-интерфейса представлено на рис. 3.2.

СОМ-сервер представляет собой приложение или библиотеку, которая предоставляет услуги приложению-клиенту или библиотеке. СОМ-сервер содержит один или более СОМ-объектов, где СОМ-объекты выступают в качестве наборов свойств, методов и интерфейсов.

Клиенты не знают как СОМ-объект выполняет свои действия. СОМ-объект предоставляет свои услуги при помощи интерфейсов., В дополнение, приложению-клиенту не нужно знать, где находится СОМ-объект. Технология СОМ обеспечивает прозрачный доступ независимо от местонахождения СОМ-объекта.

В общих чертах, СОМ-сервер должен выполнять следующее:

— регистрировать данные в системном реестре Windows для связывания модуля сервера с идентификатором класса (CLSID);

— предоставлять фабрику СОМ-класса, создающую экземпляры СОМ-объектов;

— обеспечивать механизм, который выгружает из памяти серверы СОМ, которые в текущий момент времени не предоставляют услуг клиентам.

Для создания СОМ-объектов и СОМ-серверов Delphi предоставляет мастера, который значительно упрощает процедуру. Об этом читайте следующую главу.

Интерфейс IclassFactory определен в модуле Delphi ActiveX так:

type
IClassFactory = interface (IUnknown)
[‘<00000001-0000-0000-COOO-000000000046>‘]
function Createlnstance (const unkOuter: lUnknown; const iid: TIID out obj): HResult; stdcall;
function LockServer (fLock: BOOL): HResult; stdcall;
end;

Как видно из вышеприведенного куска кода, интерфейс имеет два метода:

Createlnstance и LockServer.

Метод Createlnstance создает экземпляр СОМ-объекта ассоциированной фабрики класса.

Всякий раз, когда услуги СОМ-объекта запрашиваются клиентом, фабрика класса создает и регистрирует экземпляр объекта для конкретного пользователя. Если услуга того же СОМ-объекта запрашивает другой клиент, фабрика класса создает второй экземпляр объекта для обслуживания второго клиента. coclass должен иметь фабрику класса и идентификатор класса CLSID. Использование CLSID для cociass подразумевает, что они могут быть откорректированы всякий раз, когда в класс вводятся новые интерфейсы. Таким образом, в отличие от DLL, новые интерфейсы могут изменять или добавлять методы, не влияя на старые версии.

Мастер создания СОМ-объектов Delphi самостоятельно заботится о создании фабрики класса.

Локальные и удаленные серверы

С использованием СОМ клиент не должен беспокоиться о том, где располагается объект, он просто делает вызов интерфейса данного объекта. Технология СОМ обеспечивает все необходимые шаги для того, чтобы сделать этот вызов. Шаги могут отличаться, в зависимости от местонахождения объекта. Объект может находиться в том же процессе, где и клиент, в другом процессе на том же компьютере, где расположен клиент, или на другом компьютере в сети. В зависимости от этого применяются разные типы серверов:

— внутренний сервер (In-process server);

— локальный сервер или сервер вне процесса (Local server, Out-of-process server);

— удаленный сервер (Remote server).

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.3. Схема взаимодействия клиента с внутренним сервером

Внутренний СОМ-сервер должен экспортировать четыре функции:

function DllRegisterServer: HResult; stdcall;
function DllUnregisterServer: HResult; stdcall;
function DllGetClassObject (const CLSID, IID: TGUID; var Obj): HResult;
stdcall;
function DllCanUnloadNow: HResult; stdcall;

Все вышеперечисленные функции уже реализованы в модуле comserv, их нужно только добавить в описания exports вашего проекта.

Рассмотрим данные функции более подробно:

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.4. Путь к локальному СОМ-серверу в окне редактора системного реестра

Когда клиент и сервер находятся в различных приложениях, а также, когда они находятся на разных компьютерах в сети, СОМ использует внутренний (внутрипроцессный) прокси (In-process proxy) для реализации процедуры удаленного вызова. Прокси располагается в одном процессе вместе с клиентом, поэтому, с точки зрения клиента, вызов интерфейсов осуществляется так же, как и в случае, когда клиент и сер’вер находятся внутри одного процесса. Задача прокси заключается в том, чтобы перехватывать вызовы клиента и перенаправлять их туда, где запущен сервер. Механизм, который позволяет клиенту получать доступ к объектам, расположенным в другом адресном пространстве или на другом компьютере, называется маршалинг (marshaling).

— принимать указатель интерфейса из процесса сервера и делать указатель прокси в процессе клиента доступным;

— передавать аргументы вызовов интерфейса таким образом, как будто они произошли от клиента и размещать аргументы в процесс удаленного объекта.

Для любого вызова интерфейса клиент помещает аргументы в стек, вызывает необходимую функцию СОМ-объекта через указатель интерфейса. Если вызов объекта произошел не внутри процесса, вызов проходит через прокси. Прокси упаковывает аргументы в пакет маршалинга и передает получившуюся структуру удаленному объекту. Заглушка (stub) объекта распаковывает пакет маршалинга, выбирает аргументы из стека и вызывает необходимую функцию СОМ-объекта.

Тип маршалинга зависит от объектной принадлежности СОМ. Объекты могут использовать стандартный механизм маршалинга, предоставляемый интерфейсом IDispatch. Стандартный маршалинг позволяет устанавливать связь при помощи стандартного системного удаленного вызова процедуры (Remote Procedure Call, RFC).

На рис. 3.5 изображена схема, показывающая методику взаимодействия клиента и сервера в случае, когда приложения работают на одном компьютере, но в разных приложениях.

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.5. Схема взаимодействия клиента с сервером в разных процессах на одном компьютере

Локальный СОМ-сервер регистрируется в системном реестре Windows так же, как и внутренний СОМ-сервер.

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.6. Схема взаимодействия клиента с сервером на разных компьютерах

Очень важным при разработке СОМ-приложений является создание приложений, называемых СОМ-клиентами, которые могут запрашивать интерфейсы объектов, чтобы определить те услуги, которые может предоставить СОМ-объект.

Технология СОМ изначально разрабатывалась как ядро для осуществления межпрограммного взаимодействия. Уже на этапе разработки предполагалось расширять возможности технологии при помощи так называемых расширений СОМ. СОМ расширяет собственную функциональность, благодаря созданию специализированных наборов интерфейсов для решения конкретных задач.

Таблица 3.2. Список расширений СОМ

Серверы автоматизации (Automation servers)

Серверы автоматизации- это объекты, которыми можно управлять из других приложений во время работы приложения. Таким образом, автоматизация- это способность приложения программно контролировать объекты других приложений

Диспетчеры автоматизации или СОМ-клиенты (Automation Controllers, COM Clients)

Диспетчеры автоматизации- это клиенты серверов автоматизации. Они позволяют разработчику или пользователю писать сценарии для управления серверами автоматизации

Элементы управления ActiveX (ActiveX Controls)

Элементы управления ActiveX предназначены для серверов внутри процесса (in-process COM servers). Элементы ActiveX обычно используются путем встраивания в приложение-клиент

Библиотеки типов (Type Libraries)

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

Страницы активного сервера (Active Server Pages)

Активные серверные страницы- это компоненты ActiveX, которые позволяют вам создавать динамически изменяющиеся Web-страницы

Активные документы (Active Documents)

Визуальные межпроцессные объекты (Visual Cross-process Objects)

Визуальные межпроцессные объекты- это визуальные объекты, которыми можно управлять из других процессов

На рис. 3.7 представлена диаграмма, которая показывает связь некоторых расширений СОМ и их связь с технологией СОМ.

Приведенная ниже табл. 3.3 кратко описывает особенности объектов каждого из вышеприведенных расширений СОМ.

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект
Рис. 3.7. Технологии, основанные на СОМ
Таблица 3.3. Особенности объектов СОМ

Источник

OLE, COM, COM+

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

Обратная разработка программного обеспечения — процедура получения информации об алгоритме. При этом получение этих данных напрямую зависит от того, насколько много есть информации о приложении в документации, и от того, какой использовался способ для создания файла. Всё еще больше усложняется, если алгоритм заимствует фрагменты из других приложений или операционной системы. Эта статья расскажет о механизмах, которые заложены в ОС Windows, благодаря которым процесс обратной разработки может стать весьма сложным процессом.

С чего всё началось

Появление парадигмы объектно-ориентированного программирования подарило программистам очень мощные инструменты для обработки информации. Начали появляться новые языки, которые использовались для разных спектров задач, программное обеспечение становилось модульным. Написание новой программы с функционалом, который использовал стандартные механизмы ввода/вывода стало тривиальной задачей. Нужно было только подключить нужную библиотеку, которая уже содержала все необходимые функции.

Результатом использования парадигмы объектно-ориентированного подхода стали методы логического разбиения приложений на отдельные фрагменты, при этом можно было создавать уже скомпилированные части кода, которые собирались в новые приложения. Модульность позволила задуматься о механизмах, которые могли бы позволить объединять код, фрагменты которого были бы написаны на разных языках программирования, в одну систему, которая решала бы отдельно взятую проблему или целый класс проблем.

В операционной системе Windows подход к созданию отдельных компонентов был реализован в предоставлении унифицированных интерфейсов, которыми приложения пользуются и по сей день. Эти интерфейсы называются WinAPI. Их исследование достаточно тривиально, большая часть интерфейсов задокументирована и поэтому их обратная разработка заключается в том, чтобы найти в документации название и прочитать данные о параметрах и возвращаемом значении.

Каждый WinAPI интерфейс позволяет сделать минимальное действие, которое может произвести ОС, то есть если программист решит написать приложение, то для его реализации придётся задействовать несколько сотен, а то и тысяч интерфейсов. Отдельно стоит упомянуть, что это далеко не единственный способ, который доступен в ОС для реализации алгоритмов. ОС Windows также предлагает компонентный подход для построения приложений. Это означает, что программист может объединять целые программы вместе, чтобы реализовать выполнение алгоритма. Возможно это за счет использования механизма Component Object Module.

Появление COM не случайно, реализация этого механизма — логичный этап развития. На схеме ниже можно увидеть ретроспективу создания механизмов в ОС Windows:

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

Картинка наглядно показывает, как связано появление того или иного механизма. Реализация каждого нового механизма это решение проблем, которые возникли при реализации предыдущего механизма. Картинка включает в себя такие механизмы как OLE, COM+, DCOM, которые тоже, надо сказать очень сложные с точки зрения реализации и изучения.

Некоторые полезные определения

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

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

Примеры и практика

Давайте попробуем посмотреть, как обозначенные выше механизмы выглядят в ПО при обратной разработке. Начнем с OLE. Как было сказано выше, этот механизм проще всего обнаружить в офисных документах. Попробуем найти такой документ.

Для исследования был выбран вот этот документ. Он представляет собой docx файл, по сути это архив, который содержит некоторое количество файлов с инструкциями, как его рендерить. Заглянем внутрь: в этом формате все данные, которые могут быть добавлены через OLE это файлы, которые расположены в директории «word/embeddings». Заголовок содержимого объекта можно видеть ниже:

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

Ничего особенно примечательного, такие объекты можно анализировать с использованием набор инструментов oletools.

OLE объект представляет собой файловую систему, в которую можно положить информацию необходимую для встраивания данных. Если воспользоваться инструментом oleobj, то можно увидеть, что внутри объекта находится txt файл. Кстати, это можно увидеть и из шестнадцатеричного редактора:

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

Почти все элементы пользовательского интерфейса, которыми мы пользуемся каждый день, это COM объекты.

Как найти объекты COM+? Если в COMView вы обнаружили объект, который имеет интерфейс IUnknown, перед вами COM+ объект. Например:

что такое сом объект. Смотреть фото что такое сом объект. Смотреть картинку что такое сом объект. Картинка про что такое сом объект. Фото что такое сом объект

Таким образом можно установить, за какой функционал отвечает тот или иной объект, который используется программным обеспечением. При этом не нужно вникать в имплементацию и можно сразу разобраться в алгоритме приложения, прочитав описание объекта в интерфейсе COMView.

Статья подготовлена Александром Колесниковым в рамках курса «Reverse-Engineering. Professional». Если интересно узнать больше о программе и формате обучения на этом курсе, приходите на день открытых дверей онлайн, на котором вы также сможете познакомиться с преподавателем.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *