Pular para o conteúdo principal

.NET COM Interop, IStream e Visual Studio

Investigando oportunidades para otimizar um componente, experimentei definir métodos que não solicitam dados (em arrays etc.) como argumentos mas sim instâncias de IStream, e me deparei com um problema interessante: interoperabilidade com .NET.

Usar componentes COM em programas .NET não é particularmente difícil; no Visual Studio, é possível adicionar componentes COM como referências no projeto.

Se o componente for capaz de Automation, é possível usar a ferramenta tlbimp.exe para gerar manualmente um callable wrapper para o componente, que, se eu entendi as coisas corretamente, não precisa ser distribuído com a aplicação.

Porém, todos os métodos que eu aprendi tem essa peculiaridade: eles parecem assumir que um componente não usa interfaces definidas por outros componentes -- como, por exemplo, declarando parâmetros com tipo IStream, uma interface definida pelo sistema.

Pelo método do callable wrapper, a ferramenta assume que as interfaces mencionadas integram o componente (ou a type library) e portanto geram uma interface .NET correspondente. No meu caso,  o método causava a geração de uma nova interface IStream, ao invés de reusar a definição pré-existente em System.Runtime.InteropServices.ComTypes.

Ora, reusar uma interface como IStream é interessante justamente para permitir reuso de instâncias de IStream pré-definidas no ambiente do usuário. Meu componente permitiria, por exemplo, um script Visual Basic usar MDAC para criar streams em memória e usá-los.

Qual é a solução recomendada para esses casos, então? Ora, surrealmente, a MSDN sugere decompilar o callable wrapper com ildasm.exe, modificar o texto CIL e recompilar isto com ilasm.exe. O objetivo da modificação é remover as interfaces geradas inutilmente e substituir nas listas de argumentos e retornos referências para os tipos certos.

Tendo me acostumado com a idéia de estar prestes a escrever assembler de .NET, numa espécia de reviravolta na história da programação, pensei nos efeitos colaterais sobre meu processo completo de construção.

Em primeiro lugar, desisti de absorver o processo inteiro como sugerido e simplesmente adicionei o texto CIL modificado no Subversion. Agora, posso dizer que o nosso projeto inclui até módulos em assembler, e impressionar os estagiários.

O segundo lugar me fez demorar mais: o Visual Studio não conhece arquivos *.il. Meu objetivo é usar o callable wrapper pelo mecanismo de referências do Visual Studio em um projeto de teste de unidade. Experimentei usar Class Library Projects em C++ e C# juntamente com o mecanismo de Custom Build sem sucesso.

Felizmente, isso é possível com um Makefile Project. Este tipo de projeto permitiu a construção de um assembly .NET a partir de um único fonte em CIL (mais um arquivo de recursos) usando ilasm.exe nas regras Build e Rebuild. Definindo a regra Output corretamente, o projeto de teste de unidade foi capaz de incluir o produto gerado pelo Makefile Project como esperado.

Compreendo que uma ferramenta como tlbimp.exe não possa fazer milagre diante de componentes usando interfaces arbitrárias; porém, me parece preguiça não resolver ao menos o caso das interfaces definidas pelo próprio Windows.

Comentários

Postagens mais visitadas deste blog

Análise vs. Projeto

Eu diria que a ruptura entre Análise e o Projeto acontece quando acabam as considerações sobre o problema e começam as considerações sobre a solução. Em outras palavras, o papel do Analista é considerar o problema, enquanto o papel do Projetista é o de considerar a solução. Assim, o Analista aparece quando um problema aparece, e dá lugar ao Projetista quando o problema foi devidamente analisado, restando produzir para ele uma solução. E diria que a ruptura entre a Especificação e a Implementação acontece quando acabam os artefatos para consumo por humanos e começam os artefatos para consumo por máquinas. Em outras palavras, o papel do Especificador é produzir artefatos para consumo por humanos, enquanto o papel do Implementador é produzir artefatos para consumo por máquinas. Assim, o Especificador aparece quando a atividade é produzir efeitos sobre humanos, e dá lugar ao Implementador quando os humanos estão devidamente satisfeitos e a atividade prossegue para produzir efeito...

JNDI e Plan 9

Acabei de realizar quão similar é o J2EE e o Plan 9 quando observados pela lente da arquitetura de componentes: no Plan 9, o sistema de arquivos é tão capaz quanto o JNDI no J2EE. Um componente no qual eu estou trabalhando agora depende de um componente de banco de dados e de um componente de correio eletrônico. O modo de ligação entre esses componentes se dá através de JNDI: meu componente faz referência aos nomes "dataSource" e "mailSession" no seu contexto local de nomes. Na configuração do componente em uma instalação, esses nomes locais são ligados aos nomes globais que o administrador preparou para oferecer as dependências. É claro que tanto o meu componente quanto suas dependências mantém um contrato entre si, na forma das interfaces java EntityManager e Session. No Plan 9, meu componente seria um programa que faz referência a dois arquivos no seu file system -- um arquivo que repr...

O Problema de Gestão de I/O

A palestra que dei no Sétimo Encontro do Grupo de Usuários de C e C++ do Brasil apresentou meu resultado atual na pesquisa de um framework baseado em uma forma de orientação a aspectos em C++ descrita no "Modern C++ Design" do Alexandrescu. Minha palestra não apresentou, porém, o que é exatamente o problema a resolver, assumindo que fosse razoavelmente conhecido. Seu foco foi apresentar todos os mecanismos disponíveis para se resolver o problema no Linux 2.6, suas peculiaridades e seus aspectos comuns. Uma certa razão me motivou, após esta apresentação, a rebobinar meu raciocínio e considerar o problema original. [1] O que há exatamente de tão problemático na gestão de I/O em um programa? A imagem inicial é a de um programa cujo objetivo é interagir com um dos dispositivos ligados ao computador. A forma básica dessa interação é a cópia de dados do dispositivo para a memória e da memória para o dispositivo, sendo que trabalho útil por parte do programa ocorre enquanto os...