Compartir a través de


Registration-Free activación de componentes COM: Un tutorial

 

Steve White
Soporte técnico Premier para desarrolladores, Microsoft UK

Leslie Muller
Global IT Research & Development, Credit Suisse First Boston

Julio de 2005

Resumen: Microsoft Platform SDK realiza un excelente trabajo de documentar los temas de aplicaciones aisladas y ensamblados en paralelo. Sin embargo, no todos equivalen a este tema con el de activación sin registro de componentes COM. COM sin registro es una característica de plataforma de gran interés para las empresas con servidores bloqueados y aplicaciones aisladas en infraestructuras compartidas. En este artículo se explica un ejemplo de trabajo de la activación sin registro de un componente COM nativo por parte de clientes nativos y, a través de la interoperabilidad COM, por parte de un cliente administrado. (18 páginas impresas)

Se aplica a:
   Microsoft Windows Server 2003
   Microsoft Windows XP
   Microsoft .NET Framework versión 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Visual Studio 6.0

Descargue el ejemplo que acompaña a este artículo, MSDNRegFreeCOM.msi.

Contenido

Introducción
terminología com de Registration-Free
Ejecución del ejemplo
Compilar el servidor COM de Visual C++
Compilación del cliente de C++/.NET
activación de Registration-Free
Servidor COM y cliente de Visual Basic 6.0
Apartamentos incompatibles
Uso de la API de contexto de activación
Solución de problemas
Conclusión
Lectura adicional

Introducción

COM sin registro es un mecanismo disponible en Microsoft Windows XP (SP2 para . Componentes basados en NET) y plataformas de Microsoft Windows Server 2003. Como sugiere el nombre, el mecanismo permite la implementación sencilla (por ejemplo, mediante XCOPY) de componentes COM en una máquina sin necesidad de registrarlos.

En las plataformas de destino, una de las fases de inicialización de un proceso y sus módulos dependientes es cargar los archivos de manifiesto de asociados en una estructura de memoria denominada contexto de activación . En ausencia de las entradas del Registro correspondientes, es un contexto de activación que proporciona la información de enlace y activación que necesita el tiempo de ejecución COM. No se requiere ningún código especial en el servidor COM o en el cliente, a menos que decida evitar el uso de archivos mediante la creación de contextos de activación usted mismo mediante la API de contexto de activación de .

En este tutorial, crearé un componente COM nativo sencillo y lo consumiré, tanto registrados como no registrados, desde clientes nativos y administrados. El componente y el cliente nativo se mostrarán tanto en Visual C++ como en Visual Basic 6.0; El cliente administrado se presentará tanto en C# como en Visual Basic .NET. Puede descargar el código fuente y los ejemplos y verlos en acción inmediatamente o puede seguirlo junto con el tutorial y compilarlos paso a paso.

terminología com de Registration-Free

Cualquier persona que esté familiarizado con la tecnología de .NET Framework se acostumbrará al término ensamblado, que denota un conjunto de uno o varios módulos implementados, denominados y con versiones como unidad, con un módulo que contiene un manifiesto que define el conjunto. En COM sin registro, los términos ensamblado y manifiesto se toman prestados para ideas similares en concepto, pero no idénticas a sus homólogos de .NET.

COM sin registro usa ensamblado para significar un conjunto de uno o varios módulos PE (es decir, nativos o administrados) implementados, denominados y versionados como una unidad. COM sin registro usa manifiesto para hacer referencia a archivos de texto con la extensión .manifest que contiene XML, que define la identidad de un ensamblado de (manifiesto de ensamblado), junto con los detalles de enlace y activación de sus clases, o define la identidad de una aplicación (manifiesto de aplicación), junto con una o varias referencias de identidad de ensamblado. Un archivo de manifiesto de ensamblado se denomina para el ensamblado y se denomina un archivo de manifiesto de aplicación para la aplicación.

El término ensamblados en paralelo (SxS) hace referencia a la configuración de diferentes versiones del mismo componente COM, a través de archivos de manifiesto, para que se puedan cargar simultáneamente mediante subprocesos diferentes sin necesidad de registrarse. SxS habilita y es sinónimo de com sin registro.

Ejecución del ejemplo

Después de descargar y extraer el código de ejemplo, encontrará una carpeta denominada \deployed. Aquí está la versión de Visual C++ de la aplicación cliente (client.exe), su manifiesto (client.exe.manifest), la versión de Visual C++ del servidor COM (SideBySide.dll) y su manifiesto (SideBySide.X.manifest). Continúe y ejecute client.exe. El resultado esperado es que client.exe activará una instancia de SideBySideClass (implementada en SideBySide.dll) y mostrará el resultado de llamar a su método Version, que debería ser similar a "1.0.0-CPP".

Compilar el servidor COM de Visual C++

Paso 1

El primer paso es crear un servidor COM. En Visual Studio, cree un nuevo proyecto ATL de Visual C++ y llámelo SideBySide. En el Asistente para proyectos ATL, en la pestaña Configuración de la aplicación, anule la selección de la casilla Con atributos y active la casilla Permitir la combinación de código proxy/código auxiliar.

En el Explorador de soluciones, haga clic con el botón derecho en el nodo del proyecto y elija Agregar | Agregar clase.... Seleccione de objeto simple ATL y elija Abrir. Asigne a la clase un nombre corto de SideBySideClass y haga clic en Finalizar.

En la vista de clases, haga clic con el botón derecho en el nodo ISideBySideClass y elija Agregar | Agregar método.... En el Asistente para agregar métodos, escriba Versión como nombre del método, elija un tipo de parámetro BSTR*, escriba pVer como nombre de parámetro, active la casilla retval y, a continuación, haga clic en Agregar y, a continuación, haga clic en Finalizar.

En la Vista de clases, expanda el nodo CSideBySideClass y haga doble clic en el método version de . Reemplace la línea // TODO por:

*pVer = SysAllocString(L"1.0.0-CPP");

Genere una compilación de versión y copie \release\SideBySide.dll en \deployed.

Compilación del cliente de C++/.NET

El siguiente paso es compilar el cliente. En esta parte del tutorial tiene la opción de compilar un cliente de Visual C++ o .NET para el servidor COM de Visual C++. No es necesario decir que es posible mezclar y buscar coincidencias con clientes y servidores escritos en Visual C++, Visual Basic 6.0 y .NET. Si desea hacerlo, encontrará los ejemplos triviales para modificarlos para que funcionen juntos. Los conjuntos de clientes y servidores se organizan tal como están en este tutorial en el interés de presentar código que funciona as-is.

Paso 2 (opción A: Visual C++)

Cree un nuevo proyecto de consola Win32 de Visual C++ denominado cliente en una carpeta del mismo nivel relativa a la carpeta SideBySide proyecto. En el Asistente para aplicaciones Win32, en la pestaña configuración de la aplicación , active la casilla Agregar compatibilidad con ATL.

Edite stdafx.h y agregue la siguiente línea en la parte superior del archivo, inmediatamente después del #pragma once:

#define _WIN32_DCOM

Además, en stdafx.h agregue la siguiente línea en la parte inferior del archivo:

#import "..\deployed\SideBySide.dll" no_namespace

Reemplace el contenido de client.cpp por este código:

#include "stdafx.h"
#include <iostream>
using namespace std;

void ErrorDescription(HRESULT hr)
{
    TCHAR* szErrMsg;
    if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
      FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, 
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
      (LPTSTR)&szErrMsg, 0, NULL) != 0)
   {
        cout << szErrMsg << endl;
        LocalFree(szErrMsg);
    }
   else
        cout << "Could not find a description for error 0x" 
          << hex << hr << dec << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
   CoInitializeEx(0, COINIT_MULTITHREADED);

   {
      ISideBySideClassPtr ptr;
      HRESULT hr = ptr.CreateInstance(__uuidof(SideBySideClass));
      if (SUCCEEDED(hr))
      {
         cout << ptr->Version() << endl;
      }
      ErrorDescription(hr);

      char c;
      cin >> c;
   }

   CoUninitialize();

   return 0;
}

Genere una compilación de versión y copie \release\client.exe en \deployed.

Paso 2 (opción B: .NET Framework)

En Visual Studio .NET 2003, cree una nueva aplicación de consola .NET de C# o Visual Basic denominada cliente en una carpeta del mismo nivel relativa a la carpeta del proyecto de SideBySide. Agregue una referencia al de biblioteca de tipos SideBySide 1.0 desde la pestaña COM del cuadro de diálogo agregar referencia de .

Pegue el código siguiente dentro del método Main:

Código de C#

   SideBySideLib.ISideBySideClass obj = 
        new SideBySideLib.SideBySideClassClass();
   Console.WriteLine(obj.Version());
   Console.ReadLine();

Código de .NET de Visual Basic

    Dim obj As SideBySideLib.ISideBySideClass = 
      New SideBySideLib.SideBySideClassClass
    Console.WriteLine(obj.Version())
    Console.ReadLine()

Genere una compilación de versión y copie client.exe en \deployed.

Paso 3

En la actualidad, la carpeta \de implementada debe contener, aparte de algunos archivos intermedios, solo client.exe y SideBySide.dll, y este último se registrará mediante su proceso de compilación. Para comprobar que el servidor y el cliente funcionan conjuntamente en estas circunstancias normales, ejecute \deployed\client.exe y anote la salida esperada "1.0.0-CPP".

Paso 4

Este tutorial trata sobre COM sin registro, por lo que ahora es necesario anular el registro del ensamblado SideBySide. En un símbolo del sistema, vaya a la carpeta \deployed y ejecute el comando: regsvr32 /u SideBySide.dll.

Paso 5

Para ver qué efecto ha tenido el paso anterior, ejecute \deployed\client.exe de nuevo y verá el mensaje "Clase no registrada". En esta fase, hemos frustrado que el tiempo de ejecución com encuentre la información que necesita en el registro, pero aún tenemos que hacer que la información esté disponible por medios alternativos. Lo solucionaremos en los pasos siguientes.

activación de Registration-Free

paso 6

En la carpeta \deployed, cree un archivo de manifiesto de aplicación (un archivo de texto) para la aplicación client.exe y llámelo client.exe.manifest. Pegue lo siguiente en el archivo:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Paso 7

En la carpeta \deployed, cree un archivo de manifiesto de ensamblado privado (un archivo de texto) para el componente SideBySide.dll y llámelo SideBySide.X.manifest. Pegue lo siguiente en el archivo:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

He escrito los valores GUID, que serán específicos del proyecto, en forma de marcadores de posición. Los valores respectivos de estos marcadores de posición se pueden encontrar de la siguiente manera, ya sea en el SideBySide archivo SideBySide.idl o abriendo SideBySide.dll en la herramienta OLE/COM ObjectViewer (Oleview.exe).

[
   object,
   uuid([IID_ISideBySideClass]),
   dual,
   nonextensible,
   helpstring("ISideBySideClass Interface"),
   pointer_default(unique)
]
interface ISideBySideClass : IDispatch{
   [id(1), helpstring("method Version")] HRESULT 
        Version([out,retval] BSTR* pVer);
};
[
   uuid([LIBID_SideBySide]),
   version(1.0),
   helpstring("SideBySide 1.0 Type Library")
]
library SideBySideLib
{
   importlib("stdole2.tlb");
   [
      uuid([CLSID_SideBySideClass]),
      helpstring("SideBySideClass Class")
   ]
   coclass SideBySideClass
   {
      [default] interface ISideBySideClass;
   };
};

Paso 8

En este momento, debería analizar el asunto de insertar archivos de manifiesto de ensamblado como recursos de Win32. Cuando los desarrolladores pueden y están dispuestos a recompilar sus componentes COM, se recomienda que un manifiesto de ensamblado (como el creado en el paso anterior) se inserte en el archivo DLL COM como un recurso win32 de tipo RT_MANIFEST (definido en windows.h). En los casos en los que esto no sea posible, tenga cuidado de asignar al ensamblado (y, en consecuencia, el manifiesto del ensamblado) un nombre que difiere del del nombre de archivo del archivo DLL COM. Por lo tanto, en el caso anterior, el archivo DLL COM se denomina SideBySide, pero el ensamblado se llama SideBySide.X. Si está interesado en el motivo de esta restricción, se explica en la sección Solución de problemas. En este tutorial, el manifiesto de ensamblado no está incrustado para reflejar los muchos casos reales en los que hacerlo no será factible.

Paso 9

Para comprobarlo, cortesía de los archivos de manifiesto, el cliente puede activar una vez más la clase SideBySideClass, ejecutar \deployed\client.exe y anotar el resultado esperado "1.0.0-CPP".

Servidor COM y cliente de Visual Basic 6.0

Paso 1

El primer paso es crear un servidor COM. Cree un nuevo proyecto dll de ActiveX de Visual Basic 6.0. En el Explorador de proyectos, seleccione el nodo Project1 y, en la ventana propiedades de , cambie su nombre a SideBySide. En el Explorador de proyectos, seleccione el nodo Class1 y, en la ventana Propiedades, cambie su nombre a SideBySideClass.

Pegue la subrutina siguiente en la ventana Código:

Public Function Version()
Version = "1.0.0-VB6"
End Function

Elegir archivo | Haga SideBySide.dll... y vaya a la carpeta \de implementada y, a continuación, haga clic en Aceptar. Para evitar que se generen nuevos GUID cada vez que realice el archivo DLL, elija Project | Propiedades sideBySide..., haga clic en la pestaña Componente de y, en el grupo Compatibilidad de versiones, seleccione el botón de radio Compatibilidad binaria.

Paso 2

Cree un nuevo proyecto EXE estándar de Visual Basic 6.0. En el Explorador de proyectos, seleccione el nodo Project1 y, en la ventana Propiedades, cambie su nombre a cliente. Elegir archivo | Guarde Project As y guarde el archivo de formulario y el archivo de proyecto en una carpeta del mismo nivel relativa a la carpeta del proyecto SideBySide. Elegir proyecto de | Referencias, active la casilla situada junto a SideBySidey haga clic en Aceptar.

Haga doble clic en el formulario principal en el diseñador de formularios y pegue el código siguiente dentro de Sub Form_Load():

    Dim obj As New SideBySideClass
    MsgBox obj.Version()

Elegir archivo | Haga client.exe... y vaya a la carpeta \deployed y, a continuación, elija Aceptar.

Paso 3

En la actualidad, la carpeta \deployed debe contener, aparte de algunos archivos intermedios, solo client.exe y SideBySide.dll; y este último habrá sido registrado por su proceso de compilación. Para comprobar que el servidor y el cliente funcionan juntos en estas circunstancias normales, ejecute \deployed\client.exe y anote la salida esperada "1.0.0-VB6".

Paso 4

Este tutorial trata sobre COM sin registro, por lo que ahora es necesario anular el registro del ensamblado SideBySide. En un símbolo del sistema, vaya a la carpeta \deployed y ejecute el comando: regsvr32 /u SideBySide.dll.

Paso 5

Para ver qué efecto ha tenido el paso anterior, ejecute \deployed\client.exe de nuevo y verá el mensaje "Error en tiempo de ejecución '429': el componente ActiveX no puede crear el objeto". En esta fase, hemos frustrado que el tiempo de ejecución com encuentre la información que necesita en el registro, pero aún tenemos que hacer que la información esté disponible por medios alternativos. Lo solucionaremos en los pasos siguientes.

Paso 6

En la carpeta \deployed, cree un archivo de manifiesto de aplicación (un archivo de texto) para la aplicación client.exe y llámelo client.exe.manifest. Pegue lo siguiente en el archivo:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity
            type = "win32"
            name = "client"
            version = "1.0.0.0" />
<dependency>
            <dependentAssembly>
                        <assemblyIdentity
                                    type="win32"
                                    name="SideBySide.X"
                                    version="1.0.0.0" />
            </dependentAssembly>
</dependency>
</assembly>

Paso 7

En la carpeta \deployed, cree un archivo de manifiesto de ensamblado privado (un archivo de texto) para el componente SideBySide.dll y llámelo SideBySide.X.manifest. Pegue lo siguiente en el archivo:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">

<assemblyIdentity
   type="win32"
   name="SideBySide.X"
   version="1.0.0.0" />

<file name = "SideBySide.dll">

<comClass
    clsid="{[CLSID_SideBySideClass]}"
    threadingModel = "Apartment" />

<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="_SideBySideClass" 
    iid="{[IID__SideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />

</assembly>

He escrito los valores GUID, que serán específicos del proyecto, en forma de marcadores de posición. Los valores respectivos de estos marcadores de posición se pueden encontrar abriendo SideBySide.dll en la herramienta Ole/COM ObjectViewer (Oleview.exe).

[
  uuid([LIBID_SideBySide]),
  version(1.0),
  custom(50867B00-BB69-11D0-A8FF-00A0C9110059, 8169)

]
library SideBySide
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-
C000-000000000046}
    importlib("stdole2.tlb");

    // Forward declare all types defined in this typelib
    interface _SideBySideClass;

    [
      odl,
      uuid([IID__SideBySideClass]),
      version(1.0),
      hidden,
      dual,
      nonextensible,
      oleautomation
    ]
    interface _SideBySideClass : IDispatch {
        [id(0x60030000)]
        HRESULT Version([out, retval] VARIANT* );
    };

    [
      uuid([CLSID_SideBySideClass]),
      version(1.0)
    ]
    coclass SideBySideClass {
        [default] interface _SideBySideClass;
    };
};

Paso 8

Para comprobar que, cortesía de los archivos de manifiesto, el cliente puede activar la clase SideBySideClass , ejecutar \deployed\client.exe y anotar el resultado esperado "1.0.0-VB6".

Apartamentos incompatibles

Todos los servidores COM de este tutorial se compilan para ejecutarse en un Single-Threaded Apartment (es decir, son STA o Apartment-threaded, componentes). Todos los clientes son STA, excepto para el cliente de C++, que es MTA (su subproceso se ejecuta en un apartamento multiproceso). Por lo tanto, hay un caso en el que el cliente y el servidor residen en apartamentos incompatibles y, en este caso, las referencias entre apartamentos de las llamadas COM deben realizarse entre un proxy y un código auxiliar. Este es el caso en el que se usa la siguiente sección del archivo de manifiesto de ensamblado:

...
<typelib tlbid="{[LIBID_SideBySide]}"
       version="1.0" helpdir=""/>

</file>

<comInterfaceExternalProxyStub 
    name="ISideBySideClass" 
    iid="{[IID_ISideBySideClass]}"
    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
    baseInterface="{00000000-0000-0000-C000-000000000046}"
    tlbid = "{[LIBID_SideBySide]}" />
...

Estos elementos proporcionan información que de lo contrario estaría presente en el Registro. El elemento comInterfaceExternalProxyStub proporciona suficiente información para serialización de la biblioteca de tipos y es adecuado para las interfaces COM que derivan de IDispatch (que incluye todas las interfaces de Automation). En estos casos, ole32.dll proporciona el código auxiliar de proxy externo usado (es decir, externos a los archivos del ensamblado). Si los componentes COM implementan solo distribución o interfaces de duales, este es el elemento que debe usar.

Las interfaces personalizadas son un poco más raras y para estas tienes que hacer un poco más de trabajo. Considere una interfaz llamada ISxSCustom que deriva directamente de IUnknown y no es compatible con la automatización. Para SideBySideClass implementar ISxSCustom y para que los clientes llamen a sus métodos en un escenario sin registro, necesito agregar un elemento comInterfaceProxyStub a mi manifiesto de ensamblado. Como sugiere el nombre del elemento, esta vez el código auxiliar de proxy no se externo: se proporciona mediante SideBySide.dll (si he activado la casilla Permitir combinación de código proxy/código auxiliar en el Asistente para proyectos ATL) o en SideBySidePS.dll. Si mi código auxiliar proxy se combina, el nuevo elemento es un elemento secundario del archivo existente elemento:

<file name = "SideBySide.dll">
   ...
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

De lo contrario, necesito agregar un elemento de archivo declarando el archivo dll proxy-stub:

<file name = "SideBySide.dll"> ... </file>
<file name = "SideBySidePS.dll">
<comInterfaceProxyStub
name="ISxSCustom" 
iid="{[IID_ISxSCustom]}" />
</file>

Si no hubiera sido necesario para ilustrar los elementos descritos anteriormente, habría creado el servidor COM de Visual C++ para que se ambos subprocesos para que sus coclases se hubieran activado en el MTA del cliente. En caso de que el cliente y el servidor existan en el mismo apartamento, se omiten los elementos descritos. Sin embargo, le ruego que no dependa de esta circunstancia porque puede haber situaciones fuera de su control donde se activa un componente en un apartamento diferente al de su cliente, incluso si sus modelos de subproceso son coherentes. Teniendo en cuenta el esfuerzo relativamente pequeño necesario, creo que es aconsejable siempre incluir la configuración de código auxiliar de proxy en los manifiestos.

Uso de la API de contexto de activación

En la sección Introducción mencioné que una de las fases de inicialización del proceso de una aplicación, en las plataformas a las que se aplica este artículo, es buscar un archivo de manifiesto de aplicación. El archivo de manifiesto de aplicación contiene referencias a los ensamblados en los que la aplicación tiene dependencias. En el caso de la activación sin registro de componentes COM nativos, lo que significa un ensamblado es un conjunto de uno o varios archivos DLL COM recopilados en una identidad y versión comunes.

Si la aplicación sabe a priori el posible conjunto de coclases que se van a activar directa o indirectamente durante su vigencia, los manifiestos de aplicación son cómodos. Pero hay una clase de aplicación relativamente rara (por ejemplo, servidores de cuadrícula) que por diseño no, antes del tiempo de ejecución, conocen los módulos que cargarán. En este caso, se llama a una forma de hacer referencia a los archivos de manifiesto del ensamblado después de llamar a la inicialización del proceso. Esto se satisface mediante la API de contexto de activación de , el uso del cual obvia un archivo de manifiesto de aplicación. En su forma más sencilla, la técnica consiste en inicializar una estructura ACTCTX con la ubicación del archivo de manifiesto de ensamblado y, a continuación, crear y activar un contexto de activación de desde él. En las instrucciones siguientes se supone que ya ha compilado la aplicación de cliente de Visual C++ que se describe en el paso 2 (opción A).

Edite stdafx.h y agregue la siguiente línea inmediatamente después de la definición de _WIN32_DCOM:

#define _WIN32_FUSION 0x0100 // this causes activation context 
structs and APIs to be included.

Si observa la función _tmain en client.cpp, entre las llamadas COM inicialize y uninitialize, verá una instrucción compuesta que activa y llama a la SideBySideClass. Es necesario mover esta instrucción compuesta (todo entre las llaves) dentro de una nueva sección de código que inicializa el contexto de activación de la siguiente manera:

   ACTCTX actCtx;
   memset((void*)&actCtx, 0, sizeof(ACTCTX));
   actCtx.cbSize = sizeof(ACTCTX);
   actCtx.lpSource = "SideBySide.X.manifest";

   HANDLE hCtx = ::CreateActCtx(&actCtx);
   if (hCtx == INVALID_HANDLE_VALUE)
      cout << "CreateActCtx returned: INVALID_HANDLE_VALUE" 
             << endl;
   else
   {
      ULONG_PTR cookie;
      if (::ActivateActCtx(hCtx, &cookie))
      {
         // previous compound statement goes here...
         ::DeactivateActCtx(0, cookie);
      }
   }

El código anterior se ejecuta antes de que se activen las coclases. El código simplemente lee el archivo de manifiesto del ensamblado (cuyo nombre está codificado de forma rígida en este ejemplo, pero debe corresponder al ensamblado que quiera cargar dinámicamente) en un contexto de activación que después activa (es decir, hace actual). A partir de este punto, la activación de las coclases se produce como antes. Ahora puede descartar client.exe.manifest.

Una alternativa a la API de contexto de activación directa, pero solo disponible en Windows Server 2003, es el objeto Microsoft.Windows.ActCtx.

No es necesario decir que hay mucho más en la API de contexto de activación que he mostrado aquí y puede encontrar un vínculo a la documentación completa de la API en la sección Lectura adicional.

Solución de problemas

Como hemos visto, la activación sin registro de componentes COM no requiere ningún código especial en el servidor o en el cliente. Todo lo necesario es un par coincidente de archivos de manifiesto.

Sugiero que se acerque a su propio desarrollo libre de registro como lo hace este tutorial. En concreto: primero llegue a un estado conocido viendo que el cliente trabaja con un servidor registrado; a continuación, anule el registro del servidor y compruebe que el mensaje de error es lo que esperaba; y, por último, solucione la situación mediante la creación e implementación de archivos de manifiesto. De este modo, los esfuerzos de solución de problemas relacionados con la activación sin registro se limitarán a la estructura de los archivos de manifiesto (y la inserción correcta del manifiesto del ensamblado si decide hacerlo).

Al solucionar problemas de COM sin registro, el Visor de eventos en Windows Server 2003 es su amigo. Cuando Windows XP o Windows Server 2003 detectan un error de configuración, normalmente mostrará un cuadro de mensaje de error titulado para la aplicación que ha iniciado y que contiene el mensaje "Esta aplicación no se ha podido iniciar porque la configuración de la aplicación es incorrecta. La reinstalación de la aplicación puede solucionar este problema". Le recomiendo que, cada vez que vea este mensaje, reproduzca el problema en Windows Server 2003, consulte el registro de eventos del sistema y busque eventos del origen de SideBySide. La razón por la que no sugiero que examines el registro de eventos de Windows XP en estos casos es que invariablemente contendrá un mensaje como "Error de contexto de activación para [ruta]\[nombre de archivo de la aplicación]. Manifiesto. Mensaje de error de referencia: la operación se completó correctamente", lo que no ayuda a identificar el problema.

Los esquemas de los distintos archivos de manifiesto se documentan en el SDK de plataforma bajo el encabezado Referencia de archivos de manifiestoy la herramienta de validación de esquemas Manifestchk.vbs está disponible, por lo que aquí solo llamaré algunos puntos relevantes para el tutorial. En primer lugar, vamos a examinar el archivo de manifiesto del ensamblado. Por ejemplo, vuelva al paso 7.

Recordará que, en el sentido COM sin registro , un de ensamblado es una idea abstracta con la que asocia uno o varios archivos físicos mediante el contenido del manifiesto de ensamblado archivo. El nombre del ensamblado aparece en tres lugares y debe ser idéntico en cada uno: en el nombre de atributo del elemento assembly manifest file assemblyIdentity; en el atributo nombre de del elemento dependentAssemblyassemblyIdentity del archivo de manifiesto de aplicación; y el nombre del propio archivo de manifiesto de ensamblado, excluyendo la extensión .manifest .manifest. Si el nombre del archivo de manifiesto no coincide con el nombre de en el manifiesto de aplicación, verá el siguiente mensaje en el registro de eventos del sistema de Windows Server 2003: "Ensamblado dependiente [valor de nombre atributo en el manifiesto de aplicación] no se encontró y El último error fue El ensamblado al que se hace referencia no está instalado en el sistema". Si el nombre elemento del ensamblado manifiesto es incorrecto, verá el siguiente mensaje en el registro de eventos del sistema de Windows Server 2003: "La identidad del componente encontrada en el manifiesto no coincide con la identidad del componente solicitado".

Si SideBySide.dll hubiera sido un componente basado en .NET Framework, habríamos estado obligados a insertar el archivo de manifiesto del ensamblado en el ensamblado SideBySide como un recurso Win32 (y habríamos llamado el archivo de manifiesto después del ensamblado .NET, es decir, SideBySide.manifest). Sin embargo, debido a la secuencia de búsqueda del cargador de ensamblados, para los componentes COM nativos, es opcional para insertar el manifiesto en el módulo. Antes de que el cargador de ensamblados busque [AssemblyName].manifest, busca [AssemblyName].dll y lo busca en un recurso Win32 de tipo RT_MANIFEST. La configuración dentro del recurso debe tener un elemento AssemblyIdentity que coincida con [AssemblyName], así como los demás atributos de la referencia AssemblyIdent ity del manifiesto de aplicación.

Sin embargo, si se encuentra .dll [AssemblyName], pero no contiene un manifiesto coincidente, el mecanismo de carga de ensamblados se detiene y no sigue buscando [AssemblyName].manifest. En el momento de escribirlo, esto es cierto en el cargador de ensamblados en Windows XP (que en esta circunstancia mostrará su mensaje habitual de que "la configuración de la aplicación es incorrecta"), pero no en Windows Server 2003. En Windows Server 2003, la búsqueda continúa y buscará el archivo de manifiesto aunque coincida con el nombre de un módulo. Sin embargo, le ruego que no dependa de este comportamiento. En su lugar, para asegurarse de que admite de forma coherente ambas plataformas, se recomienda insertar el manifiesto de ensamblado en el ensamblado como un recurso de RT_MANIFEST siempre que sea factible. Cuando no sea factible, debe asignar al archivo de manifiesto de ensamblado un nombre diferente del de cualquier módulo de la misma carpeta. Como alternativa, coloque el componente COM en una carpeta secundaria de la carpeta del manifiesto de ensamblado y haga referencia a esta ruta de acceso secundaria en el archivo del manifiesto de ensamblado elemento.

El elemento assemblyIdentity define la identidad del ensamblado . En el caso de los componentes COM nativos, ni su nombre de ni su versión atributo necesitan coincidir con el de cualquier archivo físico, aunque es una buena idea aplicar algún tipo de coherencia.

El elemento del archivo es el elemento primario de la comClass, typelib, y comInterfaceProxyStub elementos. Su propósito es localizar los archivos físicos que componen el ensamblado de . Si el archivo nombre del elemento atributo no hace referencia correctamente a los archivos del sistema de archivos, CoCreateInstance devolverá un HRESULT correspondiente a "Class not registered" o "No se encontró el módulo especificado". Para poder instalar diferentes versiones del componente COM en carpetas diferentes, el nombre de atributo puede incluir una ruta de acceso. En Windows XP SP2, la ruta de acceso puede ser relativa o absoluta y puede hacer referencia a una carpeta en cualquier parte del sistema de archivos. Windows Server 2003 requiere la ruta de acceso para hacer referencia a la raíz de la aplicación o a una carpeta secundaria, y si intenta infringir esta regla, verá el siguiente mensaje en el registro de eventos del sistema de Windows Server 2003: "Error de sintaxis en el manifiesto o archivo de directiva [nombre de manifiesto del manifiesto del ensamblado] [...] El valor [...] no es válido".

El elemento comClass solo tiene un atributo obligatorio: clsid. Si la aplicación intenta activar una coclase no registrada cuyo CLSID no aparece en un elemento comClass en el manifiesto de ensamblado, coCreateInstance devolverá un HRESULT con el valor REGDB_E_CLASSNOTREG (0x80040154), el texto del mensaje para el que es "Clase no registrada".

Como he dicho, los elementos typelib y comInterface[External]ProxyStub elementos son necesarios en caso de que el cliente y el servidor existan en diferentes apartamentos, por lo que los siguientes errores solo se pueden ver cuando se procesan estos elementos. Los errores de configuración de estos elementos provocan que CoCreateInstance devolver un HRESULT que corresponda a los mensajes "Biblioteca no registrada", "Error al cargar la biblioteca de tipos/DLL" o "no se admite dicha interfaz". Si ve alguno de estos mensajes, compruebe los GUID y asegúrese de que todos los atributos obligatorios están presentes. Encontrará el esquema de archivo de manifiesto en el SDK de plataforma.

Ahora vamos a poner nuestra atención en el archivo de manifiesto de aplicación. Por ejemplo, vuelva al paso 6. El manifiesto de aplicación debe tener el nombre [nombredeArchivo de aplicación].manifest. Por lo tanto, en el tutorial se le llamó client.exe.manifest para que quede claro que debe leerse cada vez que client.exe se carga en un proceso. Si esto no se realiza correctamente, CoCreateInstance devolverá un HRESULT con el valor REGDB_E_CLASSNOTREG (0x80040154), el texto del mensaje para el que es "Clase no registrada".

El elemento más importante del manifiesto de aplicación es el elemento dependentAssembly/assemblyIdentity. Este elemento es una referencia al equivalente del manifiesto del ensamblado y los dos deben coincidir exactamente. Una buena manera de asegurarse de que lo hacen es copiar el elemento del manifiesto del ensamblado y pegarlo aquí. Si hay alguna diferencia, verá el siguiente mensaje en el registro de eventos del sistema de Windows Server 2003: "La identidad del componente encontrada en el manifiesto no coincide con la identidad del componente solicitado".

Conclusión

COM sin registro es una tecnología que libera los componentes COM de una dependencia en el Registro de Windows y, por tanto, libera las aplicaciones que las usan de requerir servidores dedicados. Permite que las aplicaciones con dependencias en distintas versiones del mismo componente COM compartan una infraestructura y carguen esas distintas versiones de componentes COM en paralelo en un eco del mecanismo de implementación y control de versiones de .NET Framework.

Este artículo le guía a través de una demostración de la activación sin registro de componentes COM nativos por aplicaciones cliente nativas escritas tanto en Visual C++ como en Visual Basic 6.0 y en un cliente administrado. Explica cómo funciona el mecanismo y subraya algunos posibles errores de configuración y cómo solucionarlos.

Lectura adicional

 

Acerca del autor

Steve White es un consultor de desarrollo de aplicaciones que trabaja en el equipo premier de soporte técnico para desarrolladores en Microsoft UK. Admite el desarrollo de clientes con Visual C#, Windows Forms y ASP.NET. Su blog tiene más información sobre sus intereses en la música, las visualizaciones y la programación.

Leslie Muller es un tecnólogo con el equipo de Investigación & Desarrollo en Credit Suisse First Boston. Leslie tiene 12 años de experiencia como desarrolladora y arquitecto técnico, trabajando en entornos como servicios financieros, startups tecnológicas, automatización industrial y defensa. Cuando no está programando o investigando disfruta de esquí, hockey sobre hielo y cuando sea posible haciendo cosas ligeramente locas con vehículos motorizados en entornos extremos como Islandia o las Rocas.