C++/CX (III). Objetos COM

Decíamos ayer y antes de ayer que para crear aplicaciones Metro en Windows 8 podíamos usar, o bien el .NET Framework 4.5 con C# y VB.NET o bien hacerlo con el tradicional C++. Ya conocemos que Microsoft se ha dado cuenta de que la cosa manejada no deja de ser un poco juguete comparada con la nativa, y que hay muchísimas empresas que siguen, no solo con C++, sino incluso con MFC.

Independientemente de eso, lo cierto es que el API para Metro no es más que una variación de Win32, empaquetada y ofrecida mediante objetos COM nativos. La idea es cambiar Win32 y prohibirla para Metro, ofreciendo su equivalente mediante la biblioteca WRL que no es más que un encapsulado exportado mediante COM.

Aquí parece como si Microsoft matara dos pájaros de un tiro, ya que .NET utiliza COM internamente, o lo más parecido y cercano a ello. Por lo tanto, lo exportado mediante la WRL es tomado, por un lado por .NET y por el otro por C++ con su extensión CX.

Tenemos que decir que C++/CX es código nativo. Sin recolector de basura. Sin máquina virtual.  Compilado para un procesador específico. No es más que nuestro código escrito en C++ que utiliza objetos COM envueltos en una serie de clases al estilo de .NET. De hecho, la sintaxis de C++/CX es la misma que la de C++/CLI, por lo que en un principio puede llevar a confusión.

De todos modos, para aquellos que hayan leído el Rationale de Sutter (versión en español de Octavio Hernández y del que suscribe aquí), sabrán que es la única otra sintaxis posible y, o bien escribimos en C++/CLI para el .NET clásico (léase Windows Forms) o bien lo hacemos en C++/CX para Metro. Y si bien podemos hacer ambas cosas para Windows 8, las aplicaciones C++/CLI serán las tradicionales, y las C++/CX las Metro.

Con esto queremos decir que, usando C++/CX, tenemos acceso a toda la parafernalia nativa de C++ como la STL, Boost, C++11… y encima desarrollar para Metro. O dicho de otro modo: cualquier código que tengamos que no haga uso de ventanas ni de Win32 de forma directa no servirá para las aplicaciones Metro.

***

La parte CX se activa con la opción /ZW del compilador, y lo que hace es mapear los objetos COM en clases normales y corrientes. Allí donde antes teníamos que enfangarnos con IInspectable, y usar toda la parafernalia COM, ahora simplemente tenemos que usar una ref class normal y corriente con sus constructores, destructores, propiedades, delegados y métodos que recibirán los tipos de datos más estándar y que devolverán también valores estándar.

O en otras palabras: las clases que nos ofrece el API de Metro (por llamarlo de alguna manera, ya que es el API de WRL) son envoltorios a objetos COM WRL. Y de igual forma que los programadores de Windows han construido la WRL, nosotros también podremos tener objetos del mismo tipo, hechos por nosotros y ofrecidos a terceros. 

Supongamos que tenemos una clase cualquiera con un método llamado Compute que toma dos enteros y devuelve un valor de estado. Para nosotros, que usamos dicha clase, tan sólo tenemos que llamar al método y esperar el resultado.

Pero internamente, lo que está haciendo el sugar syntax de la parte CX del compilador, es algo así:

 

//Parte del “consumidor” de la case
inline int Computer::Compute(int first, int second) {
    int result;
    HRESULT hr = ___impl_Compute(this, first, second, &result);
    if (hr != 0) ___impl_throw_for_hr(hr);
    return result;

 

Y luego, el método __impl_Compute, que está en el lado del componente (si fuera una clase WRL estaría dentro de la WRL), sería algo así:

 

HRESULT __stdcall ___impl_Compute(Computer* cmp, 

  int first, int second, int* result) { 

    try { *result = cmp->Compute(first, second); } 

    catch(Platform::Exception^ e) { return e->HResult; } 

    return S_OK; 

}

 

De este modo vemos cómo podemos acceder a un método sin toda la parafernalia ni las ofuscaciones de COM. Y no queremos entrar en detalles sobre por qué COM es tan así, mayormente porque no tenemos ni repajolera idea.

No obstante, si así lo queremos por detalles de la implementación o simplemente por ser guays, tenemos acceso a la parte interna de WRL de la siguiente manera:

 

//Assuming 32-bit pointers 

Computer^ computer = ref new Computer; 

int* vtable_array = (int*)computer; 

int* icomputer_vtable = (int*)vtable_array[0]; 

int* compute_will_be_fptr = (int*)icomputer_vtable[6]; 

typedef HRESULT (__stdcall *compute_fptr_t)(Computer*, 

  int, int, int*); 

compute_fptr_t compute_fptr = (compute_fptr_t)compute_will_be_fptr; 

//…use compute_fptr freely 🙂

 

No voy a comentar nada de aquí porque simplemente me parece algo demasiado barroco y supongo que sólo será útil si te encuentras con algún bug en la parte CX del compilador… También podemos acceder a la interfaz COM pura a través del método RoActivateInstance, pero lo dejamos para los hardcore.

***

Finalmente hay una opción del compilador no documentada para que éste nos muestre una estructura detallada del componente WinRT que especifiquemos: 

 

/d1reportSingleClassLayout<CLASSNAME>

 

Y de nuevo dejamos la opción para los aventureros, que yo ya me he cansado de escribir por hoy.

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *