Arquivos de Configuração para serviços COM+

by Israel Aece 30. July 2008 12:18

Há algum tempo, quando eu ainda fazia curso de COM+, em alguma das minhas noites em claro eu encontrei uma dica interessante (desculpe-me, mas não lembro da referencia) que talvez, utilizaria em algum lugar.

Hoje precisei fazer o uso de uma library em um dos componentes que está rodando dentro do COM+. Até então sem muitos problemas, até que vi que algumas configurações desta library precisavam ser realizadas no arquivo de configuração. Mas onde está o arquivo de configuração de componentes que estão hospedados no COM+? Se a aplicação do COM+ fosse do tipo Library (que é criada dentro do mesmo processo do criador), bastaria eu colocar as configurações no arquivo de configuração da aplicação cliente mas, no meu caso, a aplicação tratava-se de uma aplicação do tipo Server (que é criada em um processo a parte (dllhost.exe)).

Se formos até as propriedades da aplicação COM+, na aba Activation, veremos que há um campo chamado Application Root Directory. Podemos colocar ali o caminho até um diretório no disco exclusivo para esta aplicação e, dentro deste diretório, precisamos também criar 2 arquivos: um é o próprio arquivo tradicional de configuração (application.config); já o segundo, trata-se de um manifesto (application.manifest), que descreve as dependencias que a aplicação requer, mas que neste caso, pode ser simplesmente:

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

Agora pode-se rodar sem maiores problemas que os dados serão capturados deste arquivo. Como o dllhost.exe está dentro do %Windir%\System32, eu acredito que se seguirmos o padrão do .NET, que é criar um arquivo de configuração dentro do mesmo diretório da aplicação, deve funcionar sem problemas (dllhost.exe.config), mas o problema é que isso pode servir várias aplicações e poderemos ter confusões/complicações aqui.

Tags:

CSD

ComVisibleAttribute

by Israel Aece 7. July 2008 12:29

Este atributo está presente desde a versão 1.0 do .NET Framework. Ele tem a finalidade de controlar a acessibilidade de um membro ou tipo para o mundo COM. Já falei sobre ele aqui.

Esse atributo recebe em seu construtor um valor booleano indicando tal acessibilidade, com o valor padrão definido como True. Sendo assim, quando este atributo é omitido, assume-se que ele será exposto ao mundo COM. Só que há uma mudança no tipo de projeto Class Library entre a versão 1.x para a versão 2.0, mais precisamente no Visual Studio 2005. Quando criamos um projeto do tipo Class Library agora, esse atributo vem explicitamente definido como False, no arquivo AssemblyInfo.cs, impedindo que qualquer membro dentro deste Assembly seja visível.

Com isso, sempre que precisar criar um Assembly para hospedá-lo no COM+ ou até mesmo para interoperar com o mundo não gerenciado e, estiver criando sob o Visual Studio 2005, atente-se e defina este atributo como True.

Tags: ,

.NET Framework | Interoperabilidade

WCF - Expondo componente COM+

by Israel Aece 5. July 2008 10:22

Em 2002 a Microsoft lançou a plataforma .NET, visando o desenvolvimento de softwares e componentes. Apesar de suas evidentes inovações (como metadados, serialização e versionamento), ela é essencialmente uma tecnologia de componente, assim como o tradicional COM (Component Object Model); a plataforma .NET fornece meios (produtivos) para a construção destes componentes, baseando-se em código gerenciado, podendo escrevê-los sob qualquer linguagem considerada .NET, como as mais populares Visual Basic .NET ou Visual C#.

A plataforma .NET é a tecnologia mais recente da Microsoft para o desenvolvimento de componentes, e é intenção da Microsoft tornar o .NET a evolução do COM. Apesar da plataforma .NET ser consideravelmente mais fácil e simples de trabalhar, ela tem os mesmos problemas que o COM, ou seja, não possui seus próprios serviços de componentes, como gerenciamento de transações e concorrência (sincronização), dependência de plataforma, segurança, chamadas enfileiradas, etc.. Ambas tecnologias recorrem ao COM+ para conseguir resolver grande parte destes desafios. No mundo .NET, podemos fazer a referência ao Assembly System.EnterpriseServices.dll em nosso projeto e, conseqüentemente, criarmos componentes que suportem essas funcionalidades.

O sistema operacional como o Windows XP, Windows Vista ou até mesmo os sistemas operacionais de servidores trazem nativamente uma ferramenta administrativa para o gerenciamento dos componentes que estão hospedados e fazem o uso do COM+. Essa ferramenta, chamada Component Services, permite-nos, de forma visual, catalogar e gerenciar os componentes hospedados dentro de um computador específico, possibilitando a configuração de segurança, transação, mensageria, etc. e, um dos recursos existentes nesta mesma ferramenta (em conjunto com a versão 1.5 do COM+), é a possibilidade de expor o componente via XML Web Service, ou seja, permitir que o mesmo seja acessado através do protocolo HTTP. Uma vez habilitado, ela cria um diretório virtual dentro do IIS, colocando dentro dele um ponto de acesso até o WSDL (Web Service Description Language) com a descrição do serviço. Com isso, qualquer cliente pode adicionar a referência a este serviço e consumí-lo dentro da aplicação através da internet (HTTP + SOAP). A imagem abaixo exibe a tela de configuração para expor um componente COM+ via XML Web Service:

Figura 1 - Expondo o componente COM+ como XML Web Service.

Uma consideração importante a ser feita é com relação a segurança: como o diretório virtual não estará protegido por SSL, então é necessário que todas as opções de segurança estejam desabilitadas. Através da imagem acima podemos notar que no campo SOAP VRoot especificamos o nome do diretório virtual a ser criado no IIS e, quando clicamos no botão OK, as seguintes tarefas são executadas:

  • O diretório virtual com o nome especificado no campo SOAP VRoot é criado dentro do IIS, apontando fisicamente para um diretório com o mesmo nome criado em \Windows\System32\COM\SOAPVRoots\.

  • Dentro deste diretório são criados dois arquivos: Default.aspx e Web.Config. Estes arquivos são utilizados para localizar e configurar o Web Service.

Com o surgimento do WCF, uma plataforma de comunicação unificada, a Microsoft não se esqueceu do legado, ou seja, de componentes grandes e complexos hospedados no COM+ e, possibilita a utilização do WCF para expor esse componente através do HTTP. Ao contrário do que vimos anteriormente, não precisamos recorrer ao Component Services para isso. Junto com o SDK do .NET Framework 3.X, a Microsoft disponibiliza uma ferramenta chamada Microsoft Service Configuration Editor que, dentre todas as funcionalidades disponibilizadas, uma delas é a possibilidade de integração de um componente COM+ a um serviço WCF. Analisaremos essa ferramenta e essa funcionalidade mais tarde, ainda neste artigo.

Para exemplificar este cenário, vamos criar um componente para hospedá-lo dentro do COM+. Para que isso seja possível, você precisa se atentar a algumas regras impostas pela plataforma .NET para que isso seja possível. O primeiro passo é a criação de um projeto do tipo Class Library para dar origem a uma DLL e, conseqüentemente, ser hospedada dentro do COM+. Depois disso, é necessário fazer a referência para o Assembly System.EnterpriseServices.dll. A primeira regra entra em cena neste momento: toda classe (componente) que será hospedada dentro do COM+ precisa, obrigatoriamente, herdar da classe base ServicedComponent.

Para disponibilizar o componente para o mundo COM, primeiramente deve-se desenhar o componente visando facilitar esse processo e, para isso, devemos explicitamente implementar Interfaces nos componentes que serão expostos. Isso é necessário porque componentes COM “não podem conter” os membros diretamente e, sendo assim, devem ter as Interfaces implementadas. Apesar do COM poder gerar a Interface automaticamente, é melhor criarmos isso manualmente, o que permitirá um maior controle sob o componente e suas formas de atualização. Quando os componentes COM geram automaticamente a sua Interface, você não pode fazer nenhuma mudança em sua estrutura pública. Isso se deve porque componentes COM são imutáveis. Se você romper essa regra, o componente deixará de ser invocado.

Com essas primeiras regras, já conseguimos evoluir para a criação do componente. Abaixo são mostradas a Interface e a classe que a implementa. Reparem que nem a classe e nem a Interface nada sabem sobre WCF.

using System;
using System.Runtime.InteropServices;

namespace Library
{
    [Guid("11EF17BF-C276-41ea-AEA1-C371CF7704F1")]
    public interface IUsuario
    {
        bool Adicionar(string nome, string email);
        void Excluir(int id);
    }
}

using System;
using System.EnterpriseServices;
using System.Runtime.InteropServices;

namespace Library
{
    [Guid("14671242-00FF-4b3c-8F1C-397155857FB7")]
    [ClassInterface(ClassInterfaceType.None)]
    public class Usuarios : ServicedComponent, IUsuario
    {
        public bool Adicionar(string nome, string email)
        {
            //Adicionar a algum repositório
            return true;
        }

        public void Excluir(int id)
        {
            //Excluir do repositório
        }
    }
}

A primeira consideração a fazer é com relação a herança da classe Usuarios. Podemos reparar que ela herda diretamente da classe ServicedComponent e, além disso, implementa a Interface IUsuario. Podemos notar que tanto a classe quando a Interface estão decoradas com o atributo GuidAtrribute. Podemos recorrer a esta técnica para especificar o GUID para cada um deles, ao invés de deixar isso a cargo do registro do componente no COM+. Outro atributo que está decorando a classe Usuarios é o ClassInterfaceAttribute. Quando ele está definido como None, o COM não criará uma Interface padrão para a classe. Neste caso você deverá, explicitamente, fornecer uma Interface para ser implementada na classe que será exposta para o mundo COM.

Nota: Por padrão, quando criamos um projeto do tipo Class Library, o arquivo AssemblyInfo possui um atributo chamado ComVisibleAttribute definido como False. Isso evitará que o componente seja exposto via COM e, conseqüentemente, você não conseguirá hospedá-lo dentro do COM+. Para evitar maiores surpresas, certifique-se de que ele esteja com esse atributo definido como True.

Finalmente, para concluir a criação do componente de exemplo, apenas precisamos definir um Strong Name para ele e instalá-lo dentro do Global Assembly Cache - GAC. Depois disso, para efetivamente instalar dentro do COM+, podemos recorrer ao utilitário regsvcs.exe ou através do próprio Component Services que fornece um assistente que nos auxilia neste processo.

Até o presente momento não abordamos nada exclusivamente de WCF. Todo o processo até então já era conhecido desde a versão 1.0 do .NET Framework. A partir daqui, para conseguirmos expor esse componente via WCF, vamos recorrer ao utilitário Microsoft Service Configuration Editor. Indo até o menu File, Integrate e, em seguida, COM+ Application, um assistente é inicializado para nos auxiliar neste processo. Basicamente o que ele fará é nos conduzir desde a seleção do componente COM+ até a criação do diretório virtual dentro do IIS. A imagem abaixo ilustra a tela de seleção do componente a ser exposto:

Figura 2 - Selecionando o componente COM+.

Ao selecionarmos a Interface a ser exposta, o próximo passo consiste na seleção dos métodos que serão disponibilizados através do serviço. A imagem abaixo exibe essa tela de seleção de métodos e podemos notar que são exatamente os métodos que criamos na Interface IUsuario, um pouco mais acima.

Figura 3 - Selecionando os métodos a serem expostos.

Ao avançar para o próximo passo, o utilitário nos dá a opção para selecionarmos qual será o tipo de host utilizado pelo WCF para disponibilizarmos um ponto de acesso. Se optarmos por COM+ hosted (utiliza um processo Dllhost.exe), isso permitirá o acesso via TCP, HTTP ou Named Pipe mas obrigará que o serviço já esteja rodando para responder as requisições; já no modo Web hosted podemos utilizar o IIS como host do serviço, podendo ser ativado somente quando a primeira requisição for feita e, para o nosso exemplo, é esta opção que utilizaremos. Além disso, ainda há a possibilidade de adicionarmos ao host um endpoint para acesso aos metadados do serviço. A imagem abaixo ilustra essas opções comentadas:

Figura 4 - Escolhendo o modelo de host.

Como optamos pelo modelo Web hosted, o próximo passo exige que informemos o nome do diretório virtual existente onde a estrutura necessária para acessar o serviço será armazenada. É importante que você crie este diretório virtual antes de iniciar este assistente. Para o exemplo, foi criado um diretório virtual chamado ServicoDeUsuarios e, como podemos ver, ele está listado na tela abaixo:

Figura 5 - Selecionando o diretório virtual.

Finalmente depois de todos esses passos rigorosamente configurados, dois arquivos são criados dentro do diretório virtual exigido no último passo do assistente. Os arquivos são: Library.Usuarios.svc e Web.Config. O nome do primeiro arquivo é o full-name da classe que criamos no COM+ acrescido da extensão *.svc que, por sua vez, caracteriza um serviço WCF. Se abrirmos este arquivo em qualquer editor de texto, veremos que ele tem uma única linha, que é a diretiva @ServiceHost. Abaixo consta o conteúdo deste arquivo:

<%@ServiceHost
    Factory="System.ServiceModel.ComIntegration.WasHostedComPlusFactory" 
    Service="{14671242-00ff-4b3c-8f1c-397155857fb7},{629d362f-d757-4f97-86e6-e1901a522607}" %>

O primeiro ponto de análise é o atributo Factory. Esse atributo especifica o tipo que será a "factory" usada por instanciar a classe responsável para servir de host para o serviço. A classe WasHostedComPlusFactory que está dentro do namespace System.ServiceModel.ComIntegration é utilizada quando a implementação do serviço é um componente hospedado no COM+. Já o atributo Service especifica o tipo do serviço que será exposto. Geralmente colocamos aqui o nome da classe que implementa o contrato mas, como estamos expondo um componente que está no COM+, o valor deste atributo recebe uma informação um pouco diferente da tradicional. Neste caso, o atributo Service está recebendo duas GUIDs onde a primeira representa o GUID do componente (classe Usuarios), e a segunda especifica a GUID da aplicação COM+. É importante que essas GUIDs estejam sincronizadas para que tudo funcione da forma correta.

Ainda temos o arquivo Web.Config que também foi automaticamente gerado. Esse arquivo também sofre ligeiras mudanças em relação a uma configuração tradicional de um serviço exposto sob WCF. Abaixo consta o conteúdo do arquivo gerado (algumas linhas foram omitidas por questões de espaço):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ComServiceMexBehavior">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <comContracts>
      <comContract 
          contract="{11EF17BF-C276-41EA-AEA1-C371CF7704F1}"
          name="IUsuario" 
          namespace="http://tempuri.org/11EF17BF-C276-41EA-AEA1-C371CF7704F1"
          requiresSession="true">
        <exposedMethods>
          <add exposedMethod="Adicionar" />
          <add exposedMethod="Excluir" />
        </exposedMethods>
      </comContract>
    </comContracts>
    <services>
      <service 
        behaviorConfiguration="ComServiceMexBehavior" 
        name="{629D362F-D757-4F97-86E6-E1901A522607},{14671242-00FF-4B3C-8F1C-397155857FB7}">
        <endpoint 
            address="IUsuario" 
            binding="wsHttpBinding" 
            bindingConfiguration="comNonTransactionalBinding"
            contract="{11EF17BF-C276-41EA-AEA1-C371CF7704F1}" />
        <endpoint 
            address="mex" 
            binding="mexHttpBinding" 
            bindingConfiguration=""
            contract="IMetadataExchange" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

O que chama a atenção no arquivo Web.Config gerado é com relação ao nome do serviço exposto e, em especial, ao contrato que é exposto através do endpoint. Da mesma forma que o arquivo Library.Usuarios.svc, o Web.Config não faz referência diretamente ao nome da classe ou da Interface, mas sim aos GUIDs que estão relacionados às mesmas. O serviço exposto é configurado com o GUID que representa a aplicação dentro do COM+ em conjunto com o GUID que representa o componente (a classe Usuarios). Da mesma forma, o contrato exposto não é o nome da Interface que criamos (IUsuario), mas sim o GUID que está relacionado a ela.

Para finalizar, o arquivo Web.Config ainda conta com um novo elemento chamado comContracts que é usado para a integração entre o WCF e os componentes hospedados no COM+. Basicamente esta seção permite configurarmos algumas propriedades (como namespace, sessions, etc.) que, a princípio, deveriam estar no componente mas, como ele não foi desenhado para WCF, você tem a chance de customizar isso neste arquivo.

Depois de todo esse processo, o que precisamos neste momento é apenas fazer a referência deste serviço em nossas aplicações cliente e, via proxy, invocarmos o serviço como se fosse um tradicional serviço WCF. Para efeitos de testes, podemos acessar o endereço HTTP através do navegador e, se tudo correr bem, você deverá visualizar uma página (como mostrado abaixo) com o endereço até o WSDL do serviço.

Figura 6 - Efetuando o teste do serviço.

Conclusão: Como podemos notar no decorrer deste serviço, apesar da Microsoft ter criado uma unificada e consistente plataforma de comunicação, ela não se esqueceu das várias aplicações que foram construídas sob tecnologias anteriores, como é o caso dos XML Web Services e componentes hospedados dentro do COM+. Para aqueles que já possuem o componente, basta recorrer ao utilitário Microsoft Service Configuration Editor para configurar como o mesmo será exposto e, finalmente, desfrutar de todo o poder e flexibilidade que o WCF proporciona.

CompenenteCOMPlus.zip (55.84 kb)

Tags: , ,

CSD | Interoperabilidade | WCF

Definindo a Interface padrão

by Israel Aece 16. April 2007 19:59

Quando desejamos expor um componente .NET para o mundo COM, ele deve ter algumas configurações extras em relação aos componentes que são utilizados somente por aplicações .NET.

Uma das configurações necessárias é definirmos o atributo ClassInterfaceAttribute à classe que será acessada via COM. Esse atributo determinará qual será interface que será exposta para que o mundo COM possa consumí-la. O construtor deste atributo recebe como parametro um dos valores contidos no enumerador ClassInterfaceType. Quando o definimos com o valor ClassInterfaceType.None, indicará que a criação da Interface será fornecida por nós. Neste caso, ao registrar o componente com o Type Library Exporter (Tlbexp.exe), o mesmo será exposto com a primeira interface pública visível encontrada pelo utilitário, definindo assim, a interface padrão do componente para o mundo COM.

Mas e quando existerem mais que uma Interface no componente e, por algum motivo, queremos expor não a primeira Interface pública visível, mas a segunda ou a terceira. A versão 2.0 do .NET Framework introduz um novo atributo chamado ComDefaultInterfaceAttribute que, podemos expecificá-lo no componente. Em seu construtor, devemos especificar a Interface (através de um objeto Type) padrão para o mundo COM. O exemplo abaixo exemplifica o componente decorado com este atributo:

[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(ILogin)]
public class AuthenticationServices : IData, ILogin
{
     //Implementação...
}

Para consumir o componente no mundo COM, podemos fazer (exemplo em VB6):

[ Sem o atributo ComDefaultInterface ]
Dim authService As New AuthenticationServices
Dim login As ILogin
Set login = authService

authService.Update()
MsgBox login.Validate("IA", "Pa$$w0rd")

[ Com o atributo ComDefaultInterface ]
Dim authService As New AuthenticationServices
Dim data As IData
Set data = authService

MsgBox authService.Validate("IA", "Pa$$w0rd")
data.Update()

Tags:

Interoperabilidade

Pool de Objetos - COM+

by Israel Aece 16. August 2004 10:56

Não sou lá um especialista em COM+, mas leio sobre. Aliás, é algo que me tenta, ou seja, estou obsecado para fazer um projeto que utilize componentes no COM+ para trabalhar com Pool de Objetos, Transações, entre outras coisas que o mesmo nos oferece, mas até então não tive oportunidade para isto. :'(

Estou nas horas vagas lendo um livro de Juval Löwy chamado COM + e .NET onde é mostrado como criar e manter componentes .NET no COM+, e lendo sobre Pooling de Objeto COM+, algo que achei interessante foi dito, e gostaria de aqui expor:

O objeto é retornado para o pool quando o cliente libera a sua referencia a ele. Sendo assim, sabemos que objetos gerenciados utilizam a chamada “coleta de lixo” (Garbage Collector), e com isso, o objeto gerenciado só retorna ao pool quando este lixo é coletado.

Isso pode ser de certa forma prejudicial, pois poderá haver um retardo entre o momento em que este objeto não é mais necessário até o momento em que o mesmo retorna ao pool, pois lembre-se que o “coletor de lixo” tem um período/tempo para passar, pois ele não sai varrendo o nosso objeto assim que nós o descartamos. Agora o que de mais interessante achei: como já sabemos, um objeto é colocado em pool porque a sua criação é custosa e se este objeto gastar um tempo esperando que o “coletor de lixo” o recolha, então o aplicativo quase não se beneficia do pool de objetos.

Vi que existem duas maneiras de resolver este problema. A primeira é utilizando JITA e a segunda já requer a participação do cliente, pois como todo componente .NET que tira proveito dos serviços COM+ precisa derivar da classe base ServicedComponent que tem um método público estático chamado DisposeObject(), definido como:

public static void DisposeObject(ServicedComponent sc);

Assim que continuar os testes e a leitura por aqui, dou o feedback do uso do método DisposeObject() e sobre o JITA. ;) Mas claro que se alguém quiser contribuir, fico/ficaremos grato ;)

Tags:

.NET Framework

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Indicações

Introdução ao ASP.NET Web API - e-Book