Compartilhar via


Visão geral do WPF Add-Ins

o .NET Framework inclui um modelo de plug-in que os desenvolvedores podem usar para criar aplicativos que suportam a extensibilidade de plug-ins. Esse modelo de suplemento permite a criação de suplementos que se integram e estendem a funcionalidade do aplicativo. Em alguns cenários, os aplicativos também precisam exibir interfaces de usuário fornecidas por suplementos. Este tópico mostra como o WPF aumenta o modelo de suplemento do .NET Framework para habilitar esses cenários, a arquitetura por trás dele, seus benefícios e suas limitações.

Pré-requisitos

A familiaridade com o modelo de complementos do .NET Framework é necessária. Para obter mais informações, consulte Suplementos e extensibilidade.

Visão geral do Add-Ins

Para evitar as complexidades da recompilação e reimplantação de aplicativos para incorporar novas funcionalidades, os aplicativos implementam mecanismos de extensibilidade que permitem que os desenvolvedores (de primeiro e de terceiros) criem outros aplicativos que se integram a eles. A maneira mais comum de dar suporte a esse tipo de extensibilidade é por meio do uso de suplementos (também conhecidos como "complementos" e "plug-ins"). Exemplos de aplicações reais que expõem extensibilidade com complementos incluem:

  • Complementos do Internet Explorer.

  • Plug-ins do Windows Media Player.

  • Suplementos do Visual Studio.

Por exemplo, o modelo de suplemento do Windows Media Player permite que desenvolvedores de terceiros implementem "plug-ins" que estendem o Windows Media Player de várias maneiras, incluindo a criação de decodificadores e codificadores para formatos de mídia que não têm suporte nativo pelo Windows Media Player (por exemplo, DVD, MP3), efeitos de áudio e skins. Cada modelo de suplemento é criado para expor a funcionalidade exclusiva de um aplicativo, embora haja várias entidades e comportamentos comuns a todos os modelos de suplemento.

As três principais entidades das soluções típicas de extensibilidade de suplementos são contratos, suplementos e aplicativos host. Os contratos definem como os suplementos se integram aos aplicativos host de duas maneiras:

  • Suplementos se integram à funcionalidade que é implementada por aplicativos host.

  • Aplicativos host expõem a funcionalidade que os suplementos se integrem a ela.

Para que suplementos possam ser usados, é necessário que eles sejam localizados e carregados em tempo de execução pelos aplicativos host. Consequentemente, os aplicativos que dão suporte a suplementos têm as seguintes responsabilidades adicionais:

  • Descoberta: encontrar suplementos que aderem a contratos com suporte por aplicativos host.

  • Ativação: carregar, executar e estabelecer comunicação com suplementos.

  • Isolamento: usar domínios do aplicativo ou processos para estabelecer limites de isolamento que protegem aplicativos de possíveis problemas de execução e segurança com suplementos.

  • Comunicação: permitindo que complementos e aplicativos de hospedagem se comuniquem entre si através de limites de isolamento chamando métodos e passando dados.

  • Gerenciamento de Ciclo de Vida: Carregando e descarregando domínios de aplicação e processos de maneira limpa e previsível (consulte Domínios do Aplicativo).

  • Controle de versão: garantir que os aplicativos host e suplementos possam se comunicar quando novas versões de qualquer um deles forem criadas.

Por fim, desenvolver um modelo de suplemento robusto é uma tarefa não trivial. Por esse motivo, o .NET Framework fornece uma infraestrutura para a criação de modelos de complementos.

Observação

Para obter informações mais detalhadas sobre suplementos, consulte Suplementos e extensibilidade.

Visão geral do modelo de Add-In do .NET Framework

O modelo de suplemento do .NET Framework, encontrado no namespace System.AddIn, contém um conjunto de tipos projetados para simplificar o desenvolvimento da extensibilidade do suplemento. A unidade fundamental do modelo de suplemento do .NET Framework é o contrato, que define como um aplicativo host e um suplemento se comunicam entre si. Um contrato é exposto a um aplicativo host utilizando uma exibição do contrato específica do aplicativo host. Da mesma forma, uma exibição do contrato específica do suplemento é exposta ao suplemento. Um adaptador é usado para permitir que um aplicativo host e um suplemento se comuniquem entre suas respectivas exibições do contrato. Contratos, exibições e adaptadores são referidos como segmentos, enquanto um conjunto de segmentos relacionados constitui um pipeline. Os pipelines são a base sobre a qual o modelo de add-in do .NET Framework dá suporte à descoberta, ativação, isolamento de segurança, isolamento de execução (usando domínios e processos de aplicativos), comunicação, gerenciamento de ciclo de vida e controle de versão.

A soma desse suporte permite que os desenvolvedores criem suplementos que se integram à funcionalidade de um aplicativo host. No entanto, alguns cenários exigem que os aplicativos host exibam interfaces de usuário fornecidas por suplementos. Como cada tecnologia de apresentação no .NET Framework tem seu próprio modelo para implementar interfaces de usuário, o modelo de suplemento do .NET Framework não dá suporte a nenhuma tecnologia de apresentação específica. Em vez disso, o WPF estende o modelo de complemento do .NET Framework com suporte de interface de usuário para complementos.

Suplementos do WPF

O WPF, em conjunto com o modelo de complemento do .NET Framework, permite resolver uma ampla variedade de cenários que exigem que os aplicativos hospedeiros exibam interfaces de usuário de complementos. Em particular, esses cenários são tratados pelo WPF com os dois modelos de programação a seguir:

  1. O suplemento retorna uma interface do usuário. Um suplemento retorna uma interface do usuário para o aplicativo host por meio de uma chamada de método, conforme definido pelo contrato. Esse cenário é usado nos seguintes casos:

    • A aparência de uma interface do usuário retornada por um suplemento depende de dados ou condições que existem somente em tempo de execução, como relatórios gerados dinamicamente.

    • A interface de usuário para serviços proporcionados por um suplemento difere da interface de usuário dos aplicativos anfitriões que podem usar o suplemento.

    • O suplemento executa principalmente um serviço para o aplicativo host e relata o status para o aplicativo host por meio de uma interface do usuário.

  2. O suplemento é uma interface do usuário. Um suplemento é uma interface do usuário, conforme definido pelo contrato. Esse cenário é usado nos seguintes casos:

    • Um suplemento não fornece serviços que não sejam exibidos, como um anúncio.

    • A interface de usuário para serviços fornecidos por um complemento é comum a todos os aplicativos hospedeiros que podem usar esse complemento, como uma calculadora ou seletor de cores.

Esses cenários exigem que os objetos de interface do usuário possam ser passados entre o aplicativo host e os domínios de aplicativo de suplemento. Como o modelo de plug-in do .NET Framework depende da comunicação remota entre domínios de aplicativo, os objetos passados entre eles devem ser remotáveis.

Um objeto remoto é uma instância de uma classe que faz um ou mais dos seguintes procedimentos:

Observação

Para obter mais informações sobre a criação de objetos remotos do .NET Framework, consulte Tornando objetos remotos.

Os tipos de interface do usuário do WPF não são remotamente acessíveis. Para resolver o problema, o WPF estende o modelo de add-in do .NET Framework para habilitar a interface do usuário do WPF criada por add-ins a ser exibida em aplicativos host. Esse suporte é fornecido pelo WPF por dois tipos: a interface INativeHandleContract e dois métodos estáticos implementados pela classe FrameworkElementAdapters: ContractToViewAdapter e ViewToContractAdapter. Em um alto nível, esses tipos e métodos são usados da seguinte maneira:

  1. O WPF exige que as interfaces do usuário fornecidas pelos suplementos sejam classes que derivam direta ou indiretamente de FrameworkElement, como formas, controles, controles de usuário, painéis de layout e páginas.

  2. Sempre que o contrato declarar que uma interface do usuário será passada entre o complemento e o aplicativo host, ela deve ser declarada como um INativeHandleContract (não um FrameworkElement). INativeHandleContract é uma representação remota da interface do usuário do complemento que pode ser passada entre limites de isolamento.

  3. Antes de ser passado do domínio de aplicativo do suplemento, um FrameworkElement é empacotado como um INativeHandleContract ao chamar ViewToContractAdapter.

  4. Depois de ser passado para o domínio de aplicativo do host, o INativeHandleContract precisa ser reempacotado como um FrameworkElement pela chamada de ContractToViewAdapter.

Como INativeHandleContract, ContractToViewAdaptere ViewToContractAdapter são usados depende do cenário específico. As seções a seguir fornecem detalhes para cada modelo de programação.

O suplemento retorna uma interface do usuário

Para um suplemento retornar uma IU para um aplicativo host, é necessário o seguinte:

  1. O aplicativo host, o suplemento e o pipeline precisam ser criados, conforme descrito pela documentação Suplementos e Extensibilidade do .NET Framework.

  2. O contrato deve implementar IContract e, para retornar uma interface do usuário, o contrato deve declarar um método com um valor retornado do tipo INativeHandleContract.

  3. A interface do usuário passada entre o complemento e o aplicativo host deve derivar direta ou indiretamente de FrameworkElement.

  4. A interface do usuário retornada pelo suplemento precisa ser convertida de um FrameworkElement para um INativeHandleContract antes de cruzar o limite de isolamento.

  5. A interface do usuário retornada deve ser convertida de um INativeHandleContract para um FrameworkElement depois de cruzar o limite de isolamento.

  6. O aplicativo host exibe o FrameworkElement retornado.

Para ver um exemplo que demonstra como implementar um suplemento que retorna uma interface do usuário, consulte Criar um suplemento que retorna uma interface do usuário.

Add-In é uma interface do usuário

Quando um complemento é uma interface do usuário, são necessários os seguintes elementos:

  1. O aplicativo host, o suplemento e o pipeline precisam ser criados, conforme descrito pela documentação Suplementos e Extensibilidade do .NET Framework.

  2. A interface de contrato para o complemento precisa implementar INativeHandleContract.

  3. O suplemento passado para o aplicativo host precisa derivar direta ou indiretamente de FrameworkElement.

  4. O suplemento precisa ser convertido de um FrameworkElement para um INativeHandleContract antes de cruzar o limite de isolamento.

  5. O suplemento precisa ser convertido de um INativeHandleContract para um FrameworkElement depois de cruzar o limite de isolamento.

  6. O aplicativo host exibe o FrameworkElement retornado.

Para um exemplo que demonstra como implementar um suplemento que seja uma interface do usuário, consulte Criar um suplemento que seja uma interface de usuário.

Retornando múltiplas interfaces do usuário de um suplemento

Os suplementos geralmente fornecem várias interfaces de usuário para exibição nos aplicativos hospedeiros. Por exemplo, considere um complemento que é uma interface do usuário e que também fornece informações de status para o aplicativo host, igualmente como uma interface do usuário. Um suplemento como esse pode ser implementado pelo uso de uma combinação de técnicas de ambos os modelos O suplemento retorna uma interface do usuário e O suplemento é uma interface do usuário.

Suplementos e aplicativos de navegação XAML

Nos exemplos até agora, o aplicativo host foi um aplicativo autônomo instalado. Mas os XBAPs (aplicativos do navegador XAML) também podem hospedar suplementos, embora com os seguintes requisitos adicionais de build e implementação:

  • O manifesto do aplicativo XBAP precisa ser configurado de forma especial para possibilitar o download do pipeline (pastas e assemblies) e do assembly de suplemento para o cache do aplicativo ClickOnce no computador cliente, na mesma pasta do XBAP.

  • O código XBAP para descobrir e carregar complementos deve usar o cache do aplicativo ClickOnce para o XBAP como o fluxo de trabalho e o local do complemento.

  • O XBAP deve carregar o suplemento em um contexto de segurança especial se o suplemento fizer referência a arquivos soltos localizados no local de origem; quando hospedados por XBAPs, os suplementos só podem fazer referência a arquivos soltos localizados no site de origem do aplicativo host.

Essas tarefas são descritas em detalhes nas subseções a seguir.

Configurando o pipeline e o Add-In para a implantação do ClickOnce

Os XBAPs são baixados e executados em uma pasta segura no cache de implantação do ClickOnce. Para que um XBAP hospede um complemento, o pipeline e o assembly do complemento também precisam ser baixados para a pasta segura. Para fazer isso, você precisa configurar o manifesto do aplicativo para incluir ambos o assembly do suplemento e do pipeline para baixar. Isso é feito com mais facilidade no Visual Studio, embora o pipeline e o assembly de suplemento precisem estar na pasta raiz do projeto XBAP do host para que o Visual Studio detecte os assemblies de pipeline.

Consequentemente, a primeira etapa é criar o conjunto de pipeline e conjunto de suplemento na raiz do projeto XBAP, definindo a saída de build de cada projeto de conjunto de pipeline e de conjunto de suplemento. A tabela a seguir mostra os caminhos de saída de build para projetos de assembly de pipeline e projetos de assembly de complementos que estão na mesma solução e pasta raiz que o projeto XBAP do host.

Tabela 1: caminhos de saída de build para os assemblies do pipeline hospedados por um XBAP

Projeto de assembly do pipeline Caminho de saída de build
Contrato ..\HostXBAP\Contracts\
Exibição do suplemento ..\HostXBAP\AddInViews\
Adaptador no lado do suplemento ..\HostXBAP\AddInSideAdapters\
Adaptador Host-Side ..\HostXBAP\HostSideAdapters\
Suplemento ..\HostXBAP\AddIns\WPFAddIn1

A próxima etapa é especificar os assemblies de pipeline e o assembly de suplemento como os arquivos de conteúdo XBAPs no Visual Studio fazendo o seguinte:

  1. Incluindo o assembly do pipeline e do suplemento no projeto clicando com o botão direito do mouse em cada pasta de pipeline no Gerenciador de Soluções e escolhendo Incluir no Projeto.

  2. Definindo a Ação de Build de cada assembly do pipeline e assembly do suplemento para Conteúdo da janela Propriedades.

A etapa final é configurar o manifesto do aplicativo para incluir os arquivos do assembly do pipeline e o arquivo do assembly do suplemento para download. Os arquivos devem estar localizados em pastas na raiz da pasta no cache ClickOnce que o aplicativo XBAP ocupa. A configuração pode ser obtida no Visual Studio fazendo o seguinte:

  1. Clique com o botão direito do mouse no projeto XBAP, clique em Propriedades, clique em Publicar e, em seguida, clique no botão Arquivos de Aplicativo.

  2. Na caixa de diálogo Arquivos de Aplicativo, defina o Status da Publicação de cada DLL de suplemento e de pipeline a Incluir (Auto)e defina o Grupo de Download para cada DLL de pipeline e de suplemento para (Obrigatório).

Usando o pipeline e o suplemento da base de aplicativo

Quando o pipeline e o suplemento são configurados para a implantação do ClickOnce, eles são baixados para a mesma pasta de cache do ClickOnce que o XBAP. Para utilizar o pipeline e o suplemento do XBAP, o código XBAP precisa obtê-los da base do aplicativo. Os diversos tipos e membros do modelo de suplemento do .NET Framework para usar pipelines e suplementos dão suporte especial para esse cenário. Em primeiro lugar, o caminho é identificado pelo valor de enumeração ApplicationBase. Você pode usar esse valor com sobrecargas de membros de suplemento pertinentes para usar pipelines que incluem o seguinte:

Acessando o site de origem do host

Para garantir que um suplemento possa referenciar arquivos do site de origem, o suplemento deve ser carregado com isolamento de segurança equivalente ao aplicativo host. Esse nível de segurança é identificado pelo valor de enumeração AddInSecurityLevel.Host e passado para o método Activate quando um suplemento é ativado.

Arquitetura de suplemento do WPF

No nível mais alto, como vimos, o WPF permite que os suplementos do .NET Framework implementem interfaces de usuário (que derivam direta ou indiretamente de FrameworkElement) usando INativeHandleContract, ViewToContractAdapter e ContractToViewAdapter. O resultado é que o aplicativo host recebe um FrameworkElement como retorno, o qual é exibido da interface do usuário no aplicativo host.

Para cenários simples de add-ins de interface do usuário, esses são todos os detalhes de que um desenvolvedor precisa. Para cenários mais complexos, particularmente aqueles que tentam utilizar serviços adicionais do WPF, como layout, recursos e associação de dados, é necessário ter um conhecimento mais detalhado de como o WPF estende o modelo de suplemento do .NET Framework com suporte à interface do usuário para entender seus benefícios e limitações.

Fundamentalmente, o WPF não passa uma interface do usuário de um suplemento para um aplicativo host; ao invés disso, o WPF transmite o identificador de janela Win32 da interface do usuário através da interoperabilidade com o WPF. Dessa forma, quando uma interface do usuário de um suplemento é passada para um aplicativo host, ocorre o seguinte:

  • No lado do suplemento, o WPF adquire um identificador de janela para a interface do usuário que será exibida pelo aplicativo host. O handle da janela é encapsulado por uma classe interna do WPF que herda de HwndSource e implementa INativeHandleContract. Uma instância dessa classe é retornada por ViewToContractAdapter e passa por marshaling do domínio do aplicativo do suplemento ao domínio do aplicativo host.

  • No lado do aplicativo host, o WPF reempacota o HwndSource como uma classe interna do WPF que deriva de HwndHost e consome INativeHandleContract. Uma instância dessa classe é retornada por ContractToViewAdapter ao aplicativo host.

HwndHost existe para exibir interfaces de usuário, identificadas por identificadores de janela, de interfaces de usuário do WPF. Para obter mais informações, consulte Interoperação entre WPF e Win32.

Em resumo, INativeHandleContract, ViewToContractAdapter e ContractToViewAdapter existem para permitir que o identificador de janela de uma interface do usuário do WPF seja passado de um suplemento para um aplicativo host, onde ele é encapsulado por um HwndHost e exibido na interface do usuário do aplicativo host.

Observação

Como o aplicativo host recebe um HwndHost, ele não consegue converter o objeto retornado por ContractToViewAdapter para o tipo em que foi implementado pelo suplemento (por exemplo, um UserControl).

Por sua natureza, HwndHost tem certas limitações que afetam como os aplicativos host podem usá-los. No entanto, o WPF estende o HwndHost com várias funcionalidades para cenários de suplemento. Esses benefícios e limitações são descritos abaixo.

Benefícios de suplementos do WPF

Como as interfaces de usuário do suplemento do WPF são exibidas por aplicativos host por meio de uma classe interna derivada de HwndHost, essas interfaces de usuário são limitadas pelas funcionalidades de HwndHost com relação aos serviços de interface do usuário do WPF, como layout, renderização, associação de dados, estilos, modelos e recursos. No entanto, o WPF aumenta sua subclasse de HwndHost interna com recursos adicionais que incluem o seguinte:

  • Tabulação entre a interface do usuário de um aplicativo host e a interface do usuário de um suplemento. Observe que o modelo de programação "o suplemento é uma interface do usuário" requer que o adaptador no lado do suplemento substitua QueryContract para habilitar tabulações, seja o suplemento totalmente confiável ou parcialmente confiável.

  • Respeitando os requisitos de acessibilidade para interfaces de usuário de suplemento exibidas nas interfaces de usuário do aplicativo host.

  • Habilitar aplicativos WPF para serem executados com segurança em vários cenários de domínio do aplicativo.

  • Impedir o acesso ilegal a identificadores de janela da interface do usuário do suplemento quando os suplementos são executados com isolamento de segurança (ou seja, uma área restrita de segurança de confiança parcial). Chamar ViewToContractAdapter garante essa segurança:

    • Para o modelo de programação "o suplemento retorna uma interface do usuário", a única maneira de passar o identificador de janela para uma interface do usuário do suplemento através do limite de isolamento é chamar ViewToContractAdapter.

    • Para o modelo de programação "o suplemento é uma interface do usuário", é necessário substituir QueryContract no adaptador do lado do suplemento e chamar ViewToContractAdapter (conforme mostrado nos exemplos anteriores), assim como chamar a implementação QueryContract do adaptador do lado do suplemento a partir do adaptador do lado do host.

  • Fornecendo proteção de execução de domínio de vários aplicativos. Devido a limitações com domínios do aplicativo, as exceções sem tratamento que forem lançadas em domínios do aplicativo do suplemento fazem com que todo o aplicativo falhe, mesmo com a existência do limite de isolamento. No entanto, o WPF e o modelo de suplemento do .NET Framework fornecem uma maneira simples de solucionar esse problema e melhorar a estabilidade do aplicativo. Um suplemento para WPF que exibe uma interface de usuário cria um Dispatcher para o thread no qual o domínio do aplicativo é executado, contanto que o aplicativo host seja um aplicativo WPF. Você pode detectar todas as exceções não tratadas que ocorrem no domínio do aplicativo ao manipular o evento UnhandledException do complemento WPF Dispatcher. Você pode obter o Dispatcher da propriedade CurrentDispatcher.

Limitações de suplementos WPF

Além dos benefícios que o WPF adiciona os comportamentos padrão fornecidos pelo HwndSource, HwndHost e identificadores de janela, também há limitações para interfaces do usuário de suplemento que são exibidas de aplicativos host:

  • As interfaces do usuário de suplemento exibidas de um aplicativo host não respeitam o comportamento de recorte do aplicativo host.

  • O conceito de espaço aéreo em cenários de interoperabilidade também se aplica a suplementos (consulte Visão geral das regiões de tecnologia).

  • Os serviços de interface do usuário de um aplicativo host, tais como herança de recursos, associação de dados e comandos, não estão automaticamente disponíveis para interfaces do usuário de suplemento. Para fornecer esses serviços para o suplemento, você precisa atualizar o pipeline.

  • Uma interface do usuário de suplemento não pode ser girada, dimensionada, distorcida e nem ser afetada de nenhuma outra forma por uma transformação (consulte Visão geral de transformações).

  • O conteúdo dentro de interfaces de usuário para suplemento renderizado por operações de desenho do namespace System.Drawing pode incluir a mesclagem alfa. No entanto, uma interface do usuário de suplemento e a interface do usuário do aplicativo host que a contém precisam ser 100% opacas; em outras palavras, a propriedade Opacity em ambos precisa ser definida como 1.

  • Se a propriedade AllowsTransparency de uma janela no aplicativo host que contém uma interface de usuário do suplemento estiver definida como true, o suplemento ficará invisível. Isso será verdadeiro mesmo se a interface do usuário do suplemento for 100% opaca (ou seja, se a propriedade Opacity tiver um valor de 1).

  • Uma interface do usuário de suplemento precisa aparecer acima de outros elementos do WPF na mesma janela de nível superior.

  • Nenhum componente da interface de usuário de um complemento pode ser renderizado usando um VisualBrush. Em vez disso, o suplemento pode tirar um instantâneo da interface do usuário gerada para criar um bitmap que pode ser passado para o aplicativo host usando métodos definidos pelo contrato.

  • Não é possível reproduzir arquivos de mídia de um MediaElement em uma interface de usuário de um suplemento.

  • Os eventos do mouse gerados para a interface do usuário do suplemento não são recebidos nem gerados pelo aplicativo host e a propriedade IsMouseOver para a interface do usuário do aplicativo host tem um valor de false.

  • Quando o foco alterna entre os controles em uma interface do usuário de suplemento, os eventos GotFocus e LostFocus não são recebidos nem acionados pelo aplicativo host.

  • A parte de um aplicativo host que contém uma interface do usuário de suplemento aparece em branco quando impressa.

  • Todos os dispatchers (consulte Dispatcher) criados pela interface do usuário de suplemento precisam ser desligados manualmente antes que o suplemento proprietário seja descarregado se o aplicativo host continuar a execução. O contrato pode implementar métodos que permitam que o aplicativo host sinalize o suplemento antes que este seja descarregado, permitindo assim que a interface do usuário do suplemento desligue os respectivos dispatchers.

  • Se uma interface do usuário do suplemento for um InkCanvas ou contiver um InkCanvas, você não poderá descarregar o suplemento.

Otimização de desempenho

Por padrão, quando vários domínios de aplicativo são usados, os vários assemblies do .NET Framework exigidos por cada aplicativo são todos carregados no domínio desse aplicativo. Como resultado, o tempo necessário para criar novos domínios de aplicativo e iniciar aplicativos neles pode afetar o desempenho. No entanto, o .NET Framework fornece uma maneira de reduzir os tempos de início instruindo os aplicativos a compartilhar assemblies entre domínios de aplicativo se eles já estiverem carregados. Faça isso usando o atributo LoaderOptimizationAttribute, que deve ser aplicado ao método de ponto de entrada (Main). Nesse caso, você deve usar apenas o código para implementar sua definição de aplicativo (consulte Visão geral do Gerenciamento de Aplicativos).

Consulte também