Artigo publicado pelo site: www.bufaloinfo.com.br

 

Juntando o .NET ao COM+ : System.EnterpriseServices

Muita coisa mudou na arquitetura de componentes no Visual Studio .NET . Em primeiro lugar é importante relembrarmos rapidamente a arquitetura de componentes que utilizamos no VB 6.

O formato dos componente que criamos é chamado de COM. O padrão COM define como os components são moldados internamente e permite que qualquer linguagem compatível com o padrão COM utilize um componente COM.

Já o DCOM, abreviação de distributed COM, é um protocolo de rede que permite que um componente COM seja chamado remotamente pela rede.

A partir do momento que temos componentes centralizados em um servidor sendo chamados por um número indeterminado de clients precisamos ter um gerenciamento eficiente de recursos em RAM.
Estes componentes precisam compartilhar recursos, tal como conexões com o banco, além de poderem ter suas instâncias reaproveitadas entre a chamada de um client e outro.

O MTS foi o servidor responsável por todo esse gerenciamento na época do Windows NT. Ao surgir o Windows 2000 a dobradinha COM/MTS mudou de nome, passando a chamar-se COM+ .

No .NET o formato de componentes utilizado mudou muito, passou a se chamar CLS e traz diversas vantagens sobre o formato dos componentes COM.

Ao contrário dos componentes COM, porém, os componentes CLS não estão diretamente em código de máquina mas sim em um código intermediário, IL, que precisa ser executado por outro software, o
CLR. Assim sendo todos os componentes CLS rodam dentro de um ambiente controlado, o CLR.

Mas o CLR apenas controla a execução do código IL, não se encarrega de questões de escalabilidade importantes para um servidor de aplicações. De fato, não existe um servidor de aplicações exclusivo para componentes CLS. Mas o .NET possui um namespace chamado EnterpriseServices que permite que os serviços do COM+ sejam utilizados pelos componentes CLS.

Assim sendo, podemos construir nossos componentes no novo formato, o CLS, e inseri-los dentro do velho COM+ exatamente como temos feito com nossos componentes COM.

Vamos ver, passo a passo, como construirmos um componente CLS e inseri-lo no COM+ e como utiliza-lo tanto por uma aplicação .NET como por uma aplicação no VB 6.

Dentro do VS.NET devemos solicitar a criação de um novo projeto e escolhermos uma nova Class Library. Em nosso exemplo utilizaremos uma Class Library do VB.NET. Não esqueça de determinar o caminho em que o projeto ficará gravado (será mais fácil se for um diretório simples, próximo a raiz).

Defina o nome do projeto como sendo MC (abreviação de MinhaClasse ;-) . Vamos alterar o nome da classe, que inicialmente aparece como Class1 para MinhaClasse.

Precisamos então indicar ao VB que nossa classe fará uso do namespace System.EnterpriseServices.
Um namespace guarda algumas semelhanças com as bibliotecas de componentes : Para utiliza-lo é necessário adicionar uma referencia utilizando Project->Add Reference

Mas a referencia não basta. Para que as constantes e objetos contidos dentro do System.EnterpriseServices possam ser compreendidas dentro de nossa aplicação sem que tenhamos que digitar seu nome completo (ex. System.EnterpriseServices.objeto) é necessário importarmos os nomes de objetos e cia. que existem dentro deste namespace, declarando no inicio do código que faremos uso dele através da instrução Imports.

Imports System.EnterpriseServices
Imports System.EnterpriseServices.ObjectPoolingAttribute
Imports System.Runtime.InteropServices

Na utilização tradicional do COM+ normalmente criavamos nossos componentes e os configurávamos dentro do package do COM+. No .NET isso não acontece. Os parâmetros de configuração do COM+ são inseridos dentro do código do componente e o próprio .NET se encarrega de criar o pacote com a configuração determinada para o componente.

Desta forma no inicio da classe precisaremos inserir as configurações referentes ao COM+, veja :

JustInTimeActivation(), _
ObjectPooling(MinPoolSize:=5, MaxPoolSize:=5, Enabled:=True), _
ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class MinhaClasse

End Class

Com isso estamos determinando o ProgId de nossa classe (o nome da classe), ativando a Just In Time Activation, definindo o Pooling de objetos e determinando que tipo de interface COM a nossa classe terá quando for compilada (Iunknow, IDispatch ou Dual).

A nossa classe precisa herdar as características de um objeto chamado ServicedComponent para que desta forma possa ser inserida no COM+. Para definirmos a herança devemos utilizar a instrução "InHerits"

Public Class MinhaClasse
Inherits ServicedComponent
...
End Class


O próximo passo é programarmos nossa classe. Vamos criar um método (função) chamado calcular que receba um número e multiplique por dois, algo bem simples.

Public Function Calcular(ByVal v As Int32) As Int32
Calcular = v * 2
End Function

Assim como nos componentes COM tradicionais, precisamos chamar setcomplete/setabort ao término do método. Podemos fazer isso de duas formas : Explicitamente ou fazendo um setcomplete implicito.

Para fazer um setcomplete implicito basta marcar o método com uma característica que é chamada de autocomplete. Veja :

Public Function Calcular(ByVal v As Int32) As Int32
Calcular = v * 2
End Function

Neste exemplo ao término do método está automaticamente sendo feito um setcomplete. Cômodo, mas não suficiente : e se você desejasse fazer um setabort ?

Para realizar explicitamente o setcomplete ou o setabort é necessário utilizar um objeto chamado ContextUtil (System.EnterpriseServices.ContextUtil). Veja como fica :


Public Function Calcular(ByVal v As Int32) As Int32
Calcular = v * 2
ContextUtil.SetComplete
End Function


Feito isso devemos especificar algumas características de compilação para a nossa classe. Essas características determinarão a configuração do package no COM+ que, sim, será criado automaticamente pelo .NET

No Project Explorer temos acesso ao AssemblyInfo.VB, que guarda informações para a compilação de nosso componente (lembrando que ele não se transforma em código de máquina mas sim em código IL).
Neste arquivo devemos inserir os seguintes parâmetros :

O Imports para o System.EnterpriseServices também deverá ser adicionado no inicio do arquivo AssemblyInfo.

Os dois parâmetros acima estão realizando a configuração do package no COM+ : Será criado um package chamado NetComponent dentro do COM+ e este será um Library Package.

Para que o assembly (DLL) de nosso componente possa ser inserido no COM+ é necessário que possua uma assinatura, essa assinatura o tornará único perante outros componentes.

Para criarmos esta assinatura precisamos utilizar um utilitário chamado SN.EXE com a chave -k . Isso irá gerar um arquivo SNK para o qual precisaremos apontar no arquivo AssemblyInfo através da seguinte instrução :

O caminho, claro, é apenas um exemplo que utilizei em meu micro.

Para gerarmos o arquivo mykey.snk devemos entrar em uma janela DOS, acessarmos o diretório onde gravamos a aplicação e utilizarmos a seguinte instrução :

SN -k mykey.snk

O arquivo SN assim como outros utilitários encontram-se divididos em 2 caminhos :

winnt\microsoft.NET\Framework\v1.0.2914

e

program files\microsoft.net\frameworkSDK\bin

Você deve encontra-los em seu micro e acrescenta-los ao path para facilitar seu trabalho. Feito isso podemos fazer o Build (menu Build->Build) e o assembly (DLL) de nosso componente será gerado.

Não podemos inserir esse componente diretamente no COM+. Devemos utilizar a aplicação RegSvcS para realizar o registro deste componente. Esta aplicação automaticamente fará a criação do package do COM+ para o componente, seguindo os parâmetros de configuração que informamos.

Entrando em uma janela DOS, encontraremos nossa DLL (MC.DLL) no diretório BIN imediatamente abaixo do diretório MC criado quando nós iniciamos o projeto. Podemos então utilizar RegSvcs MC.DLL

A partir deste ponto podemos entrar no COM+ e já encontraremos nosso package. Você lembra o nome ? Sim, NetComponent, o nome que definimos no Assembly.


O fato de termos definido nossa interface como Dual fez com que fosse criada uma interface padrão dentro do componente, interface essa cujo nome inicia-se com "_" e tem o mesmo nome da classe ("minhaclasse"). Dentro desta interface padrão está o nosso método ("Calcular") e todos os demais métodos das demais interfaces que nossa classe possui. A interface Dual no padrão COM gera algum ganho no momento do acesso ao componente porém no caso do .NET expõem métodos demais para cada componente.

Podemos experimentar os demais tipos de interface. Podemos desregistrar nosso componente com a seguinte instrução :

RegSvcs -u MC.DLL

E em seguida alterarmos o tipo de interface para AutoDispatch, recompilando e registrando novamente o componente.

Ao contrário da Dual uma interface padrão não é automaticamente gerada, portanto nosso método calcular some pois não pertence a nehuma das interfaces de nosso componente que estão definidas.

Assim sendo, no caso do AutoDispatch o nosso método não aparece, no caso do Dual aparece, mas em conjunto com diversos outros métodos. A solução para isso é criarmos uma interface personalizada
para o nosso componente. Assim sendo nossos métodos passam a fazer parte desta interface personalizada.

Veja como fica o código completo :

Imports System.EnterpriseServices
Imports System.EnterpriseServices.ObjectPoolingAttribute
Imports System.Runtime.InteropServices

_
Public Interface Imyinterface
Function Calcular(ByVal v As Int32) As Int32
End Interface

JustInTimeActivation(), _
ObjectPooling(MinPoolSize:=5, MaxPoolSize:=5, Enabled:=True), _
ClassInterface(ClassInterfaceType.AutoDispatch)> _
Public Class MinhaClasse
Inherits ServicedComponent
Implements Imyinterface


Public Function Calcular(ByVal v As Integer) As Integer Implements MC.Imyinterface.Calcular
Calcular = v * 2
End Function
End Class

Na interface definimos apenas a estrutura da função Calcular. Utilizamos na classe a instrução Implements para indicar que iremos implementar a interface. Ao fazermos isso o VB.NET nos obriga a implementar todos os métodos que a interface possui.

No todo da janela de código, nas combos de objeto e método podemos selecionar a nossa interface e o vb.net já nos abre a estrutura desta função calcular para implementarmos. Observe que o truque da síntaxe desta função é o Implements no final indicando que ela se refere a um método da interface que dissemos que iríamos implementar.

Ao realizarmos a instalação desta aplicação no COM+ observaremos que a nossa interface aparecerá na lista de interfaces da classe. Se entrarmos nas propriedades de nosso componente, porém, observaremos que seu caminho físico aponta para um arquivo chamado mscore.dll . Nosso componente não pode rodar fora do CLR, o que temos no COM+ na verdade é como uma pequena "casca" que expõem nosso componente CLS para o mundo COM e faz o interfaceamento entre o mundo COM e o mundo CLR.

image001.gif

image002.gif

Podemos a partir de então acessar nosso componente a partir de qualquer aplicação VB. Para isso basta utilizarmos o References para o componente (para acessarmos via early binding, mas o acesso também poderia ser feito via late binding) e utilizarmos o código a seguir :

Private Sub Command1_Click()
Dim x As Imyinterface

Set x = New MinhaClasse
MsgBox x.Calcular(20)
Set x = Nothing

End Sub


Observe que a variável foi definida como sendo do tipo da interface e, posteriormente, recebeu uma nova instância da "MinhaClasse", que contém a "ImyInterface".

Claro que, sem definirmos a interface no componente, poderíamos instanciar o componente com como qualquer outro componente. Mas os métodos herdados de outras classes seriam visíveis, como vimos um pouco antes pelo COM+ . Fazendo desta forma, com a criação de uma interface e sua utilização no código, apenas nossos métodos ficam visíveis no objeto (x, no exemplo).

Uma observação interessante é que a ativação do package ocorre de forma consideravelmente mais rápida em comparação com componentes criados em VB. Observando-se as propriedades do componente no COM+, em "Concurrency" observamos o threading-model "Any Apartment", equivalente ao "Free Threaded" que pode ser criado em C++. O VB 6 apenas utiliza o Threading model apartment e não o Free Threaded, desta forma o uso de componentes CLS dentro do COM+, apesar da "casca COM" necessária a seu uso acaba ganhando em performance/escalabilidade dos componentes COM criados pelo VB 6.

 

No VB.NET o nosso componente pode ser utilizado da mesma forma como chamaríamos qualquer componente CLS, bastando fazer o references e utilizar o seguinte código de exemplo :

Imports MC

Public Class Form1
Inherits System.Windows.Forms.Form

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim x As Imyinterface

x = New MinhaClasse()
MsgBox(x.Calcular(10))
x = Nothing
End Sub
End Class

É necessário o references tanto para o MC como para o System.EnterpriseServices para que o código acima funcione.

Desta forma concluimos que apesar de ter um modelo de componentes totalmente novo o .NET ainda precisa do COM+ como servidor de aplicações e continuará utilizando-o durante algum tempo. Além disso a facilidade de utilizar componentes CLS no COM+ e acessá-los a partir de aplicações legadas (ASP/VB6) tornará fácil a migração de aplicações atuais.