Compartilhar via


Registration-Free ativação de componentes COM: um passo a passo

 

Steve White
Suporte premier para desenvolvedores, Microsoft UK

Leslie Muller
Pesquisa Global de TI & Desenvolvimento, Credit Suisse First Boston

Julho de 2005

Resumo : o SDK da Plataforma Microsoft faz um excelente trabalho documentando os tópicos de aplicativos isolados e assemblies lado a lado. No entanto, nem todos igualam este tópico ao da ativação sem registro de componentes COM. O COM sem registro é um recurso de plataforma de grande interesse para empresas com servidores e aplicativos bloqueados isolados em infraestruturas compartilhadas. Este artigo apresenta um exemplo de trabalho da ativação sem registro de um componente COM nativo por clientes nativos e, por meio da interoperabilidade COM, por um cliente gerenciado. (18 páginas impressas)

Aplica-se a:
   Microsoft Windows Server 2003
   Microsoft Windows XP
   Microsoft .NET Framework versão 1.1
   Microsoft Visual Studio .NET 2003
   Microsoft Visual Studio 6.0

Baixe o exemplo que acompanha este artigo, MSDNRegFreeCOM.msi.

Conteúdo

Introdução
Terminologia com Registration-Free COM
Executando o exemplo
Compilando o servidor COM do Visual C++
Compilando o cliente C++/.NET
Ativação do Registration-Free
O cliente e o servidor COM do Visual Basic 6.0
Apartamentos incompatíveis
Usando a API de Contexto de Ativação
Solucionando problemas
Conclusão
Leitura adicional

Introdução

O COM sem registro é um mecanismo disponível no Microsoft Windows XP (SP2 para . Componentes baseados em NET) e plataformas do Microsoft Windows Server 2003. Como o nome sugere, o mecanismo permite a implantação fácil (por exemplo, usando XCOPY) de componentes COM em um computador sem a necessidade de registrá-los.

Nas plataformas de destino, um dos estágios de inicialização de um processo e seus módulos dependentes é carregar todos os arquivos de manifesto associados em uma estrutura de memória chamada contexto de ativação. Na ausência de entradas correspondentes do Registro, é um contexto de ativação que fornece as informações de associação e ativação necessárias para o tempo de execução COM. Nenhum código especial é necessário no servidor COM ou no cliente, a menos que você opte por evitar o uso de arquivos criando contextos de ativação por conta própria usando a API de contexto de ativação .

Neste passo a passo, criarei um componente COM nativo simples e o consumirei, registrado e não registrado, de clientes nativos e gerenciados. O componente e o cliente nativo serão apresentados no Visual C++ e no Visual Basic 6.0; o cliente gerenciado será apresentado no C# e no .NET do Visual Basic. Você pode baixar o código-fonte e os exemplos e vê-los em ação imediatamente ou pode acompanhar o passo a passo e compilá-los passo a passo.

Terminologia com Registration-Free COM

Qualquer pessoa familiarizada com a tecnologia do .NET Framework estará acostumada com o termo de assembly, que indica um conjunto de um ou mais módulos implantados, nomeados e com versão como uma unidade, com um módulo contendo um manifesto que define o conjunto. No COM sem registro, os termos de assembly e manifesto são emprestados para ideias semelhantes no conceito, mas não idênticas aos seus equivalentes do .NET.

O COM sem registro usa assembly para significar um conjunto de um ou mais módulos PE (ou seja, nativos ou gerenciados) implantados, nomeados e com versão como uma unidade. O COM sem registro usa manifesto para se referir a arquivos de texto com a extensão .manifest contendo XML, que define a identidade de um assembly (manifesto do assembly), juntamente com os detalhes de associação e ativação de suas classes, ou define a identidade de um aplicativo (manifesto do aplicativo), juntamente com uma ou mais referências de identidade de assembly. Um arquivo de manifesto do assembly é nomeado para o assembly e um arquivo de manifesto do aplicativo é nomeado para o aplicativo.

O termo assemblies SxS (lado a lado) refere-se à configuração de diferentes versões do mesmo componente COM, por meio de arquivos de manifesto, para que possam ser carregados simultaneamente por threads diferentes sem precisar ser registrados. SxS habilita e é vagamente sinônimo de COM sem registro.

Executando o exemplo

Depois de baixar e extrair o código de exemplo, você encontrará uma pasta chamada \deployed. Aqui está a versão do Visual C++ do aplicativo cliente (client.exe), seu manifesto (client.exe.manifest), a versão do Visual C++ do servidor COM (SideBySide.dll) e seu manifesto (sideBySide.X.manifest). Vá em frente e execute client.exe. O resultado esperado é que client.exe ativará uma instância de SideBySideClass (implementada em SideBySide.dll) e exibirá o resultado da chamada de seu método de versão , que deve se parecer com "1.0.0-CPP".

Compilando o servidor COM do Visual C++

Etapa 1

A primeira etapa é criar um servidor COM. No Visual Studio, crie um novo Projeto atl do Visual C++ e chame-o SideBySide. No Assistente de Projeto da ATL, na guia configurações do aplicativo , desmarque a caixa de seleção Atributo e marque a caixa de seleção Permitir mesclagem de código proxy/stub caixa de seleção.

No Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e escolha Adicionar | Adicionar classe.... Selecione de Objeto Simples da ATL e escolha Abrir. Dê à classe um nome curto de SideBySideClass e clique em Concluir.

No Modo de Exibição de Classe, clique com o botão direito do mouse no nó ISideBySideClass e escolha Adicionar | Adicionar Método.... No Assistente para Adicionar Método, insira Versão como o nome do método, escolha um tipo de parâmetro bstr*, insira pVer como o nome do parâmetro, selecione a caixa de seleção revalitar e clique em Adicionar e clique em Concluir.

No Modo de Exibição de Classe, expanda o nó CSideBySideClass e clique duas vezes no método versão . Substitua a linha // TODO por:

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

Produzir uma compilação de versão e copiar \release\SideBySide.dll em \deployed.

Compilando o cliente C++/.NET

A próxima etapa é criar o cliente. Nesta parte do passo a passo, você tem a opção de criar um Visual C++ ou um cliente .NET para o servidor COM do Visual C++. Desnecessário dizer que é possível misturar e corresponder clientes e servidores escritos no Visual C++, Visual Basic 6.0 e .NET. Se você quiser fazer isso, encontrará os exemplos triviais para alterar para que eles trabalhem juntos. Os conjuntos de clientes e servidores são organizados como estão neste passo a passo no interesse de apresentar o código que funciona as-is.

Etapa 2 (opção A: Visual C++)

Crie um novo Projeto de Console do Visual C++ Win32 chamado cliente em uma pasta irmão em relação à pasta do projeto SideBySide. No Assistente de Aplicativo Win32, na guia configurações do aplicativo , marque a caixa de seleção Adicionar suporte para ATL.

Edite stdafx.h e adicione a seguinte linha na parte superior do arquivo, imediatamente após o #pragma once:

#define _WIN32_DCOM

Também no stdafx.h adicione a seguinte linha na parte inferior do arquivo:

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

Substitua o conteúdo 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;
}

Produzir uma compilação de versão e copiar \release\client.exe em \deployed.

Etapa 2 (Opção B: .NET Framework)

No Visual Studio .NET 2003, crie um novo aplicativo de console .NET C# ou Visual Basic chamado cliente em uma pasta irmão em relação à pasta SideBySide do projeto. Adicione uma referência ao da Biblioteca de Tipos SideBySide 1.0 da guia COM da caixa de diálogo Adicionar Referência de .

Cole o seguinte código dentro do método Main:

Código C#

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

Código .NET do Visual Basic

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

Produzir uma compilação de versão e copiar client.exe em \deployed.

Etapa 3

Atualmente, a pasta \deployed deve conter, além de alguns arquivos intermediários, apenas client.exe e SideBySide.dll, e esta última terá sido registrada por seu processo de build. Para verificar se o servidor e o cliente trabalham juntos nessas circunstâncias normais, execute \deployed\client.exe e observe a saída esperada "1.0.0-CPP".

Etapa 4

Este passo a passo é sobre COM sem registro, portanto, agora precisamos cancelar o registro do assembly SideBySide. Em um prompt de comando, navegue até a pasta \deployed e execute o comando: regsvr32 /u SideBySide.dll.

Etapa 5

Para ver o efeito que a etapa anterior teve, execute \deployed\client.exe novamente e você verá a mensagem "Classe não registrada". Neste estágio, frustramos o runtime COM de encontrar as informações necessárias no registro, mas ainda não disponibilizamos as informações por meios alternativos. Corrigiremos isso nas etapas a seguir.

Ativação do Registration-Free

etapa 6

Na pasta \deployed, crie um arquivo de manifesto do aplicativo (um arquivo de texto) para o aplicativo client.exe e chame-o de client.exe.manifest. Cole o seguinte no arquivo:

<?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>

Etapa 7

Na pasta \deployed, crie um arquivo de manifesto de assembly privado (um arquivo de texto) para o componente SideBySide.dll e chame-o sidebyside.x.manifest. Cole o seguinte no arquivo:

<?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>

Escrevi os valores guid, que serão particulares ao seu projeto, na forma de espaços reservados. Os respectivos valores desses espaços reservados podem ser encontrados da seguinte maneira, no arquivo SideBySide.idl do projeto SideBy Side.idl ou abrindo SideBySide.dll na ferramenta 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;
   };
};

Etapa 8

Neste ponto, devo abordar o assunto da inserção de arquivos de manifesto do assembly como recursos do Win32. Quando os desenvolvedores são capazes e dispostos a recompilar seus componentes COM, é recomendável que um manifesto de assembly (como o criado na etapa anterior) seja inserido na DLL COM como um recurso win32 do tipo RT_MANIFEST (definido em windows.h). Nos casos em que isso não for possível, tenha cuidado para dar ao assembly (e, consequentemente, ao manifesto do assembly) um nome diferente do nome do arquivo da DLL COM. Portanto, no caso acima, a DLL COM é chamada SideBySide, mas o assembly é chamado SideBySide.X. Se você estiver interessado no motivo dessa restrição, ela será explicada na seção solução de problemas. Neste passo a passo, o manifesto do assembly não é inserido para refletir os muitos casos reais em que isso não será viável.

Etapa 9

Para verificar se, cortesia dos arquivos de manifesto, seu cliente é mais uma vez capaz de ativar a classe SideBySideClass, executar \deployed\client.exe e observar a saída esperada "1.0.0-CPP".

O cliente e o servidor COM do Visual Basic 6.0

Etapa 1

A primeira etapa é criar um servidor COM. Crie um novo projeto de DLL ActiveX do Visual Basic 6.0. No Gerenciador de Projetos, selecione o nó Project1 e, noProperties Window, altere seu nome para SideBySide. No Gerenciador de Projetos, selecione o nó Class1 e, na Janela Propriedades, altere seu nome para SideBySideClass.

Cole a seguinte sub-rotina na Janela de Código:

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

Escolha Arquivo | Faça SideBySide.dll... e navegue até a pasta \implantou e clique em OK. Para impedir que novos GUIDs sejam gerados sempre que você fizer a dll, escolha Projeto | Propriedades sidebyside..., em seguida, clique na guia Componente e, no grupo de compatibilidade de versão, selecione o botão de compatibilidade binária .

Etapa 2

Crie um novo projeto do Visual Basic 6.0 Standard EXE. No Gerenciador de Projetos, selecione o nó Project1 e, na Janela Propriedades, altere seu nome para do cliente. Escolha Arquivo | Salve o Project As e salve o arquivo de formulário e o arquivo de projeto em uma pasta irmão em relação à pasta SideBySide projeto. Escolha Projeto | Referências, marque a caixa de seleção ao lado de SideBySide e clique em OK.

Clique duas vezes no formulário principal no designer de formulários e cole o seguinte código dentro Sub Form_Load():

    Dim obj As New SideBySideClass
    MsgBox obj.Version()

Escolha Arquivo | Faça client.exe... e navegue até a pasta \deployed e escolha OK.

Etapa 3

Atualmente, a pasta \deployed deve conter, além de alguns arquivos intermediários, apenas client.exe e SideBySide.dll; e este último terá sido registrado por seu processo de build. Para verificar se o servidor e o cliente trabalham juntos nessas circunstâncias normais, execute \deployed\client.exe e observe a saída esperada "1.0.0-VB6".

Etapa 4

Este passo a passo é sobre COM sem registro, portanto, agora precisamos cancelar o registro do assembly SideBySide. Em um prompt de comando, navegue até a pasta \deployed e execute o comando: regsvr32 /u SideBySide.dll.

Etapa 5

Para ver o efeito que a etapa anterior teve, execute \deployed\client.exe novamente e você verá a mensagem "Erro em tempo de execução '429': o componente ActiveX não pode criar objeto". Neste estágio, frustramos o runtime COM de encontrar as informações necessárias no registro, mas ainda não disponibilizamos as informações por meios alternativos. Corrigiremos isso nas etapas a seguir.

Etapa 6

Na pasta \deployed, crie um arquivo de manifesto do aplicativo (um arquivo de texto) para o aplicativo client.exe e chame-o de client.exe.manifest. Cole o seguinte no arquivo:

<?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>

Etapa 7

Na pasta \deployed, crie um arquivo de manifesto de assembly privado (um arquivo de texto) para o componente SideBySide.dll e chame-o sidebyside.x.manifest. Cole o seguinte no arquivo:

<?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>

Escrevi os valores guid, que serão particulares ao seu projeto, na forma de espaços reservados. Os respectivos valores desses espaços reservados podem ser encontrados abrindo SideBySide.dll na ferramenta 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;
    };
};

Etapa 8

Para verificar se, cortesia dos arquivos de manifesto, seu cliente é mais uma vez capaz de ativar a classe SideBySideClass, executar \deployed\client.exe e observar a saída esperada "1.0.0-VB6".

Apartamentos incompatíveis

Todos os servidores COM neste passo a passo são criados para serem executados em um Single-Threaded Apartment (ou seja, eles são componentes STA ou threaded de apartamento). Todos os clientes são STA, exceto o cliente C++, que é o MTA (seu thread é executado em um Apartamento Multi-Threaded). Portanto, há um caso em que o cliente e o servidor residem em apartamentos incompatíveis e, nesse caso, o marshaling entre apartamentos de chamadas COM precisa ocorrer entre um proxy e um stub. É esse caso que usa a seguinte seção do arquivo de manifesto do assembly:

...
<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]}" />
...

Esses elementos fornecem informações que, de outra forma, estariam presentes no Registro. O elemento comInterfaceExternalProxyStub fornece informações suficientes para que de marshalling da biblioteca de tipos ocorra e é apropriado para interfaces COM que derivam do IDispatch (que inclui todas as interfaces de Automação). Nesses casos, ole32.dll fornece o stub de proxy externo usado (ou seja, externo aos arquivos no assembly). Se os componentes COM implementarem apenas de expedição ou interfaces de duplas, esse será o elemento que você deve usar.

As interfaces personalizadas são um pouco mais raras e, para elas, você precisa trabalhar um pouco mais. Considere uma interface chamada ISxSCustom que deriva diretamente de IUnknown e não é compatível com automação. Para SideBySideClass implementar ISxSCustom e para que os clientes chamem seus métodos em um cenário sem registro, preciso adicionar um elemento comInterfaceProxyStub ao meu manifesto do assembly. Conforme sugerido pelo nome do elemento, desta vez o proxy-stub não é não externo: ele é fornecido por SideBySide.dll (se eu marquei o Permitir mesclagem de código proxy/stub caixa de seleção no Assistente de Projeto da ATL) ou em SideBySidePS.dll. Se meu proxy-stub for mesclado, o novo elemento será um filho do elemento de arquivo existente:

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

Caso contrário, preciso adicionar um mais elemento de arquivo declarando a dll proxy-stub:

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

Se eu não precisasse ilustrar os elementos descritos acima, teria criado o servidor COM do Visual C++ para ser de thread para que suas coclasses tivessem sido ativadas no MTA do cliente. Caso o cliente e o servidor existam no mesmo apartamento, os elementos discutidos serão ignorados. No entanto, peço que não dependa dessa circunstância porque pode haver situações fora do seu controle em que um componente é ativado em um apartamento diferente do cliente, mesmo que seus modelos de threading sejam consistentes. Considerando o esforço relativamente pequeno necessário, acho aconselhável sempre incluir a configuração de proxy-stub em seus manifestos.

Usando a API de Contexto de Ativação

Na seção introdução , mencionei que um dos estágios de inicialização do processo de um aplicativo, nas plataformas às quais este artigo se aplica, é localizar um arquivo de manifesto do aplicativo. O arquivo de manifesto do aplicativo contém referências aos assemblies em que o aplicativo tem dependências. No caso da ativação sem registro de componentes COM nativos, o que significa um assembly é um conjunto de uma ou mais DLLs COM coletadas em uma identidade e versão comuns.

Se o aplicativo souber a priori o conjunto potencial de coclasses a ser ativado direta ou indiretamente durante seu tempo de vida, os manifestos do aplicativo serão convenientes. Mas há uma classe relativamente rara de aplicativo (por exemplo, servidores de grade) que, por design, não sabem, antes do runtime, os módulos que eles carregarão. Nesse caso, uma forma de referenciar arquivos de manifesto do assembly após a inicialização do processo é chamada. Isso é atendido pelo de API de contexto de ativação, o uso do qual obvia um arquivo de manifesto do aplicativo. Em sua mais simples, a técnica é inicializar uma estrutura ACTCTX com o local do arquivo de manifesto do assembly e, em seguida, criar e ativar um contexto de ativação dele. As instruções a seguir pressupõem que você já tenha criado o cliente Visual C++ aplicativo descrito na Etapa 2 (Opção A).

Edite stdafx.h e adicione a seguinte linha imediatamente após a definição de _WIN32_DCOM:

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

Se você examinar a função _tmain em client.cpp, entre as chamadas COM inicializar e não inicializar, verá uma instrução composta que ativa e chama o SideBySideClass. Precisamos mover essa instrução composta (tudo entre as chaves) dentro uma nova seção de código que inicializa o contexto de ativação da seguinte maneira:

   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);
      }
   }

O código acima é executado antes que as coclasses sejam ativadas. O código simplesmente lê o arquivo de manifesto do assembly (cujo nome é codificado neste exemplo, mas deve corresponder a qualquer assembly que você deseja carregar dinamicamente) em um contexto de ativação que ele ativa (ou seja, torna atual). Desse ponto em diante, a ativação de coclasses ocorre como antes. Agora você pode descartar client.exe.manifest.

Uma alternativa à API de contexto de ativação direta, mas disponível apenas no Windows Server 2003, é o objeto Microsoft.Windows.ActCtx.

Desnecessário dizer que há muito mais na API de contexto de ativação do que eu mostrei aqui e você pode encontrar um link para a documentação completa da API na seção De Leitura Adicional.

Solucionando problemas

Como vimos, a ativação sem registro de componentes COM não requer nenhum código especial no servidor ou no cliente. Tudo o que é necessário é um par correspondente de arquivos de manifesto.

Sugiro que você aborde seu próprio desenvolvimento sem registro da maneira como este passo a passo faz. Especificamente: primeiro chegue a um estado conhecido vendo seu cliente trabalhando com um servidor registrado; em seguida, cancele o registro do servidor e verifique se a mensagem de erro é o que você esperava; e, por fim, remediar a situação criando e implantando arquivos de manifesto. Dessa forma, seus esforços de solução de problemas em torno da ativação sem registro serão limitados à estrutura dos arquivos de manifesto (e à inserção correta do manifesto do assembly se você optar por fazer isso).

Ao solucionar problemas de COM sem registro, o Visualizador de Eventos no Windows Server 2003 é seu amigo. Quando o Windows XP ou o Windows Server 2003 detecta um erro de configuração, ele normalmente mostrará uma caixa de mensagem de erro intitulada para o aplicativo que você iniciou e contendo a mensagem "Este aplicativo falhou ao iniciar porque a configuração do aplicativo está incorreta. Reinstalar o aplicativo pode corrigir esse problema." Aconselho que, sempre que vir essa mensagem, reproduza o problema no Windows Server 2003, consulte o Log de Eventos do Sistema e procure eventos da origem do SideBySide. O motivo pelo qual não sugiro que você examine o Log de Eventos do Windows XP nesses casos é que ele invariavelmente conterá uma mensagem como "Gerar Contexto de Ativação falhou para [caminho]\[nome do arquivo do aplicativo]. Manifesto. Mensagem de erro de referência: a operação foi concluída com êxito", o que não ajuda a identificar o problema.

Os esquemas dos vários arquivos de manifesto estão documentados no SDK da Plataforma sob o título de Referência de Arquivos de Manifesto e a ferramenta de validação de esquema Manifestchk.vbs está disponível, portanto, aqui só chamarei alguns pontos relevantes para o passo a passo. Primeiro, vamos examinar o arquivo de manifesto do assembly. Por exemplo, observe a Etapa 7.

Você lembrará que, no sentido de COM sem registro, um assembly é uma ideia abstrata à qual você associa um ou mais arquivos físicos por meio do conteúdo do manifesto do assembly arquivo. O nome do assembly é exibido em três locais e deve ser idêntico em cada um: no nome atributo do elemento assemblyIdentity do arquivo de manifesto do assembly; no nome atributo do elemento dependentAssembly/assemblyIdentity do arquivo de manifesto do aplicativo; e o nome do próprio arquivo de manifesto do assembly, excluindo o .manifest extensão. Se o nome do arquivo de manifesto não corresponder ao nome no manifesto do aplicativo , você verá a seguinte mensagem no Log de Eventos do Sistema do Windows Server 2003: "Assembly dependente [valor do nome atributo no manifesto do aplicativo] não foi encontrado e o último erro foi que o assembly referenciado não está instalado em seu sistema". Se o nome elemento no assembly manifesto estiver incorreto, você verá a seguinte mensagem no Log de Eventos do Sistema do Windows Server 2003: "A identidade do componente encontrada no manifesto não corresponde à identidade do componente solicitado".

Se SideBySide.dll tivesse sido um componente baseado no .NET Framework, teríamos sido obrigados a inserir o arquivo de manifesto do assembly no assembly SideBySide como um recurso Win32 (e teríamos nomeado o arquivo de manifesto após o assembly do .NET, ou seja, SideBySide.manifest). No entanto, devido à sequência de pesquisa do carregador de assembly, para componentes COM nativos, é opcional inserir o manifesto no módulo. Antes que o carregador de assembly procure [AssemblyName].manifest, ele procura [AssemblyName].dll e pesquisa nele um recurso Win32 do tipo RT_MANIFEST. A configuração dentro do recurso deve ter um elemento AssemblyIdentity que corresponda [AssemblyName], bem como os outros atributos na referência AssemblyIdentity do manifesto do aplicativo.

No entanto, se [AssemblyName].dll for encontrado, mas não conter um manifesto correspondente, o mecanismo de carregamento do assembly será interrompido e não continuar procurando [AssemblyName].manifest. No momento da gravação, isso é verdadeiro para o carregador de assembly no Windows XP (que, nesta circunstância, exibirá sua mensagem habitual de que "a configuração do aplicativo está incorreta"), mas não no Windows Server 2003. No Windows Server 2003, pesquisar continuar e localizará o arquivo de manifesto mesmo se ele tiver correspondido ao nome de um módulo. No entanto, peço que não dependa desse comportamento. Em vez disso, para garantir que você dê suporte consistente a ambas as plataformas, recomendo que você insira o manifesto do assembly em seu assembly como um recurso de RT_MANIFEST sempre que possível. Quando não for viável, você deve dar ao arquivo de manifesto do assembly um nome diferente daquele de qualquer módulo na mesma pasta. Como alternativa, coloque o componente COM em uma pasta filho da pasta do manifesto do assembly e faça referência a esse caminho filho no arquivo do manifesto do assembly elemento.

O elemento assemblyIdentity define a identidade do assembly . Para componentes COM nativos, nem seu nome de nem sua versão atributo precisam corresponder ao de qualquer arquivo físico, embora seja uma boa ideia aplicar algum tipo de consistência.

O elemento do arquivo é o pai dos elementos comInterfaceProxyStub , typelib, e comInterfaceProxyStub. Sua finalidade é localizar os arquivos físicos que compõem o assembly . Se o arquivo nome do elemento atributo não fizer referência correta aos arquivos no sistema de arquivos, CoCreateInstance retornará um HRESULT correspondente a "Classe não registrada" ou "O módulo especificado não pôde ser encontrado". Para que você possa instalar diferentes versões do componente COM em pastas diferentes, o nome atributo pode incluir um caminho. No Windows XP SP2, o caminho pode ser relativo ou absoluto e pode fazer referência a uma pasta em qualquer lugar do sistema de arquivos. O Windows Server 2003 requer o caminho para referenciar a raiz do aplicativo ou uma pasta filho e, se você tentar violar essa regra, verá a seguinte mensagem no Log de Eventos do Sistema do Windows Server 2003: "Erro de sintaxe no manifesto ou arquivo de política [nome do arquivo de manifesto do assembly] [...] O valor [...] é inválido."

O elemento comClass tem apenas um atributo obrigatório: clsid. Se o aplicativo tentar ativar uma coclasse não registrada cujo CLSID não está listado em um elemento comClass no manifesto do assembly, CoCreateInstance retornará um HRESULT com o valor REGDB_E_CLASSNOTREG (0x80040154), o texto da mensagem para o qual é "Classe não registrada".

Como já disse, os elementos typelib e comInterface[External]ProxyStub são necessários caso o cliente e o servidor existam em apartamentos diferentes, portanto, os erros a seguir só podem ser vistos quando esses elementos são processados. Erros de configuração nesses elementos fazem com que CoCreateInstance retorne um HRESULT que corresponda às mensagens "Biblioteca não registrada", "Biblioteca de tipos de carregamento de erro/DLL" ou "nenhuma interface com suporte". Se você vir qualquer uma dessas mensagens, verifique os GUIDs duas vezes e verifique se todos os atributos obrigatórios estão presentes. Você encontrará o esquema de arquivo de manifesto no SDK da Plataforma.

Agora, vamos voltar nossa atenção para o arquivo de manifesto do aplicativo. Para obter um exemplo, volte para a Etapa 6. O manifesto do aplicativo deve ser nomeado no formato [nome do arquivo do aplicativo].manifest. Portanto, no passo a passo, ele foi nomeado client.exe.manifest para deixar claro que ele deve ser lido sempre que client.exe for carregado em um processo. Se isso não for feito corretamente, o CoCreateInstance retornará um HRESULT com o valor REGDB_E_CLASSNOTREG (0x80040154), o texto da mensagem para o qual é "Classe não registrada".

O elemento mais importante no manifesto do aplicativo é o elemento dependentAssembly/assemblyIdentity. Esse elemento é uma referência ao equivalente no manifesto do assembly e os dois devem corresponder exatamente. Uma boa maneira de garantir que eles façam isso é copiar o elemento do manifesto do assembly e colá-lo aqui. Se houver alguma diferença, você verá a seguinte mensagem no Log de Eventos do Sistema do Windows Server 2003: "A identidade do componente encontrada no manifesto não corresponde à identidade do componente solicitado".

Conclusão

O COM sem registro é uma tecnologia que libera componentes COM de uma dependência no Registro do Windows e, consequentemente, libera os aplicativos que os usam de servidores dedicados. Ele permite que aplicativos com dependências em versões diferentes do mesmo componente COM compartilhem uma infraestrutura e carreguem essas várias versões de componente COM lado a lado em um eco do mecanismo de versão e implantação do .NET Framework.

Este artigo explica uma demonstração da ativação sem registro de componentes COM nativos por aplicativos cliente nativos escritos no Visual C++ e no Visual Basic 6.0 e por um cliente gerenciado. Ele explica alguns de como o mecanismo funciona e sublinha alguns possíveis erros de configuração e como solucioná-los.

Leitura adicional

 

sobre o autor

Steve White é um consultor de desenvolvimento de aplicativos que trabalha na equipe de Suporte Premier para Desenvolvedores no Microsoft UK. Ele dá suporte a clientes que desenvolvem com Visual C#, Windows Forms e ASP.NET. Seu blog tem mais informações sobre seus interesses em música, visualizações e programação.

Leslie Muller é tecnóloga da equipe de Pesquisa & Desenvolvimento do Credit Suisse First Boston. Leslie tem 12 anos de experiência como desenvolvedora e arquiteta técnica, trabalhando em ambientes como serviços financeiros, startups de tecnologia, automação industrial e defesa. Quando ele não está programando ou fazendo pesquisas ele gosta de esquiar, hóquei no gelo e quando possível fazer coisas ligeiramente loucas com veículos motorizados em ambientes extremos como a Islândia ou as Montanhas Rochosas.