Está en la página 1de 24

I NSTITUTO TECNOLÓGICO SUPERIOR

DE PEROTE

INFORMÁTICA

PROGRAMACIÓN EN AMBIENTE C/S

SÉPTIMO SEMESTRE

ACTIVIDAD:
INVESTIGACIÓN COM/DCOM

DOCENTE:
CRISTIAN IGNACIO GARCÍA
BARRALES

ALUMNO:
EDGARDO RODRÍGUEZ HIPÓLITO

M = 15050054.

1
ÍNDICE:

PORTADA--------------------------------------------------- 1
ÍNDICE------------------------------------------------------- 2
INTRODUCCIÓN------------------------------------------ 3
COM/DCOM------------------------------------------------ 4
CREACIÓN DE SERVIDORES COM---------------- 7
CREACIÓN DE UN CLIENTE COM------------------ 12
AUTOMATIZACIÓN--------------------------------------- 20
ATL------------------------------------------------------------- 21
DCOM--------------------------------------------------------- 23
CONCLUSIÓN----------------------------------------------- 25
REFERENCIAS--------------------------------------------- 25

INTRODUCCIÓN.

2
Microsoft Distributed COM (DCOM) extiende COM (Component Object Model) para
soportar comunicación entre objetos en ordenadores distintos, en una LAN, WAN, o
incluso en Internet. Con DCOM una aplicación puede ser distribuida en lugares que
dan más sentido al cliente y a la aplicación.
Como DCOM es una evolución lógica de COM, se pueden utilizar los componentes
creados en aplicaciones basadas en COM, y trasladarlas a entornos distribuidos.
DCOM maneja detales muy bajos de protocolos de red, por lo que uno se puede
centrar en la realidad de los negocios: proporcionar soluciones a clientes.

COM/DCOM (Component Object Model/ Distributed COM)

El Component Object Model es una arquitectura de componentes de software que permite que las
aplicaciones y sistemas se construyan a partir de componentes producidos por distintos proveedores
de software.

Servidores COM
Los objetos “servidores” son aquellas instancias de las clases que contienen los
métodos que resuelven el problema del que se ocupa el sistema.

Cliente COM
Los objetos “clientes” son aquellas instancias de las clases que contengan la
interfaz del sistema con el usuario, que implementan los textos de ayuda del

3
sistema, los cuadros de dialogo para introducir información al sistema o bien para
mostrar resultados.

COM está diseñado para permitir que los clientes se comuniquen con otros objetos
en forma transparente independientemente del lugar donde se están ejecutando, ya
sea en el mismo proceso, la misma computadora o una computadora diferente.
COM provee acceso transparente a los servidores locales y remotos a través de
objetos proxy y stub.

¿Qué es un componente COM?

} Es un contenedor binario
} Contiene el código de una o más clases de objetos
} Cada clase puede tener una o más interfaces
} COM expone o pública estas interfaces para que puedan ser usadas por otras
aplicaciones.
} Una aplicación puede usar componentes COM. independientemente del
lenguaje en que fueron escritos.

DCOM

DCOM es la extensión del Component Object Model a los ambientes distribuidos,


que define los mecanismos de conexión y el protocolo de red necesario para hacer
llamadas a procedimientos remotos orientadas a objetos, a nivel de aplicación, que
lo vuelven útil para sistemas distribuidos de todo tipo basados en componentes.

Los servidores COM/DCOM se crean como ATL Object, que producen archivos.DLL
o .EXE, según el tipo de servidor que se requiera, mientras que los clientes se crean
como proyectos normales de aplicaciones Windows, y que hacen referencia a las
clases contenidas en la parte servidor mediante los punteros de interfaz a objetos
COM/DCOM.

4
Características principales:

Los Componentes y su reutilización


Cualquier componente que sea desarrollado como una parte de una aplicación
distribuida es un candidato para ser reutilizado. Organizando los procesos de
desarrollo alrededor del paradigma de los componentes permite continuar
aumentando el nivel de funcionalidad en las nuevas aplicaciones y reducir el tiempo
de desarrollo.

Independencia de la localización
DCOM olvida completamente la localización de los componentes, ya esté en el
mismo proceso que el cliente o en una máquina en cualquier lugar del mundo. En
cualquier caso, la forma en la que el cliente se conecta a un componente y llama a
los métodos de éste es identica. No es solo que DCOM no necesite cambios en el
código fuente, sino que además no necesita que el programa sea recompilado. Una
simple reconfiguración cambia la forma en la que los componentes se conectan
entre sí.

Independencia del lenguaje de programación


Con la independencia de lenguaje de DCOM, los desarrolladores de aplicaciones
pueden elegir las herramientas y lenguajes con los que estén más familiarizados.

Independencia del protocolo


Muchas aplicaciones distribuidas tienen que ser integradas en la infraestructura de
una red existente. Necesitar un protocolo específico de red, obligará a mejorar todos
los cliente, lo que es inaceptable en muchas situaciones. Los desarrolladores de
aplicaciones tienen que tener cuidado de mantener la aplicación lo más
independiente posible de la infraestructura de la red.

5
DCOM proporciona esta transparencia: DCOM puede utilizar cualquier protocolo de
transporte, como TCP/IP, UDP, IPX/SPX y NetBIOS. DCOM proporciona un marco
de seguridad a todos estos protocolos.

Ejemplo de COM en embarcadero.

4.1. Creación de Servidores COM.

Microsoft Distributed COM (Component Object Model) sirve para soportar

comunicación entre objetos en ordenadores distintos, en una LAN, WAN, o incluso

en Internet.

DCOM es una extensión de COM, y éste define como los componentes y sus

clientes interactúan entre sí. Esta interacción es definida de tal manera que el cliente

y el componente pueden conectar sin la necesidad de un sistema intermedio. El

cliente llama a los métodos del componente sin tener que preocuparse de niveles

más complejos.

6
DCOM es una evolución lógica de COM, se pueden utilizar los componentes

creados en aplicaciones basadas en COM, y trasladarlas a entornos distribuidos.

DCOM maneja detales muy bajos de protocolos de red, por lo que uno se puede

centrar en la realidad de los negocios: proporcionar soluciones a clientes.

SERVIDOR

Es una aplicación que ofrece un servicio a usuarios de Internet, el servidor es un

programa que recibe una solicitud, realiza el servicio requerido y devuelve los

resultados en forma de una respuesta. Generalmente un servidor puede tratar

múltiples peticiones (múltiples clientes) al mismo tiempo.

1.1 Los clientes mandan peticiones a los servidores solicitando una respuesta , el
servidor escucha la petición y envía una respuesta a la petición del cliente

7
Las funciones que lleva a cabo el proceso servidor se resumen en los siguientes

Puntos

• Aceptar los requerimientos de bases de datos que hacen los clientes.

• Procesar requerimientos de bases de datos.

• Formatear datos para trasmitirlos a los clientes.

• Procesar la lógica de la aplicación y realizar validaciones a nivel de bases de

datos.

El uso de los componentes COM es un claro ejemplo de la arquitectura

Cliente/Servidor. El objeto COM en sí es el servidor, y es usado por un programa

que hace de cliente. Existen varias formas de realizar la comunicación. Podemos

encontrar los componentes como partes de un ejecutable (como el caso de los

productos de Microsoft Office, o si implementamos nuestro propio componente y lo

incluimos con un programa que los use), dentro de una librería de enlace dinámico

(DLL) o incluso en otra máquina (DCOM).

8
Tipos de Servidores COM

Anteriormente hemos visto que existen varios tipos distintos de servidores COM.

Entre ellos están los objetos COM que se “alojan” en DLLs, los que se alojan en un

ejecutable y los que se alejan en otra máquina de una red. Vamos a ver el problema

de la comunicación entre cliente y servidor (para el paso de parámetros).

SERVIDORES COM EN DLL

Esta es la forma más sencilla de funcionamiento del modelo COM. Cliente y

Servidor comparten un espacio de direcciones y un mapa de memoria. La carga del

objeto COM desde el DLL se hace de forma transparente al usuario. La

comunicación se puede realizar con el paso de parámetros normal fijado para una

DLL (incluyendo eso sí, como parámetros también, un puntero a la instancia del

objeto).

Es el programa cliente el que realiza la creación de memoria para comenzar la

creación del objeto COM. Al entrar en ejecución el constructor del objeto, éste

podría realizar peticiones de memoria dinámica.

SERVIDORES COM EN EXE

Son llamados servidores locales. Se ejecutan en el mismo ordenador que el cliente,

pero en procesos distintos. La comunicación la gestiona el sistema operativo. No

hace falta que esté el ejecutable del servidor funcionando, pues el sistema es capaz

de ejecutarlo. El “exe” contiene una parte que se encarga de la gestión de memoria

inicial que requieren los objetos COM.

SERVIDORES COM REMOTOS O DCOM

9
Cliente y servidor se encuentran en ordenadores distintos. La comunicación la

gestiona el sistema operativo. Internamente, Microsoft usa el protocolo RPC para

realizar la comunicación. Sin embargo, el programa servidor debe de estar

ejecutándose.

A la hora de la práctica, hay poca diferencia en la programación. Las funciones

miembros se llaman de la misma forma, los objetos se crean prácticamente igual (en

servidores remotos se usa la función CoCreateInstanceEx, la cual tiene un

parámetro que identifica al ordenador servidor). Pero, si los objetos se usan igual,

¿cómo se puede conseguir entonces el efecto de llamadas a función? Es decir,

¿cómo podría usarse?

Objeto1->Funcion1 (parámetros);

El cliente no tiene constancia entonces de que tiene establecer una conexión por

red. Es el sistema operativo el que tiene que poner un proxy que “simule” que las

funciones están accesibles. Y la llamada a la Funcion1 realmente estará llamando a

una función local gestionada por el sistema operativo que realice la comunicación

con el otro extremo.

10
4.2. Creación de un cliente COM.

El cliente es una aplicación informática o un ordenador que consume un servicio


remoto en otro ordenador conocido como servidor, normalmente a través de una red
de telecomunicaciones. También se puede definir un cliente es cualquier cosa (que
no sea un servidor) que se conecta a un servidor.

11
En los componentes COM no existe el concepto de herencia, para la reutilización se
utiliza:
COMPOSICIÓN o AGREGACIÓN.

COMPOSICIÓN
•En la composición el objeto COM simplemente actúa a su vez como cliente del
objeto COM que contiene.
AGREGACIÓN
•Se expone directamente la interfaz del objeto COM agregado, de modo que el
cliente puede acceder de forma transparente a las interfaces de los dos objetos
COM.

COM+ es una ampliación al modelo de componentes COM para la construcción de


aplicaciones empresariales, se encarga de proporcionar una serie de
servicios:
Seguridad de grano fino, controla el acceso a cada método.
Escalabilidad, mediante balanceo de carga y “object pooling”
Manejo de transacciones, a través de MTS(Microsoft Transactional server).

Implementación
Se va a construir un objeto COM capaz de almacenar la siguiente información sobre
un usuario:
Age -> short
Name -> LPSTR
Sex -> unsigned char

El objetivo es poner de manifiesto cuales son las tareas básicas de las que es
responsable todo objeto COM.
EJ: Tareas del COM
1. Crear los GUID’s necesarios para el objeto y sus interfaces
2. Definir las interfaces del objeto
3. Implementar las funciones de la interfaz definida
4. Registrar el servidor COM

12
EJ: Crear los GUID’s
•El API COM dispone del método CoCreateGuid que se encarga de esta tarea.
•Como parte de la distribución de VS se puede encontrar el programa
UUIUDGEN.exe para realizar esta tarea, aunque internamente sencillamente llama
al método del API anterior. Generamos los 3 GUID’s que vamos a necesitar:
C:>uuidgen -n3
acceeb00-86c7-11d0-94ab-0080c74c7e95 acceeb01-86c7-11d0-94ab-
0080c74c7e95 acceeb02-86c7-11d0-94ab-0080c74c7e95

EJ: Definir las Interfaces


•Definición de la interfaz IUserInfo import "unknwn.idl" ;
[ object, uuid(acceeb02-86c7-11d0-94ab-0080c74c7e95)
] interface IUserInfo : IUnknown
{
[propget] HRESULT Age([out, retval] short *nRetAge);
[propput]HRESULT Age([in] short nAge);
[propget]HRESULT Name([out, retval] LPSTR *lpszRetName);
[propput]HRESULT Name([in] LPSTR lpszName);
[propget]HRESULT Sex([out, retval] unsigned char *byRetSex);
[propput]HRESULT Sex([in] unsigned char bySex);
}

EJ: Definir las Interfaces


•Definición de la type library y del objeto UserInfo
[uuid(acceeb00-86c7-11d0-94ab-0080c74c7e95),version(1.0)]
library UserInfo
{
[uuid(acceeb01-86c7-11d0-94ab-0080c74c7e95),]
coclass UserInfo
{
[default] interface IUserInfo;
}

13
}

•Las interfaces en IDL creadas las compilamos a través de MIDL (incluido en VS) y
se obtienen los siguientes ficheros:

•Contenido de UserInfo_i.c
const IID IID_IUserInfo = {0xacceeb02,0x86c7,0x11d0,
{0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};
const CLSID CLSID_UserInfo = {0xacceeb01,0x86c7,0x11d0,
{0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};
const IID LIBID_UserInfo =
{0xacceeb00,0x86c7,0x11d0,
{0x94,0xab,0x00,0x80,0xc7,0x4c,0x7e,0x95}};

• Contenido de UserInfo_i.h
Este fichero contiene las cabeceras del interfaz tanto en C como en C++
La interfaz en C++ se declara como una clase abstracta con todos sus métodos
virtuales puros, de esta manera se fuerza a quien implemente la interfaz a
implementar todos sus métodos.

class IUserInfo {
public: virtual QueryInterface(REFIID iid, LPVOID *ppv) = 0; .............}
class CUserInfo : IUserInfo { private: ULONG m_cRef; private: short m_nAge;
private: LPSTR m_lpszName; private: BYTE m_bySex;
public: STDMETHODIMP QueryInterface(REFIID iid, LPVOID *ppv); public:
STDMETHODIMP_(ULONG)AddRef(void); public:
STDMETHODIMP_(ULONG)Release(void); public: STDMETHODIMP get_Age(short
*nRetAge); public: STDMETHODIMP put_Age(short nAge); public:
STDMETHODIMP get_Name(LPSTR *lpszRetName); public: STDMETHODIMP
put_Name(LPSTR lpszname); public: STDMETHODIMP get_Sex(BYTE *byRetSex);
public: STDMETHODIMP put_Sex(BYTE bySex);
CUserInfo();
~CUserInfo(); };

14
•Implementación del método QueryInterface
STDMETHODIMP CUserInfo::QueryInterface(REFIID iid, LPVOID *ppv)
{
*ppv = NULL; if (IID_IUnknown == iid) *ppv = (LPVOID)(IUnknown *)this; else if
(IID_IUserInfo == iid) *ppv = (LPVOID)(IUserInfo *)this; else
return E_NOINTERFACE; //Interface not supported
((IUnknown *)*ppv)->AddRef(); return NOERROR;
}
•Implementación del método AddRef
STDMETHODIMP_(ULONG) CUserInfo::AddRef(void)
{ return ++m_cRef;
}
•Implementación del método Release
STDMETHODIMP_(ULONG)CUserInfo::Release(void)
{ m_cRef-; if (0 == m_cRef)
{ delete this; g_cObjects-; if (::ServerCanUnloadNow())
::UnloadServer(); return 0;
} return m_cRef;
}

Una vez implementados los métodos de la interfaz Iunknow simplemente restaría


implementar el resto de métodos de la clase CUserInfo, los que corresponden a la
interfaz IUserInfo.
La implementación de estos métodos es trivial, consiste únicamente en establecer y
devolver los valores de las propiedades definidas para contener el sexo, nombre y
edad del usuario.

EJ: Registrar el servidor COM


•La información sobre el objeto COM debe estar en el registro de windows para que
los clientes puedan localizarlo, la especificación COM define que las dll’s que
contengan objetos COM deben implementar los siguientes métodos para esta tarea:

15
DllRegisterServer – En esta función se escribirá la información necesaria en el
registro para que luego se pueda localizar el servidor COM.

DllUnregisterServer – Esta función es la encargada de borrar la información sobre el


COM escrita en el registro.

EJ: Registrar el servidor COM


•La información que se debe almacenar en el registro sobre el servidor COM es la
siguiente:

HKEY_CLASSES_ROOT CLSID
{acceeb01-86c7-11d0-94ab0080c74c7e95} = Description
InprocServer32
C:\UserInfo\UserInfo.dll

•Los programas como Regsrv32.exe que se utilizan para registrar objetos COM
simplemente cargan la dll y
invocan el método DllRegisterServer

EJ: Creación de un cliente


•El cliente debe realizar las siguientes tareas:
Iniciar la librería COM
Obtener la interfaz
Manipular el objeto a través de su interfaz
Liberar las interfaces
Finalizar la librería COM

EJ: Iniciar la librería COM


•Para iniciar la librería COM hay que llamar al método del API COM CoInitialize:
hr = CoInitialize(NULL); if ( SUCCEEDED(hr) )
{ ...}

16
•El método CoInitialize inicializa la librería en el thread de ejecución desde el que se
invoque. Es necesario llamar a CoInitialize desde cada thread de la aplicación que
quiera acceder a objetos COM.

EJ: Obtener la interfaz


•Para obtener la interfaz inicial llamamos al método CoCreateInstance, este creará
una nueva instancia de un objeto COM y nos devolverá un puntero a su interfaz.
IUnknown *pIUnknown = NULL; hr = CoCreateInstance(CLSID_UserInfo, NULL,
CLSCTX_INPROC_SERVER, IID_IUnknown,
(LPVOID *)&pIUnknown);
if (SUCCEEDED(hr))
{....}

EJ: Obtener la interfaz


•A través del puntero a IUnknow obtener el puntero a la interfaz IUserInfo
hr = pIUnknown->QueryInterface (IID-IUserInfo,
(LPVOID *)&pIUserInfo); if (SUCCEEDED(hr))
{\\manipulación del objeto}

EJ: liberar las interfaces


•Para liberar las interfaces hay que llamar al método Release, si el objeto COM no
tiene más interfaces referenciadas se borrara automaticamente:
pIUserInfo->Release(); pIUnknown->Release();

EJ: Finalizar la librería COM


•La librería COM se finaliza a través del método CoUninitialize, una vez llamado a

Este método no se podrá seguir llamando a funciones de la librería COM ni


manipulando objetos COM.

17
4.3. Automatización:
Es un mecanismo formal de comunicación entre procesos basado en COM.

18
Facilita:
Una infraestructura que permite que aplicaciones llamadas automation controllers
para que puedan acceder, manipular y compartir automation objects (por ejemplo,
propiedades o métodos de otras aplicaciones).
El controlador es el "cliente" y, la aplicación que exporta los objetos de
automatización, el servidor.
Los componentes COM se pueden agrupar básicamente en tres categorías.

$ In-Process
$ Locales
$ Remotos

Los componentes In-Process se cargan en el mismo espacio de procesos que la


aplicación cliente. Estos componentes se implementan como librerías dinámicas
(DLL) por lo cual se minimiza el tiempo requerido para la invocación de los métodos.
Por otra parte, como el componente comparte el espacio de direcciones de la
aplicación, una falla en el componente puede causar daños en la aplicación. Los
componentes In-Process no son programas ejecutables, por lo tanto solo pueden
usarse en el contexto de un programa que los invoca. Los controles ActiveX son
componentes In-Process.

Los componentes locales se ejecutan en un proceso separado en el mismo


computador mientras que los componentes remotos se ejecutan en otro
computador. La aplicación cliente no necesita saber dónde reside el componente.
Cuando el componente es remoto, DCOM crea un proxy que intercepta las
referencias a la interface del objeto y luego usa RPC para ejecutar la instancia “real”
del objeto

4.4. ATL (Active Template Library)

19
Active (ATL) Template Library es un conjunto de clases de C++ basadas
en plantillas que permiten crear objetos pequeños, rápidos (COM) del
modelo de objetos componentes. Tiene compatibilidad especial para
características COM clave, incluidas las implementaciones comunes,
interfaces duales, interfaces COM estándar de enumeradores, puntos
de conexión, rasga interfaces, y controles ActiveX.

ATL incluye un asistente de objeto que establece la estructura primaria de los


objetos muy rápidamente con un mínimo de codificación manual.
En el lado del cliente COM ATL proporciona punteros inteligentes que tienen que ver
con el recuento de referencias COM.

Historia
ATL versión 7 introdujo atributos en C ++ en un intento de ofrecer algo similar a los
atributos de la CLI, no han tenido mucho éxito, y se han restado importancia en la

20
versión de ATL 8 (Visual Studio 2005). La versión7 introduce nuevas clases de
conversión de cadenas.
El 28 de julio de 2009, Microsoft lanzó un parche para ATL para corregir un error
que podría permitir ActiveX controles creados con ATL a ser vulnerable a una falla
de seguridad de ejecución remota de código.
Desde Visual Studio 2013, código de ATL en Visual C ++ 2013 es estática, lo que
elimina la DLL.

Clases de apoyo
ATL incluye muchas RAII clases para simplificar la gestión de tipos COM.
Las clases más comúnmente utilizados son:
•CComPtr <T> de propósito general Smart-puntero,
•CComBSTR envoltorio BSTR,
•CComVariant envoltorio VARIANTE, y
•CComSafeArray <T> envoltorio SAFEARRAY.

Apoyo Compiler COM


Aunque no es formalmente parte de ATL, Microsoft Visual C ++ también incluye
adicional de C ++ clases RAII para simplificar la gestión de tipos COM.
Estos apoyo compilador COM clases pueden ser utilizados como reemplazo para o
en combinación con ATL, e incluye:
_com_ptr_t smart-puntero que decora el nombre de la interfaz COM con un sufijo
"PTR",
_bstr_t envoltorio BSTR,
_variant_t envoltorio VARIANTE, y
_com_error envoltorio HRESULT.

Tenga en cuenta que a partir de Visual Studio 2012, las clases de apoyo compilador
COM no incluye una envoltura SAFEARRAY.
4.5. DCOM (Distributed COM).

Distributed Component Object Model (DCOM), en español Modelo de


Objetos de Componentes Distribuidos, es una tecnología propietaria de
Microsoft para desarrollar componentes software distribuidos sobre

21
varios ordenadores y que se comunican entre sí. Extiende el modelo
COM de Microsoft y proporciona el sustrato de comunicación entre la
infraestructura del servidor de aplicaciones COM+ de Microsoft. Ha sido
abandonada en favor del framework .NET.1 2
La adición de la "D" a COM fue debido al uso extensivo de DCE/RPC, o
más específicamente la versión mejorada de Microsoft, conocida como
MSRPC.
En términos de las extensiones que añade a COM, DCOM tenía que
resolver los problemas de
Aplanamiento - Serializar y deserializar los argumentos y valores
de retorno de las llamadas a los métodos "sobre el cable".
Recolección de basura distribuida, asegurándose que las
referencias mantenidas por clientes de las interfaces sean liberadas
cuando, por ejemplo, el proceso cliente ha caído o la conexión de red se
pierde.
Uno de los factores clave para resolver estos problemas es el uso de
DCE/RPC como el mecanismo RPC subyacente bajo DCOM. DCE/RPC
define reglas estrictas en cuanto al aplanamiento y a quién es
responsable de liberar la memoria.
DCOM fue uno de los mayores competidores de CORBA. Los
defensores de ambas tecnologías sostenían que algún día serían el
modelo de código y servicios sobre Internet. Sin embargo, las
dificultades que suponía conseguir que estas tecnologías funcionasen a
través de cortafuegos y sobre máquinas inseguras o desconocidas,
significó que las peticiones HTTP normales, combinadas con los
navegadores web les ganasen la partida. Microsoft, en su momento
intentó y fracasó anticiparse a esto añadiendo un transporte extra HTTP
a DCE/RPC denominado "ncacn_http" (Connection-based, over HTTP).

22
CONCLUSIÓN.

23
Com/dcom en el lenguaje c++ se utilizan para automatizar los
procedimientos, estos al automatizar los servidores nos permitieron
acceder a los objetos y la funcionalidad con la que está creado, los
servidores que están disponibles, estos objetos permiten a los
proveedores mejora las aplicaciones para poder compartirse entre sí.
El modelo com fue una herramienta creada mediante los servidores del
modelo c++ y solucionar problemas al conectarse a una red y tener
viabilidad para reestructurar por algunos fallos.

REFERENCIAS:
http://programacionambienteclienteservidor.blogspot.com/2014/11/comdcom-
component-object-model.html
Wikipedia. (12 de Marzo de 2016). wikipedia. Recuperado el 15 de Julio de 2017, de
wikipedia: https://en.wikipedia.org/wiki/Active_Template_Library

24

También podría gustarte