Artigo: 839076 - Última revisão: sexta-feira, 28 de Outubro de 2005 - Revisão: 3.1

Detectar um comportamento inesperado quando utilizar o Windows Forms COM aplicações de cliente

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 | Reduzir tudo

Sumário

Quando abre uma instância de um formulário de Windows da Microsoft a partir de uma aplicação de cliente COM, tal como uma aplicação do Microsoft Visual Basic 6.0 ou uma aplicação Microsoft Foundation Classes (MFC), o formulário de Windows .NET poderão ter um comportamento inesperado. Por exemplo, quando prime TAB, o foco não muda de um controlo a outro controlo. Ou quando prime ENTER enquanto um botão de comando tiver o foco, o evento Click do botão não é despoletado. Poderá também detectar um comportamento inesperado para batimentos de teclas ou para a actividade do rato.

Estes sintomas ocorrem porque a aplicação anterior não implementa o suporte de ciclo de mensagem de um formulário do .NET Framework Windows necessita para funcionar correctamente.

Este artigo descreve como resolver estes problemas, apresentando o formulário num ciclo de mensagem do .NET Framework que criar utilizando o método Application.Run .

Sintomas

Quando cria uma instância de um formulário de Windows da Microsoft de uma aplicação de cliente COM, o formulário poderá apresentar um comportamento inesperado. Por exemplo, quando cria uma instância do formulário a partir de uma aplicação do Microsoft Visual Basic 6.0 ou a partir de uma aplicação Microsoft Foundation Classes (MFC), o foco não muda de um controlo a outro controlo quando prime TAB. Ou, se premir ENTER enquanto um botão de comando tiver o foco, o evento Click do botão não é despoletado. Poderá também detectar um comportamento inesperado para batimentos de teclas ou para a actividade do rato.

Causa

Este problema ocorre porque o ciclo de mensagem que utiliza o formulário de Windows e o ciclo de mensagem que fornece a aplicação de cliente COM são diferentes.

A Microsoft tomou uma alteração para resolver este problema de estrutura.
Para obter mais informações, clique no número de artigo que se segue para visualizar o artigo na Microsoft Knowledge Base:
885446  (http://support.microsoft.com/kb/885446/ ) CORRECÇÃO: Poderá detectar um comportamento inesperado quando prime TAB ou ENTER no formulário de Windows num computador que esteja a executar o S do .NET Framework 1.1
Este artigo descreve como resolver este problema, apresentando o formulário num ciclo de mensagem do .NET Framework que criar utilizando o método Application.Run .

Resolução

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

Utilize o método ShowDialog para apresentar o formulário do Windows

Esta é a solução mais fácil para apresentar o formulário num ciclo de mensagem do .NET Framework. Para o fazer, substitua as chamadas para o método Mostrar chamadas para o método ShowDialog seu componente .NET Framework.

O método ShowDialog suspende o ciclo de mensagens da aplicação anterior e apresenta o formulário como uma caixa de diálogo. Uma vez que ciclo de mensagem a aplicação inicial foi suspenso, o método ShowDialog cria um novo ciclo de mensagem .NET Framework para processar mensagens do formulário. Esta é a solução mais fácil porque requer o código de mínimo para implementar. No entanto, a desvantagem desta abordagem é que o formulário será aberto de forma modal. Este comportamento bloqueia qualquer interface de utilizador (UI) da aplicação chamada enquanto o formulário do Windows estiver aberto. Quando o utilizador fecha o formulário do Windows, o .NET Framework sai do ciclo de mensagem e mensagem a aplicação anterior repetir currículos em execução.

Apresentar cada formulário do Windows num novo thread

Modificar o componente do .NET Framework para apresentar cada instância de um formulário no seu próprio thread utilizando o respectivo ciclo de mensagem. Não pode ter mais do que um ciclo de mensagem por thread a ser executado. Por conseguinte, não pode alterar ciclo de mensagem a aplicação cliente. No entanto, pode modificar o componente do .NET Framework para iniciar um novo thread que utiliza o respectivo ciclo de mensagem.

Nota Programação multithread é um conceito avançado que requer cuidadosa consideração antes de implementação. Isto é particularmente verdade se vários threads tem de aceder um recurso partilhado. Se vários threads tem aceder um recurso partilhado ao mesmo tempo, certifique-se de que criar os componentes do .NET Framework para segurança de thread.Para obter informações adicionais, clique no número de artigo que se segue para visualizar o artigo na Microsoft Knowledge Base:
316136  (http://support.microsoft.com/kb/316136/ ) Como sincronizar o acesso a um recurso partilhado num ambiente multithreading com Visual Basic .NET
Para criar cada instância de um formulário de Windows num novo thread, siga estes passos:
  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para o fazer, siga estes passos:
    1. Utilize Visual Basic .NET 2003 para criar um novo projecto de biblioteca de classes. Nome do projecto COMWinform.
    2. Elimine o ficheiro de Class1.vb predefinido.
    3. No menu projecto , clique em Adicionar classe .
    4. Seleccione o modelo COM classe .
    5. Na caixa nome , escreva COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do ficheiro 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 seguinte em código que foi inserido quando criou o 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 projecto , clique em Adicionar classe .
    9. Clique em classe .
    10. Na caixa nome , escreva 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 projecto , clique em Adicionar formulário de Windows e, em seguida, clique em Abrir .
    13. Adicione alguns controlos de caixa de texto e um botão de comando ao formulário.
    14. Adicione o seguinte código ao processador de evento clicar o botão de comando.
      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. Este passo também regista o projecto para interoperabilidade COM neste computador.
  3. Crie um ficheiro executável. Para o fazer, siga estes passos:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projecto EXE padrão.
    3. No menu projecto , clique em References .
    4. Adicione uma referência à biblioteca de tipos COMWinform que foi gerada quando é criado a solução do Visual Basic. NET. Se não visualizá-lo na lista, clique em Procurar para localizar o ficheiro de biblioteca de (tipos.tlb) do tipo manualmente.
    5. Adicionar um botão de comando ao formulário.
    6. No menu Ver , clique em código e 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 ficheiro , clique em Criar EXE para compilar o projecto.
  4. Execute o ficheiro executável de Visual Basic 6.0 compilado.
  5. Clique no botão. Aparece o formulário de Windows da biblioteca de classe que criou anteriormente.
  6. Definir o foco dos controlos de caixa de texto no formulário do Windows e, em seguida, prima TAB para se deslocar entre os controlos.
Repare que a tecla TAB funciona com êxito para percorrer os controlos. Para além disso, repare que clique evento o botão de comando é accionado quando prime ENTER.

Criar um ciclo de mensagens partilhada num novo thread no componente do .NET Framework

Este método é semelhante na secção "Mostrar cada forma de Windows num novo thread". No entanto, em vez de apresentar cada formulário no seu próprio thread utilizando o respectivo ciclo de mensagem, este método cria um ciclo de mensagens partilhada compatível com apenas um novo thread no componente do .NET Framework.

Este método com mais precisão representa o comportamento que teria se estivesse a executar uma aplicação Windows Forms padrão. Esta estrutura também facilita a partilhar recursos entre vários formulários, uma vez que todos os formulários são executados no mesmo thread. A solução na secção "Mostrar cada forma de Windows num novo thread" cria um novo thread para cada formulário. Essa solução requer código de sincronização de thread adicionais para partilhar recursos entre diferentes formatos.

Uma vez que este método é mais semelhante de outros métodos ao comportamento de uma aplicação Windows Forms, repare que todos os formulários do .NET Framework que abre a aplicação cliente fechará quando pára o ciclo de mensagem do .NET Framework. Este comportamento ocorre quando o utilizador fecha o formulário é designado como o formulário principal para ApplicationContext . ApplicationContext é utilizado para iniciar o ciclo de mensagem.

No seguinte exemplo de código, a forma principal de ApplicationContext está definida para o primeiro formulário que abre a aplicação cliente. Por conseguinte, quando o utilizador fecha essa instância de formulário, sai de ciclo de mensagem o .NET Framework e todos os formulários do Windows será encerrada.

Para criar um ciclo de mensagens partilhada num novo thread para todos os formulários utilizar, siga estes passos:
  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para o fazer, siga estes passos:
    1. Utilize Visual Basic .NET 2003 para criar um novo projecto de biblioteca de classes com o nome COMWinform.
    2. Elimine o ficheiro de Class1.vb predefinido.
    3. No menu projecto , clique em Adicionar classe .
    4. Seleccione o modelo COM classe .
    5. Na caixa nome , escreva COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do ficheiro 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 seguinte código em código que foi inserido quando criou o 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 projecto , clique em Adicionar classe .
    9. Clique em classe .
    10. Na caixa nome , escreva FormManager.vb e, em seguida, clique em OK .
    11. Substituir o conteúdo do ficheiro 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 projecto , clique em Adicionar formulário de Windows e, em seguida, clique em Abrir .
    13. Adicione alguns controlos de caixa de texto e um botão de comando ao formulário.
    14. Adicione o seguinte código ao processador de evento clicar o botão de comando.
      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. Este passo também regista o projecto para interoperabilidade neste computador.
  3. Crie um ficheiro executável. Para o fazer, siga estes passos:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projecto EXE padrão.
    3. No menu projecto , clique em References .
    4. Adicione uma referência à biblioteca de tipos COMWinform que foi gerada quando é criado a solução do Visual Basic. NET. Se não visualizá-lo na lista, clique em Procurar para localizar o ficheiro de biblioteca de (tipos.tlb) do tipo manualmente.
    5. Adicionar um botão de comando ao formulário.
    6. No menu Ver , clique em código e 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 ficheiro , clique em Criar EXE para compilar o projecto.
  4. Execute o ficheiro executável de Visual Basic 6.0 compilado.
  5. Clique no botão. Aparece o formulário de Windows da biblioteca de classe que criou anteriormente.
  6. Definir o foco dos controlos de caixa de texto no formulário do Windows e, em seguida, prima TAB para se deslocar entre os controlos.
Repare que a tecla TAB funciona com êxito para mover entre os controlos. Para além disso, repare que clique evento o botão de comando é accionado quando prime ENTER.

Ponto Da Situação

Este comportamento ocorre por predefinição.

Mais Informação

Mensagem de uma aplicação ciclo é um ciclo de programa interno que obtém mensagens da fila de mensagem de um thread, converte-os e, em seguida, envia-os para a aplicação a ser processado. O ciclo de mensagem para um formulário do Windows não tem a mesma arquitectura como ciclos de mensagem que aplicações anteriores, tais como aplicações do Visual Basic 6.0 e aplicações de MFC, fornecem. As mensagens de janela que são enviadas para o ciclo de mensagem podem ser processadas diferente do que o formulário de Windows espera. Assim, poderá ocorrer um comportamento inesperado. Algumas combinações de teclas poderão não funcionar, algumas actividades do rato poderão não funcionar ou alguns eventos não podem lançar conforme esperado.

Nota Um ciclo de mensagem do .NET Framework é a arquitectura suportada apenas para apresentar e para utilizar um formulário do Windows. As soluções que são descritas na secção "Resolução" só se aplicam apresentar um formulário do Windows. O contentor de suportadas apenas para hospedar um controlo de .NET Framework através de um wrapper de chamada de COM (CCW) é o Microsoft Internet Explorer. Uma vez que um controlo tem de ser um contentor, esse controlo tem de utilizar o ciclo de mensagem que fornece o contentor. Assim, não poderá utilizar as soluções que são descritas na secção "Resolução" para um controlo de .NET Framework.

Para obter informações adicionais, clique no número de artigo que se segue para visualizar o artigo na Microsoft Knowledge Base:
311334  (http://support.microsoft.com/kb/311334/ ) Contentores de controlo ActiveX que suportem controlos .NET
317346  (http://support.microsoft.com/kb/317346/ ) Controla o nativo versus .NET COM chamada no Internet Explorer

Passos para reproduzir o comportamento

  1. Inicie o Microsoft Visual Studio .NET 2003.
  2. Crie uma biblioteca de classe. Para o fazer, siga estes passos:
    1. Utilize Visual Basic .NET 2003 para criar um novo projecto de biblioteca de classes com o nome COMWinform.
    2. Elimine o ficheiro de Class1.vb predefinido.
    3. No menu projecto , clique em Adicionar classe .
    4. Seleccione o modelo COM classe .
    5. Na caixa nome , escreva COMForm.vb e, em seguida, clique em Abrir .
    6. Cole as seguintes instruções de código na parte superior do ficheiro 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 seguinte código em código que foi inserido quando 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 projecto , clique em Adicionar formulário de Windows e, em seguida, clique em Abrir .
    9. Adicione alguns controlos de caixa de texto e um botão de comando ao formulário.
    10. Adicione o seguinte código ao processador de evento clicar o botão de comando.
      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. Este passo também regista o projecto para interoperabilidade neste computador.
  3. Crie um ficheiro executável. Para o fazer, siga estes passos:
    1. Inicie o Microsoft Visual Basic 6.0.
    2. Crie um novo projecto EXE padrão.
    3. No menu projecto , clique em References .
    4. Adicione uma referência à biblioteca de tipos COMWinform que foi gerada quando é criado a solução do Visual Basic. NET. Se não visualizá-lo na lista, clique em Procurar para localizar o ficheiro de biblioteca de (tipos.tlb) do tipo manualmente.
    5. Adicionar um botão de comando ao formulário.
    6. No menu Ver , clique em código e 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 ficheiro , clique em Criar EXE para compilar o projecto.
  4. Execute o ficheiro executável de Visual Basic 6.0 compilado.
  5. Clique no botão. Aparece o formulário de Windows da biblioteca de classe que criou anteriormente.
  6. Definir o foco dos controlos de caixa de texto no formulário do Windows e, em seguida, prima TAB para se deslocar entre os controlos. Repare que a tecla TAB não move foco entre os controlos como previsto.
  7. Definir o foco no botão de comando no formulário Windows e, em seguida, prima ENTER. Repare que o botão não está a responder e o evento clicar não é despoletado.

Referências

Para obter mais informações, clique números de artigo que se seguem para visualizar os artigos na base de dados de conhecimento da Microsoft:
316422  (http://support.microsoft.com/kb/316422/ ) Informações gerais para threads no Visual Basic .NET
315577  (http://support.microsoft.com/kb/315577/ ) A criação de 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 revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes 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/ )