ID do artigo: 196026 - Última revisão: quinta-feira, 22 de maio de 2003 - Revisão: 4.0

PROBLEMA: Disparando evento no thread de segundo causa IPF ou falha

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

Sintomas

No Visual Basic, um evento disparado por um componente ActiveX de um thread diferente do thread principal, às vezes, causará uma falha de página inválida (IPF) ou falha de proteção geral (falha). Geralmente ele parece funcionar bem no IDE do Visual Basic, mas falha quando ele é executado como um EXE autônomo.

Causa

Visual Basic usa um modelo de segmentação de apartamento. Chamadas de função entre thread precisará ser empacotada. Visual Basic não oferece suporte eventos acionados diretamente de qualquer segmento diferente o thread principal criado por um projeto Visual Basic sem empacotamento.

Resolução

Método 1

Ilhas o código de acionamento do evento:
  1. O thread que está sendo spun desativar precisa chamar o CoInitialize/CoUnInitialize.
  2. A interface de coletor sendo retorno de chamada no segmento deve ser empacotada sobre para esse segmento com CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream.

Método 2

Em vez de acionar um evento de um thread secundário, postar uma mensagem ao thread principal e aciona o evento não existe. Um exemplo desse método recebe neste artigo.

Situação

Esse comportamento é por design.

Mais Informações

Etapas para reproduzir o problema

O exemplo a seguir cria um projeto ATL, em seguida, um projeto Visual Basic 6.0. Depois ele usa esses para demonstrar o modo adequado com uma solicitação de thread secundário que o thread principal dispara um evento.

Passos para criar um projeto ATL com o Visual C++

  1. Crie um novo ATL COM AppWizard projeto chamado MyAtlDll e manter as configurações padrão.
  2. No modo de exibição de classe, clique com o botão direito do mouse o nome do projeto e selecione novo objeto ATL. Selecione Simple Object na caixa de diálogo exibida e clique em Avançar. Digite no "MyAtl" como o nome curto do C++. Clique na guia atributo e selecione pontos de conexão suporte. Clique em OK e um novo objeto ATL é adicionado.
  3. No modo de exibição de classe, clique o IMyAtl com o botão direito do mouse e escolha Add método. Digite "InitTask" na caixa de texto Nome do método e "[in] número longo" na caixa de texto parâmetros. Clique em OK.
  4. No modo de exibição classe, clique com o botão direito do mouse _IMyAtlEvents e selecione Adicionar método. Selecione "void" na caixa de listagem drop-down Tipo de retorno. Digite "TaskFinished" na caixa de texto Nome do método e "[in] resultado longo" na caixa de texto parâmetros. Clique em OK.
  5. No modo de exibição de classe, clique com o botão direito do mouse CMyAtl e escolha Adicionar variável de membro. Digite "longa" na caixa de texto tipo de variável e "m_number" na caixa de texto Nome da variável.
  6. Crie o projeto para gerar a biblioteca de tipos necessária para a etapa 10.
  7. No modo de exibição de classe, expanda e clique duas vezes CMyAtl-> IMyAtl-> InitTask. Edite a função InitTask para torná-la aparecer da seguinte maneira:
          STDMETHODIMP CMyAtl::InitTask(long number)
          {
             HANDLE hThrd;
             DWORD tid;
    
             m_number = number;
             if((hThrd = CreateThread(
                     0,
                     0,
                     (LPTHREAD_START_ROUTINE)justDoIt,
                     (void *)this,
                     0,
                     &tid)) == NULL)
             {
                //error handling here
             }
             CloseHandle(hThrd);
             return S_OK;
          }
    
    					
  8. Adicione o código a seguir ao arquivo MyAtl.cpp:
          DWORD WINAPI justDoIt(LPVOID lpParameter)
          {
             CMyAtl *myAtl = (CMyAtl*)lpParameter;
    
             long result;
             for (int i = 1; i <= myAtl->m_number; ++i)
                result = i * 2;
    
             myAtl->Fire_TaskFinished(result);
    
             return 0;
          }
    
    					
  9. Adicione o seguinte código logo acima da linha "# endif //__MYATL_H_" no arquivo MyAtl.h:
          DWORD WINAPI justDoIt(LPVOID lpParameter);
    
    					
  10. No modo de exibição de classe, clique com o botão direito do mouse CMyAtl e selecione o ponto de conexão de implementar. Selecione eventos _IMyAt na caixa de diálogo exibida. Clique em OK.
  11. Crie o projeto ATL e o controle será automaticamente registrado.

Passos para criar o projeto do Visual Basic 6.0

  1. Crie um novo projeto Standard EXE. O Form1 é criado por padrão.
  2. Escolha referências no menu Project, selecione "MyAtlDll 1.0 biblioteca de tipos," e clique em OK.
  3. Adicione um CommandButton ao formulário e mantenha o nome padrão (comando_1).
  4. Adicione o seguinte código para a janela código do form1:
          Option Explicit
          Private WithEvents vbATL As MYATLDLLLib.MyAtl
    
          Private Sub Command1_Click()
             vbATL.InitTask 11111
          End Sub
    
          Private Sub Form_Load()
             Set vbATL = New MYATLDLLLib.MyAtl
          End Sub
    
          Private Sub vbATL_TaskFinished(ByVal result As Long)
             MsgBox result
          End Sub
    
    					
  5. Pressione a tecla F5 para executar o projeto. Clique em Command1 e você obterá 22222 em uma caixa de mensagem.
  6. Crie o projeto para ser um EXE e executar o EXE fora do IDE. Você receberá uma mensagem de erro quando você clica no CommandButton.

    Observe que podem funcionar às vezes, mas não funciona consistentemente. Para corrigir o problema nesse exemplo específico, você precisará derivar o CMyAtl CWindowImpl e adicionar um mapa da mensagem. Certifique-se que a janela está oculta. Agora, em vez de acionar um evento de thread secundário, você pode postar uma mensagem para o segmento principal e aciona o evento no manipulador de mensagem.

Etapas para resolver o problema

  1. Usando o projeto ATL criado nas etapas acima, adicione a seguinte linha:
          #include <atlwin.h>
    
    						
    após a linha:
          #define _MYATL_H
    
    						
    no arquivo MyAtl.h.
  2. Adicione a linha:
          #define WM_TASK_FINISH  (WM_USER + 101)
    
    						
    antes da linha:
          class ATL_NO_VTABLE CMyAtl :
    
    						
    no arquivo MyAtl.h.
  3. Adicionar CWindowImpl para uma das classes pai de CMyAtl adicionando a seguinte linha:
          public CWindowImpl<CMyAtl>,
    
    						
    logo após a linha:
          class ATL_NO_VTABLE CMyAtl :
    
    						
    no arquivo MyAtl.h.
  4. Adicione o seguinte código à parte de definição de arquivo MyAtl.h CMyAtl:
          public:
             DECLARE_WND_CLASS("MyAtl")
    
             BEGIN_MSG_MAP(CMyAtl)
                MESSAGE_HANDLER(WM_TASK_FINISH, OnTaskFinished)
             END_MSG_MAP()
          public:
             LRESULT OnTaskFinished(UINT uMsg, WPARAM wParam,
                            LPARAM lParam, BOOL& bHandled)
             {
                Fire_TaskFinished((long)wParam);
                return 0;
             }
    
            HRESULT FinalConstruct()
            {
               RECT rect;
               rect.left=0;
               rect.right=100;
               rect.top=0;
               rect.bottom=100;
    
               HWND hwnd = Create( NULL, rect, "MyAtlWindow", WS_POPUP);
    
               if (hwnd)
                   return S_OK;
               else
                   return HRESULT_FROM_WIN32(GetLastError());
            }
    
            void FinalRelease()
            {
               if (m_hWnd != NULL)
                   DestroyWindow();
            }
    
    						
  5. Altere a seguinte linha:
          myAtl->Fire_TaskFinished(result);
    
    						
    na função
          DWORD WINAPI justDoIt(LPVOID lpParameter)
    
    						
    no arquivo MyAtl.cpp seja:
          myAtl->PostMessage(WM_TASK_FINISH,result,0);
    
    						
  6. Recrie o projeto ATL.
  7. Execute o projeto Visual Basic partir do IDE e como um EXE. Observe que o evento é acionado apenas bem de qualquer forma.

Referências

Para obter informações adicionais sobre disparando um evento de um segundo thread e sobre o método PostMessage, clique nos números abaixo para ler os artigos na Base de dados de Conhecimento da Microsoft:
157437  (http://support.microsoft.com/kb/157437/EN-US/ ) ARQUIVO: Fireev.exe aciona eventos de um segundo thread
280512  (http://support.microsoft.com/kb/280512/EN-US/ ) EXEMPLO: ATLCPImplMT encapsula eventos ATL disparando em COM

A informação contida neste artigo aplica-se a:
  • Microsoft Visual Basic 6.0 Learning Edition
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic Enterprise Edition for Windows 6.0
Palavras-chave: 
kbmt kbactivexevents kbcode kbprb kbthread KB196026 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: 196026  (http://support.microsoft.com/kb/196026/en-us/ )