ID do artigo: 839076 - Última revisão: sexta-feira, 28 de outubro de 2005 - Revisão: 3.1

Você enfrenta um comportamento inesperado quando você usa Windows Forms em aplicativos de cliente COM

Dica do SistemaEste artigo aplica-se a um sistema operativo diferente do que está a utilizar. Foi desactivado o conteúdo do artigo, que pode não ser relevante para si.

Nesta página

Expandir tudo | Recolher tudo

Sumário

Quando você abre uma instância de um formulário do Microsoft Windows de um aplicativo de cliente COM, como um aplicativo do Microsoft Visual Basic 6.0 ou um aplicativo MFC (Microsoft Foundation Classes), o formulário .NET do Windows podem se comportar inesperadamente. Por exemplo, quando você pressiona TAB, o foco não altera de um controle para um outro controle. Ou, quando você pressiona ENTER enquanto um botão de comando tem o foco, o evento Click do botão não dispara. Você também pode enfrentar um comportamento inesperado para pressionamentos de teclas ou para atividade de mouse.

Esses sintomas ocorrem porque o aplicativo anterior não implementa o suporte de loop de mensagem que deve ter um formulário do Windows .NET Framework para funcionar corretamente.

Este artigo descreve como resolver esses problemas, exibindo o formulário em um loop de mensagem do .NET Framework que você criar usando o método Application.Run .

Sintomas

Quando você cria uma instância de um formulário do Microsoft Windows de um aplicativo de cliente COM, o formulário pode comportar-se inesperadamente. Por exemplo, quando você cria uma instância do formulário de um aplicativo do Microsoft Visual Basic 6.0 ou de um aplicativo MFC (Microsoft Foundation Classes), o foco não é alterado de um controle para um outro controle quando você pressiona TAB. Ou, se você pressionar ENTER enquanto um botão de comando tem o foco, o evento Click do botão não será acionado. Você também pode enfrentar um comportamento inesperado para pressionamentos de teclas ou para atividade de mouse.

Causa

Esse problema ocorre porque o loop de mensagem que usa o formulário do Windows e o loop de mensagem que o aplicativo de cliente COM fornece são diferentes.

A Microsoft fez uma alteração para resolver esse problema de design.
Para obter mais informações, clique no número abaixo para ler o artigo na Base de dados de Conhecimento da Microsoft:
885446  (http://support.microsoft.com/kb/885446/ ) CORRECÇÃO: Você pode enfrentar um comportamento inesperado ao pressionar TAB ou ENTER em um Windows Form em um computador que esteja executando o .NET Framework 1.1 S
Este artigo descreve como resolver esse problema, exibindo o formulário em um loop de mensagem do .NET Framework que você criar usando o método Application.Run .

Resolução

Para iniciar um loop de mensagem do .NET Framework, use um dos seguintes métodos:

Use o método ShowDialog para exibir o formulário do Windows

Isso é a solução mais fácil para exibir o formulário em um loop de mensagem do .NET Framework. Para fazer isso, substitua as chamadas para o método Show chamadas para o método ShowDialog em seu componente .NET Framework.

O método ShowDialog suspende o loop de mensagem do aplicativo anterior e exibe o formulário como uma caixa de diálogo. Como o loop de mensagem do aplicativo host tiver sido suspenso, o método ShowDialog cria um loop de mensagem novo .NET Framework para processar mensagens do formulário. Isso é a solução mais fácil porque requer o menos código para implementar. No entanto, a desvantagem dessa abordagem é que o formulário será aberto restrito. Esse comportamento bloqueia qualquer interface de usuário (UI) do aplicativo chamada enquanto o formulário do Windows está aberto. Quando o usuário fecha o formulário do Windows, o .NET Framework sai do loop de mensagem e mensagem do aplicativo anterior loop continua sendo executado.

Exibir cada Windows Form em um novo thread

Modificar componente .NET Framework para exibir cada instância de um formulário em seu próprio thread usando seu próprio loop de mensagem. Você não pode ter mais de um loop de mensagem em execução por thread. Portanto, você não pode alterar o loop de mensagem do aplicativo cliente. No entanto, você pode modificar o componente .NET Framework para iniciar um novo thread que usa seu próprio loop de mensagem.

Observação Programação multissegmentada é um conceito avançado que requer atenção antes da implementação. Isso é especialmente verdadeiro se vários segmentos devem acessar um recurso compartilhado. Se vários segmentos devem acessar um recurso compartilhado ao mesmo tempo, certifique-se que você criar os componentes do .NET Framework para segurança do thread.Para obter informações adicionais, clique no número abaixo para ler o artigo na Base de dados de Conhecimento da Microsoft:
316136  (http://support.microsoft.com/kb/316136/ ) Como sincronizar o acesso a um recurso compartilhado em um ambiente multithread com o Visual Basic .NET
Para criar cada instância de um formulário do Windows em um novo thread, execute essas etapas:
  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para fazer isso, execute as seguintes etapas:
    1. Use o Visual Basic .NET 2003 para criar um novo projeto Class Library. Nomeie o projeto COMWinform.
    2. Exclua o arquivo Class1.vb de padrão.
    3. No menu Project , clique em Add Class .
    4. Selecione o modelo COM Class .
    5. Na caixa nome , digite COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do arquivo COMForm.vb, antes da definição de classe.
      Imports System.Windows.Forms
      Imports System.Runtime.InteropServices
    7. Na definição de classe COMForm, cole o código a seguir no código que foi inserido quando você criou class.
          Private frmManager As FormManager
      
          Public Sub ShowForm1()
              ' Call the StartForm method by using a new instance
              ' of the Form1 class.
              StartForm(New Form1)
          End Sub
      
          Private Sub StartForm(ByVal form As Form)
              ' This procedure is used to show all forms
              ' that the client application requests.
              
              ' Later forms will appear on a new message loop.
              If IsNothing(frmManager) Then
                  frmManager = New FormManager
              End If
              frmManager.ShowForm(form)
          End Sub
      
          Protected Overrides Sub Finalize()
              frmManager = Nothing
              MyBase.Finalize()
          End Sub
    8. No menu Project , clique em Add Class .
    9. Clique em Class .
    10. Na caixa nome , digite FormManager.vb e, em seguida, clique em OK .
    11. Replace the contents of the FormManager.vb file with the following code.
      Imports System.Runtime.InteropServices
      Imports System.Threading
      Imports System.Windows.Forms
      
      <ComVisible(False)> _
      Friend Class FormManager
          Public Sub ShowForm(ByVal form As Form)
              Dim thrd As Thread
              Dim wrapper As FormWrapper
              wrapper = New FormWrapper(form)
              thrd = New Thread(AddressOf wrapper.ShowFormOnNewThread)
              thrd.IsBackground = True
              thrd.ApartmentState = ApartmentState.STA
              thrd.Start()
          End Sub
      
          Private Class FormWrapper
              Private WithEvents appContext As ApplicationContext
              Public Sub New(ByVal form As Form)
                  MyBase.New()
                  appContext = New ApplicationContext(form)
              End Sub
      
              Public Sub ShowFormOnNewThread()
                  Application.Run(appContext)
              End Sub
      
              Private Sub ac_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) Handles appContext.ThreadExit
                  appContext.MainForm.Dispose()
                  appContext.MainForm = Nothing
                  appContext.Dispose()
                  appContext = Nothing
              End Sub
          End Class
      End Class
    12. No menu Project , clique em Add Windows Form e, em seguida, clique em Abrir .
    13. Adicione alguns controles TextBox e um botão de comando ao formulário.
    14. Adicione o seguinte código ao manipulador de evento Click do comando botão.
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
         MessageBox.Show("Clicked button")
      End Sub
      
    15. Crie a solução. Esta etapa também registra o projeto para interoperabilidade COM neste computador.
  3. Crie um arquivo executável. Para fazer isso, execute as seguintes etapas:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projeto EXE padrão.
    3. No menu Project , clique em referências .
    4. Adicione uma referência à biblioteca de tipos COMWinform foi gerada quando você criou a solução do Visual Basic. NET. Se você não vi-lo na lista, clique em Procurar para localizar o arquivo de biblioteca (.tlb) do tipo manualmente.
    5. Adicione um botão de comando ao formulário.
    6. No menu Exibir , clique em código e, em seguida, adicione o seguinte código para o módulo de formulário.
      Option Explicit
      
      Private Sub Command1_Click()
          Dim frm As COMWinform.COMForm
          Set frm = New COMWinform.COMForm
          frm.ShowForm1
      End Sub
      
    7. No menu arquivo , clique em Make EXE para compilar o projeto.
  4. Execute o arquivo executável de Visual Basic 6.0 compilado.
  5. Clique no botão. O Windows Form da biblioteca de classe que você criou anteriormente será exibido.
  6. Definir o foco em um dos controles TextBox no Windows Form e, em seguida, pressione TAB para mover entre os controles.
Observe que a tecla TAB funciona com êxito para mover entre controles. Observe também que o evento Click do botão de comando é acionado quando você pressiona ENTER.

Criar um loop de mensagem compartilhada em um novo segmento no componente do .NET Framework

Esse método é semelhante na seção "Exibir cada Windows Form em um novo thread". No entanto, em vez de exibir cada formulário em seu próprio thread usando seu próprio loop de mensagem, esse método cria um loop de mensagem compartilhada que é executado em apenas um novo segmento no componente do .NET Framework.

Esse método com mais precisão representa o comportamento que teria se estivesse executando um aplicativo Windows Forms padrão. Esse design torna mais fácil compartilhar recursos entre vários formulários, porque todos os formulários são executados no mesmo thread. A solução na seção "Exibir cada Windows Form em um novo thread" cria um novo thread para cada formulário. Essa solução requer código de sincronização do segmento adicionais para compartilhar recursos entre formulários diferentes.

Como esse método é mais semelhante que os outros métodos para o comportamento de um aplicativo Windows Forms, observe que todos os formulários .NET Framework que abre o aplicativo cliente serão fechado quando o loop de mensagem do .NET Framework pára. Esse comportamento ocorre quando o usuário fecha o formulário que é designado como o formulário principal para ApplicationContext . ApplicationContext é usado para iniciar o loop de mensagem.

No exemplo de código a seguir, o formulário principal de ApplicationContext é definido como o primeiro formulário que abre o aplicativo cliente. Portanto, quando o usuário fecha essa instância do formulário, a sai do loop de mensagem .NET Framework e todos os outros formulários do Windows será fechado.

Para criar um loop de mensagem compartilhada em um novo segmento para todos os formulários usar, execute essas etapas:
  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para fazer isso, execute as seguintes etapas:
    1. Use o Visual Basic .NET 2003 para criar um novo projeto Class Library chamado COMWinform.
    2. Exclua o arquivo Class1.vb de padrão.
    3. No menu Project , clique em Add Class .
    4. Selecione o modelo COM Class .
    5. Na caixa nome , digite COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do arquivo COMForm.vb, antes da definição de classe.
      Imports System.Windows.Forms
      Imports System.Runtime.InteropServices
    7. Na definição de classe de COMForm, cole o código a seguir no código que foi inserido quando você criou class.
      
          Private WithEvents frmManager As FormManager
      
          Public Sub ShowForm1()
              ' Call the StartForm method by using a new instance
              ' of the Form1 class.
              StartForm(New Form1)
          End Sub
      
          Private Sub StartForm(ByVal frm As Form)
      
              ' This procedure is used to show all forms
              ' that the client application requests. When the first form
              ' is displayed, this code will create a new message
              ' loop that runs on a new thread. The new form will
              ' be treated as the main form.
      
              ' Later forms will be shown on the same message loop.
              If IsNothing(frmManager) Then
                  frmManager = New FormManager(frm)
              Else
                  frmManager.ShowForm(frm)
              End If
          End Sub
      
          Private Sub frmManager_MessageLoopExit() Handles frmManager.MessageLoopExit
              'Release the reference to the frmManager object.
              frmManager = Nothing
          End Sub
      
    8. No menu Project , clique em Add Class .
    9. Clique em Class .
    10. Na caixa nome , digite FormManager.vb e, em seguida, clique em OK .
    11. Substituir o conteúdo do arquivo FormManager.vb com o seguinte código.
      Imports System.Runtime.InteropServices
      Imports System.Threading
      Imports System.Windows.Forms
      
      <ComVisible(False)> _
      Friend Class FormManager
          ' This class is used so that you can generically pass any
          ' form that you want to the delegate.
      
          Private WithEvents appContext As ApplicationContext
          Private Delegate Sub FormShowDelegate(ByVal form As Form)
          Event MessageLoopExit()
      
          Public Sub New(ByVal MainForm As Form)
              Dim t As Thread
              If IsNothing(appContext) Then
                  appContext = New ApplicationContext(MainForm)
                  t = New Thread(AddressOf StartMessageLoop)
                  t.IsBackground = True
                  t.ApartmentState = ApartmentState.STA
                  t.Start()
              End If
          End Sub
      
          Private Sub StartMessageLoop()
              ' Call the Application.Run method to run the form on its own message loop.
              Application.Run(appContext)
          End Sub
       
           Public Sub ShowForm(ByVal form As Form)
      
              Dim formShow As FormShowDelegate
      
              ' Start the main form first. Otherwise, focus will stay on the 
              ' calling form.
              appContext.MainForm.Activate()
      
              ' Create a new instance of the FormShowDelegate method, and
              ' then invoke the delegate off the MainForm object.
              formShow = New FormShowDelegate(AddressOf ShowFormOnMainForm_MessageLoop)
              appContext.MainForm.Invoke(formShow, New Object() {form})
          End Sub
      
          Private Sub ShowFormOnMainForm_MessageLoop(ByVal form As Form)
              form.Show()
          End Sub
       
          Private Sub ac_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) Handles appContext.ThreadExit
              appContext.MainForm.Dispose()
              appContext.MainForm = Nothing
              appContext.Dispose()
              appContext = Nothing
              RaiseEvent MessageLoopExit()
          End Sub
        End Class
      End Class
    12. No menu Project , clique em Add Windows Form e, em seguida, clique em Abrir .
    13. Adicione alguns controles TextBox e um botão de comando ao formulário.
    14. Adicione o seguinte código ao manipulador de evento Click do comando botão.
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
         MessageBox.Show("Clicked button")
      End Sub
      
    15. Crie a solução. Esta etapa também registra o projeto para interoperabilidade COM neste computador.
  3. Crie um arquivo executável. Para fazer isso, execute as seguintes etapas:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projeto EXE padrão.
    3. No menu Project , clique em referências .
    4. Adicione uma referência à biblioteca de tipos COMWinform foi gerada quando você criou a solução do Visual Basic. NET. Se você não vi-lo na lista, clique em Procurar para localizar o arquivo de biblioteca (.tlb) do tipo manualmente.
    5. Adicione um botão de comando ao formulário.
    6. No menu Exibir , clique em código e, em seguida, adicione o seguinte código para o módulo de formulário.
      Option Explicit
      
      Private Sub Command1_Click()
          Dim frm As COMWinform.COMForm
          Set frm = New COMWinform.COMForm
          frm.ShowForm1
      End Sub
      
    7. No menu arquivo , clique em Make EXE para compilar o projeto.
  4. Execute o arquivo executável de Visual Basic 6.0 compilado.
  5. Clique no botão. O Windows Form da biblioteca de classe que você criou anteriormente será exibido.
  6. Definir o foco em um dos controles TextBox no Windows Form e, em seguida, pressione TAB para mover entre os controles.
Observe que a tecla TAB funciona com êxito para mover entre os controles. Observe também que o evento Click do botão de comando é acionado quando você pressiona ENTER.

Situação

Esse comportamento é por design.

Mais Informações

Mensagem de um aplicativo loop é um loop de programa interno que recupera mensagens da fila de mensagens do thread, converte e envia-los para o aplicativo para ser manipulado. O loop de mensagem para um formulário do Windows não tem a mesma arquitetura como loops de mensagem que os aplicativos anteriores, como aplicativos Visual Basic 6.0 e aplicativos MFC, fornecer. As mensagens de janela lançadas para o loop de mensagem podem ser manipuladas diferente que espera que o formulário do Windows. Portanto, pode ocorrer um comportamento inesperado. Algumas combinações de pressionamento de tecla podem não funcionar, alguma atividade de mouse pode não funcionar ou alguns eventos podem não ser disparados conforme o esperado.

Observação Um loop de mensagem do .NET Framework é a arquitetura para os quais há suporte somente para exibição e para usar um Windows Form. As soluções que estão descritas na seção "Resolução" se aplicam apenas a exibir um formulário do Windows. O recipiente para os quais há suporte somente para hospedar um controle do .NET Framework por meio de um COM callable wrapper (CCW) é Microsoft Internet Explorer. Como um controle deve estar em um recipiente, esse controle deve usar o loop de mensagem que fornece o recipiente. Portanto, você não pode usar as soluções que são descritas na seção "Resolução" para um controle do .NET Framework.

Para obter informações adicionais, clique no número abaixo para ler o artigo na Base de dados de Conhecimento da Microsoft:
311334  (http://support.microsoft.com/kb/311334/ ) Recipientes de controle ActiveX que oferecem suporte a controles .NET
317346  (http://support.microsoft.com/kb/317346/ ) Controla nativo versus COM-callable .NET no Internet Explorer

Etapas para reproduzir o comportamento

  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para fazer isso, execute as seguintes etapas:
    1. Use o Visual Basic .NET 2003 para criar um novo projeto Class Library chamado COMWinform.
    2. Exclua o arquivo Class1.vb de padrão.
    3. No menu Project , clique em Add Class .
    4. Selecione o modelo COM Class .
    5. Na caixa nome , digite COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do arquivo COMForm.vb, antes da definição de classe.
      Imports System.Windows.Forms
      Imports System.Runtime.InteropServices
    7. Na definição de classe de COMForm, cole o código a seguir no código que foi inserido quando você criou o class.
          Public Sub ShowForm1()
              ' Create an instance of the .NET Windows Form (Form1),
              ' and then display it
      
              Dim frm As Form1
              frm = New Form1
              frm.Show
          End Sub
      End Class
    8. No menu Project , clique em Add Windows Form e, em seguida, clique em Abrir .
    9. Adicione alguns controles TextBox e um botão de comando ao formulário.
    10. Adicione o seguinte código ao manipulador de evento Click do comando botão.
      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
         MessageBox.Show("Clicked button")
      End Sub
      
    11. Crie a solução. Esta etapa também registra o projeto para interoperabilidade COM neste computador.
  3. Crie um arquivo executável. Para fazer isso, execute as seguintes etapas:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projeto Standard EXE.
    3. No menu Project , clique em referências .
    4. Adicione uma referência à biblioteca de tipos COMWinform foi gerada quando você criou a solução do Visual Basic. NET. Se você não vi-lo na lista, clique em Procurar para localizar o arquivo de biblioteca (.tlb) do tipo manualmente.
    5. Adicione um botão de comando ao formulário.
    6. No menu Exibir , clique em código e, em seguida, adicione o seguinte código para o módulo de formulário.
      Option Explicit
      
      Private Sub Command1_Click()
          Dim frm As COMWinform.COMForm
          Set frm = New COMWinform.COMForm
          frm.ShowForm1
      End Sub
      
    7. No menu arquivo , clique em Make EXE para compilar o projeto.
  4. Execute o arquivo executável de Visual Basic 6.0 compilado.
  5. Clique no botão. O Windows Form da biblioteca de classe que você criou anteriormente será exibido.
  6. Definir o foco em um dos controles TextBox no Windows Form e, em seguida, pressione TAB para mover entre os controles. Observe que a tecla TAB não move foco entre os controles conforme o esperado.
  7. Definir o foco no botão de comando no Windows Form e, em seguida, pressione ENTER. Observe que o botão não responde e o evento Click não é disparado.

Referências

Para obter mais informações, clique nos números abaixo para ler os artigos na Base de dados de Conhecimento da Microsoft:
316422  (http://support.microsoft.com/kb/316422/ ) Mapa para threading no Visual Basic .NET
315577  (http://support.microsoft.com/kb/315577/ ) Como criar threads no Visual Basic .NET

A informação contida neste artigo aplica-se a:
  • Microsoft .NET Framework 1.1
  • Microsoft Visual Basic Enterprise Edition for Windows 6.0
  • Microsoft Foundation Class Library 4.2
Palavras-chave: 
kbmt kbprb kbwindowsforms kbinfo KB839076 KbMtpt
Tradução automáticaTradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine Translation ou MT), não tendo sido portanto traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 839076  (http://support.microsoft.com/kb/839076/en-us/ )