ComVisibleAttribute

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.

WCF – Expondo componente COM+

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 WindowsSystem32COMSOAPVRoots.

  • 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)

Fiddler e Localhost

Para aqueles que usam o Fiddler com IE 7 e não estão conseguindo capturar as requisições e respostas quando estão utilizando proxy, eis aqui uma dica que encontrei no próprio site da ferramenta:

IE7 and the .NET Framework are hardcoded not to send requests for Localhost through any proxies, and as a proxy, Fiddler cannot intercept such traffic.

The workaround is to use your machine name as the hostname instead of Localhost or 127.0.0.1. So, for instance, rather than hitting http://localhost:8081/mytestpage.aspx, instead visit http://machinename:8081/mytestpage.aspx.  Alternatively, you can use http://localhost.:8081/mytestpage.aspx (note the trailing dot after localhost).

…Or, you could Customize your Rules file like so:

static function OnBeforeRequest(oSession:Fiddler.Session){
  if (oSession.host.ToUpper() == “MYAPP”) { oSession.host = “127.0.0.1:8081”; }
}

…and then just hit http://myapp, which will act as an alias for 127.0.0.1:8081.

Fonte: http://www.fiddler2.com/Fiddler/help/hookup.asp

“Exigências” para programar

Com o passar do tempo eu tenho procurado algumas formas de otimizar o dia-a-dia do meu trabalho. Mesmo que, sem querer, você acaba comprando um hardware, fazendo um ajuste ali e outro aqui e, quando você percebe, já não consegue mais trabalhar sem isso. Algumas de minhas “exigências” são:

  • O Microsoft Outlook deve ficar minimizado junto ao systray (o melhor seria ficar fechado :));
  • O Mouse deve ter scroller e também um botão para “voltar”;
  • Pen-Drive com MP3 (meu HD só tem 40GB);
  • Fone de ouvido potente;
  • As preferências do Google devem ser alteradas para exibir 100 resultados por página;
  • Ter um conta no Bloglines para ler blogs em qualquer lugar;
  • Ter a maioria dos aplicativos que uso na barra de tarefas. Atualmente são:
    • Desktop
    • Reflector
    • Notepad
    • Outlook
    • IE
    • VS.NET 2005
    • MSDN Library
    • VS.NET Command Prompt
    • Computer Management
    • Process Explorer
    • Service Configuration Editor
    • Virtual PC
    • Enterprise Manager? Query Analyser? Não! Nada disso. Uso o Server Explorer do VS.NET
  • 2GB de RAM;
  • 2 Monitores (isso é demais!);
  • Skin clássico do Windows;
  • Trabalhar com o VS.NET em full-screen;
  • Atalhos
    • Windows + E: Windows Explorer
    • Windows + D: Show Desktop
    • Windows + R: Run (Executar)
    • Ctrl + F4 (VS.NET): Fecha o documento aberto
    • Ctrl W + L (VS.NET): Server Explorer

Bem, acho que de momento é isso. Acredito que tenho outros macetes mas que não me recordo agora.

Overview da Revista Mundo .NET

Uffa! Finalmente consegui finalizar a leitura das duas primeiras edições da revista Mundo .NET. Ainda bem que ela é bimestral 🙂

Felizmente deu para absorver bastante informação entre a número 1 e 2 justamente porque a edição da revista tentou manter os mesmos autores, criando uma espécie de “coluna”, assim como já acontece com a MSDN americana e, sendo assim, é possível voce acompanhar o raciocínio dos artigos que, muitas vezes, um complementa o outro.

Em ambas as edições, o Wallace Santos da uma visão legal para quem está dando os primeiros passos em WCF, abordando de forma clara cada um dos vários tópicos que o WCF possui. Além disso, o Cláudio Heckler deu uma idéia de como estender o PowerShell com o Visual Studio .NET mas antes, eu preciso entender o que é o Power Shell 🙂 – lembro-me que o Luis Abreu escreveu bastante sobre o assunto. Dos artigos que li, o qual mais me chamou a atenção foi a coluna de Tools que o Eduardo Miranda escreveu. Lá ele abordou sobre ferramentas para testes unitários. Apesar de já ter especulado sobre o assunto, que não levei muito a sério e a coluna abriu a minha cabeça e pude ver alguns exemplos bem interessantes no decorrer dos dois artigos. Para finalizar, o Roberto Prado encerrou as duas edições com comentários sobre a interoperabilidade (lembro-me que assisti uma palestra dele em algum lugar).

Apesar de bimestral, acho que é uma revista que a pena comprar. Em Campinas/Valinhos foi difícil de encontrar uma banca de revista que tivesse. Talvez na Saraiva ou FNAC mas o ideal é assinar que, além de sair alguns reais mais barato, voce tem a comodidade de receber na sua casa.

Definindo a Interface padrão

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()

Vista – Delayed Startup Services

Apesar de não estar utilizando o novo sistema operacional da Microsoft – Windows Vista – tenho lido algumas coisas a respeito do mesmo, e uma das grandes mudanças que temos em relação aos famosos Windows Services é com a nova forma de inicialização que, agora podemos definir como delayed e, para entender melhor qual o impacto desta mudança, veja essa matéria: http://www.vistanews.org/cms/2005/oct/03/delayed-start.

Como podem notar, não é só os aspectos visuais que mudaram no Vista 😉