Artigo: 196026 - Última revisão: quinta-feira, 22 de Maio de 2003 - Revisão: 4.0

PROBLEMA: Accionar eventos no segundo thread devido a erro de página inválida ou GPF

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

Sintomas

No Visual Basic, um evento é desencadeado por um componente ActiveX a partir de um thread diferente do thread principal por vezes irá causar um erro de protecção geral (GPF) ou um erro de página inválida (erro). Normalmente, parece funcionam correctamente no IDE do Visual Basic mas falha quando é executado como um EXE autónomo.

Causa

Visual Basic utiliza um modelo de threading de apartamento. Chamadas de função entre threads tem de ser organizado. Visual Basic não suporta desencadeados directamente a partir de qualquer thread diferente do thread principal criado por um projecto do Visual Basic sem marshalling de eventos.

Resolução

Método 1

Ilhas o código de activação de eventos:
  1. O thread a ser spun desactivar tem de chamar CoInitialize/CoUnInitialize.
  2. A interface de receptor de que está a ser chamada de retorno no thread tem de ser organizada sobre para esse thread com CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream.

Método 2

Em vez de accionar um evento a partir de um thread secundário, enviar uma mensagem para o thread principal e lançar o evento existe. Um exemplo deste método é fornecido neste artigo.

Ponto Da Situação

Este comportamento ocorre por predefinição.

Mais Informação

Passos para reproduzir o comportamento

O exemplo seguinte cria primeiro um projecto ATL, em seguida, um projecto de Visual Basic 6.0. Em seguida, utiliza estas para demonstrar a forma adequada têm um pedido secundário thread que o thread principal accionado um evento.

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

  1. Crie um novo projecto de AppWizard de COM ATL denominado MyAtlDll e mantenha as predefinições.
  2. Na vista de classe, clique com o botão direito do rato no nome do projecto e seleccione novo objecto ATL. Seleccione o objecto simples na caixa de diálogo apresentada e clique em Seguinte. Escreva no "MyAtl" como o nome abreviado C++. Clique no separador atributos e seleccionar pontos de ligação de suporte. Clique em OK e é adicionado um novo objecto ATL.
  3. Na vista classe, clique com o botão direito do rato a IMyAtl e escolher Adicionar método. Escreva "InitTask" na caixa de texto nome do método e "[no] número longo" na caixa de texto de parâmetros. Clique em OK.
  4. Na vista classe, clique com o botão direito do rato _IMyAtlEvents e seleccione Adicionar método. Seleccione "nulo" da caixa de lista pendente Tipo de retorno. Escreva "TaskFinished" na caixa de texto nome do método e "[no] longo resultado" na caixa de texto de parâmetros. Clique em OK.
  5. Na vista de classe, clique com o botão direito do rato CMyAtl e escolha Adicionar membro variável. Escreva "tempo" na caixa de texto do tipo de variável e "m_number" na caixa de texto nome de variável.
  6. Crie o projecto para gerar a biblioteca de tipo necessária para o passo 10.
  7. Na vista de classe, expanda e faça duplo clique CMyAtl-> IMyAtl-> InitTask. Edite a função InitTask para ser apresentado da seguinte forma:
          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 seguinte código ao ficheiro 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 imediatamente acima da linha "#endif //__MYATL_H_" no ficheiro MyAtl.h:
          DWORD WINAPI justDoIt(LPVOID lpParameter);
    
    					
  10. Na vista de classe, clique com o botão direito do rato CMyAtl e seleccione implementar ponto de ligação. Seleccione _IMyAt eventos na caixa de diálogo apresentada. Clique em OK.
  11. Criar projecto ATL e o controlo será automaticamente registado.

Passos para criar o projecto de Visual Basic 6.0

  1. Crie um novo projecto EXE padrão. É criado o Form1 por predefinição.
  2. Escolha referências no menu Project, seleccione "MyAtlDll 1.0 biblioteca de tipos," e clique em OK.
  3. Adicione um CommandButton ao formulário e manter o nome predefinido (Comando1).
  4. Adicione o seguinte código à janela de 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. Prima a tecla F5 para executar o projecto. Clique em Command1 e irá obter 22222 numa caixa de mensagem.
  6. Crie o projecto para ser um EXE e executar EXE fora de IDE. Obterá uma mensagem de erro quando clica no CommandButton.

    Tenha em atenção que poderá funcionar por vezes, mas não funciona consistentemente. Para corrigir o problema específico neste exemplo, terá de derivar de CMyAtl CWindowImpl e adicionar um mapa de mensagem. Certifique-se que a janela está oculto. Agora, em vez de accionar um evento do thread secundário, pode registar uma mensagem para o thread principal e lançar o evento no processador de mensagens.

Passos para resolver o problema

  1. Utilizando o projecto ATL criou nos passos acima, adicione a seguinte linha:
          #include <atlwin.h>
    
    						
    depois da linha:
          #define _MYATL_H
    
    						
    no ficheiro MyAtl.h.
  2. Adicione a linha:
          #define WM_TASK_FINISH  (WM_USER + 101)
    
    						
    antes da linha:
          class ATL_NO_VTABLE CMyAtl :
    
    						
    no ficheiro MyAtl.h.
  3. Adicionar CWindowImpl a uma das classes principais de CMyAtl adicionando a seguinte linha:
          public CWindowImpl<CMyAtl>,
    
    						
    logo após a linha:
          class ATL_NO_VTABLE CMyAtl :
    
    						
    no ficheiro MyAtl.h.
  4. Adicione o código seguinte à parte do ficheiro MyAtl.h definição 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 ficheiro MyAtl.cpp seja:
          myAtl->PostMessage(WM_TASK_FINISH,result,0);
    
    						
  6. Reconstrua o projecto ATL.
  7. Execute o projecto do Visual Basic do IDE do e como um EXE. Tenha em atenção que o evento é despoletado apenas excelente de qualquer forma.

Referências

Para obter informações adicionais sobre accionar um evento a partir de um segundo thread e sobre o método PostMessage, clique números de artigo que se seguem para visualizar os artigos na base de dados de conhecimento da Microsoft:
157437  (http://support.microsoft.com/kb/157437/EN-US/ ) FICHEIRO: Fireev.exe desencadeia eventos de um segundo thread
280512  (http://support.microsoft.com/kb/280512/EN-US/ ) EXEMPLO: ATLCPImplMT encapsula ATL eventos accionar 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 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: 196026  (http://support.microsoft.com/kb/196026/en-us/ )