C# 4.0: Interop com COM facilitada

Email

Este é o último post sobre C# 4, que vou mostrar o último pilar de mudanças propostas para o C# versão 4.0. Se você não viu os outros:

  1. C# 4.0 – Quanto antes melhor – onde apresentei um resumo das novidades, além de assuntos que envolvem a nova versão da linguagem;
  2. C# 4.0: Uma linguagem dinâmica – onde apresentei as novidades do C# 4.0 que vão aproximá-lo mais de uma linguagem dinâmicas.
  3. C# 4.0: Teremos covariância e contravariância – onde apresentei a tão esperada variância do C#.
  4. C# 4.0: Argumentos opcionais e argumentos nomeados – onde apresentei o que você ganha com argumentos nomeados e opcionais.

Para falar de Interop com COM, e das suas dificuldades, puxei da memória um post do Scott Hanselman sobre o assunto. Neste post ele apresenta a dificuldade de chamar um método COM, como uma automação do Excel, a partir do C#:

   ApplicationClass WordApp = new ApplicationClass();
   WordApp.Visible = true;
   object missing = System.Reflection.Missing.Value;
   object readOnly = false;
   object isVisible = true;
   object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");
   Microsoft.Office.Interop.Word.Document aDoc = WordApp.Documents.Open(
       ref fileName,ref missing,ref readOnly,ref missing,
       ref missing,ref missing,ref missing,ref missing,
       ref missing,ref missing,ref missing,ref isVisible,
       ref missing,ref missing,ref missing,ref missing);  

Se você utilizasse VB ficava bem mais fácil. Vejam o mesmo exemplo no VB (11 linhas viram 4 – se usasse With viravam 3):

Dim WordApp = New Microsoft.Office.Interop.Word.ApplicationClass
WordApp.Visible = True
Dim fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\..\..\NewTest.doc")
Dim aDoc As Document = WordApp.Documents.Open(FileName:=fileName, ReadOnly:=True, Visible:=True)  

O post do Scott se chama "Dim não é igual a var". Sugiro a leitura, é bem legal. Além disso o Scott escreve bem.

Pois bem, o que mudou?

Basicamente tudo. Agora com os argumentos opcionais, dos quais falei no último post sobre C# 4 tudo ficou mais fácil:

   ApplicationClass WordApp = new ApplicationClass();
   WordApp.Visible = true;
   object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");
   dynamic aDoc = WordApp.Documents.Open(fileName, ReadOnly:false, Visible:true);

Ou seja, as mesmas 4 linhas que o VB já oferecia.

Há duas opções para resolver este problema. A primeira seria que, quando uma dll COM é referenciada, um método como o Open() receberia todos os argumentos com valores opcionais iguais a System.Reflection.Missing.Value, o que permitiria esta sintaxe. Analisei o código gerado com o Reflector (que já está atualizado para trabalhar com .Net 4.0: parabéns à Redgate), e percebi que toda a mágica acontece em tempo de compilação. Vejam o código gerado pelo compilador:

private static void Main(string[] args)
{
    ApplicationClass app = new ApplicationClass();
    object <>r__ComRefCallLocal0 = "teste";
    object <>r__ComRefCallLocal1 = Type.Missing;
    object <>r__ComRefCallLocal2 = Type.Missing;
    object <>r__ComRefCallLocal3 = Type.Missing;
    object <>r__ComRefCallLocal4 = Type.Missing;
    object <>r__ComRefCallLocal5 = Type.Missing;
    object <>r__ComRefCallLocal6 = Type.Missing;
    object <>r__ComRefCallLocal7 = Type.Missing;
    object <>r__ComRefCallLocal8 = Type.Missing;
    object <>r__ComRefCallLocal9 = Type.Missing;
    object <>r__ComRefCallLocala = Type.Missing;
    object <>r__ComRefCallLocalb = Type.Missing;
    object <>r__ComRefCallLocalc = Type.Missing;
    object <>r__ComRefCallLocald = Type.Missing;
    object <>r__ComRefCallLocale = Type.Missing;
    object <>r__ComRefCallLocalf = Type.Missing;
    Document doc = app.get_Documents().Open(
        ref <>r__ComRefCallLocal0,
        ref <>r__ComRefCallLocal1,
        ref <>r__ComRefCallLocal2,
        ref <>r__ComRefCallLocal3,
        ref <>r__ComRefCallLocal4,
        ref <>r__ComRefCallLocal5,
        ref <>r__ComRefCallLocal6,
        ref <>r__ComRefCallLocal7,
        ref <>r__ComRefCallLocal8,
        ref <>r__ComRefCallLocal9,
        ref <>r__ComRefCallLocala,
        ref <>r__ComRefCallLocalb,
        ref <>r__ComRefCallLocalc,
        ref <>r__ComRefCallLocald,
        ref <>r__ComRefCallLocale,
        ref <>r__ComRefCallLocalf);
}

Ou seja, o compilador entendeu que é uma chamada COM e criou os Type.Missing por conta própria. Não é nada dinâmico, é estático com "syntatic sugar", como se diz…

Outras novidades interessantes (algumas que vocês devem ter notado neste rápido exemplo):

  1. Não há mais a necessidade do ref. (Isso é só para o COM, para o resto do C# continua precisando e é por causa do modelo de programação do COM que é muito particular.)
  2. Todos os métodos retornam dynamic, e não object.
  3. O código de interoperabilidade, que costumava ser gerado em outra dll, passa a ficar no próprio assembly da aplicação, e não mais no assembly externo como era antes. Na figura abaixo você pode ver a opção de "Embed Interop Assembly":

    Embed Interop Assembly no Visual Studio 2010

E também no Reflector você pode ver o Assembly misturado junto às outras classes (deixei expandido para destacar, notem o namespace Microsoft.Office.Interop.Word):

Reflector mostrando o assembly de PIA no meio do assembly da aplicação

O interessante é que, ao que tudo indica, o Visual Studio só coloca no assembly o que ele precisa. Neste caso foram apenas 3 interfaces. Acrescentei ao projeto uma referência ao componente do Excel, e ele não acrescentou ao assembly porque eu não estava utilizando. Inteligente!

Os dois últimos itens da lista anterior são opcionais, mas virão ligados por padrão.

E como todos os objetos do COM serão dynamic, isso significa que você faz tudo o que precisar com eles, como acessar indexadores, métodos e propriedades diretamente.

Mais um exemplo. Este código:

            var ExcepApp = new Microsoft.Office.Interop.Excel.Application();
            var chart = ExcepApp.ActiveWorkbook.Charts.Add();          

Gerou este resultado depois de compilado:

object charts = Activator.CreateInstance(
		Type.GetTypeFromCLSID(
			new Guid("00024500-0000-0000-C000-000000000046"))
	).ActiveWorkbook.Charts.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing); 

Notem como a compilação é inteligente: o objeto do Excel é instanciado a partir do seu class id, o GUID correto é passado, e partir desta instância é então criado um gráfico, passando-se argumentos Type.Missing. E o gráfico, como dito, é dinâmico, portanto do tipo object.

O Reflector também ajudou com o inlining, mas sem dúvida é um excelente exemplo do quanto um bom compilador faz a diferença.

É bom observar, tudo com relação à COM funciona com a máquina virtual disponibilizada pela Microsoft, ao contrário do que encontrei quando fui pesquisar as outras novidades do C# 4.0. Então, se você quiser ver isso tudo funcionando pode se animar.

Isso encerra a apresentação do C# 4.0. Espero que vocês tenham gostado. Vou falar ainda um pouco mais de C# 4.0, mas assumindo que todo mundo já sabe o que é!

Bom Natal a todos!

Email
Esse post foi publicado em Sem categoria e marcado por Giovanni Bassi. Marcar link permanente.

Sobre Giovanni Bassi

Arquiteto e desenvolvedor, agilista, pai, filho, namorado, escalador, provocador.
Programa porque gosta, e começou a trabalhar com isso porque acha que trabalhar como administrador é meio chato. Por esse motivo sempre diz que nunca mais vai virar gerente de ninguém. E também porque acredita que pessoas autogerenciadas funcionam melhor e por acreditar que heterarquia é melhor que hierarquia. Mas isso é outro assunto.
Foi reconhecido  Microsoft MVP depois que alguém notou que ele não dormia a noite pra ficar escrevendo artigos, cuidando e participando do .Net Architects, gravando o podcast Tecnoretórica, escrevendo posts no blog e falando o que bem entende no twitter @giovannibassi. E por falar nisso é no twitter que conta pra todos que gerencia de projetos deve ser feita pelo time e não por um gerentes, que greves em TI são coisas sem sentido e que stored procedure com regras de negócio são malígnas.
Você já deve ter percebido (até porque está lá na primeira frase) que Giovanni é agilista. De tanto gostar disso ele trouxe os programas de certificação e treinamento  PSD e PSM da Scrum.org pro Brasil, e por causa deles, do MVP e de algum trabalho que aparece tem que ficar indo pros EUA de vez enquando, coisa que prefere não fazer. (É bem comum você ouvir ele perguntando porque a Scrum.org e a Microsoft não estão na Itália, por exemplo.)
Junto com alguns Jedis criou a Lambda3, que, apesar de ser pequena e de não ser muito comum no Brasil, insiste em fazer projetos e consultoria direito. Por causa da Lambda3 ele tem trabalhado mais do que quando era consultor independente, mas menos do que a maioria das pessoas. Quer dizer, isso se você considerar que os trabalhos junto à comunidade não são trabalho, caso contrário ele trabalha mais que a maioria das pessoas.
Recentemente ele resolveu que merecia viver melhor e ganhar uns anos de vida e desistiu de ser sedentário, fazendo algum barulho de vez em quando com os amigos no twitter com a hashtag #DotNetEmForma. Por causa do convite recente de amigos do lado Open Source (que ele respeita e admira), começou a escalar, e agora está sempre com as mãos machucadas. Mas ainda dá pra programar. Você encontra ele sempre em algum evento, como o TechEd, e o DNAD, mas também outros menos comuns para o pessoal do .NET, como a RubyConf. Nesses eventos, ou ele está vendo palestras, ou batendo papo com alguém, ou codando alguma aplicação que alguém achou que dava pra fazer durante o evento.
  • http://pinayspeak.com/pinaytest/ Busby SEO Test

    Really wonderful topic here, im fun with your post im keep on reading for this.. thanks by the way please do always good post thanks.

  • http://discountwatchonline.biz/ Gino Franco

    I like your blog curently we are looking for a part time article writer would you be interested?