Compartilhar via


Como iniciar e parar um thread de impressão

Este tópico descreve como iniciar e parar o thread de processamento do trabalho de impressão.

Visão geral

Para impedir que as atividades de impressão bloqueiem a resposta da interface do usuário, crie um thread separado para processar o trabalho de impressão. O thread iniciado quando o programa é iniciado é o thread que manipula as mensagens de janela resultantes da interação do usuário e, portanto, é o thread da interface do usuário. O programa deve processar essas mensagens sem demora para que a interface do usuário responda à entrada do mouse e do teclado do usuário em tempo hábil. Para que o programa possa responder a essas mensagens rapidamente, a quantidade de processamento que pode ser executada durante qualquer mensagem é limitada. Quando uma solicitação de usuário requer processamento extensivo, um thread diferente deve executar esse processamento para permitir que a interação subsequente do usuário seja tratada pelo programa.

O processamento de dados em um thread separado requer que o programa coordene a operação do thread da interface do usuário e do thread de processamento. Por exemplo, enquanto o thread de processamento está processando os dados, o thread main não deve alterar esses dados. Uma maneira de gerenciar isso é por meio de objetos de sincronização thread-safe, como semáforos, eventos e mutexes.

Ao mesmo tempo, alguma interação do usuário deve ser impedida enquanto o thread de processamento está em execução. No programa de exemplo, os dados do aplicativo são protegidos e a interação do usuário é limitada, fazendo com que o processamento do trabalho de impressão seja gerenciado pela caixa de diálogo progresso modal. Uma caixa de diálogo modal impede que o usuário interaja com a janela main do programa, o que impede que o usuário altere os dados do programa de aplicativo enquanto os dados são impressos.

A única ação que o usuário pode executar enquanto um trabalho de impressão está sendo processado é cancelar o trabalho de impressão. Essa restrição é sinalizada para o usuário pela forma do cursor. Quando o cursor está sobre o botão Cancelar , um cursor de seta é exibido, o que indica que o usuário pode clicar neste botão. Quando o cursor está sobre qualquer outra parte da área da janela do programa, um cursor de espera é exibido, o que indica que o programa está ocupado e não pode responder à entrada do usuário.

Criando o procedimento de thread de impressão

Recomendamos incluir esses recursos durante o processamento de impressão.

  • O processamento de impressão é dividido em etapas

    Você pode dividir o processamento de impressão em etapas de processamento discretas que podem ser interrompidas se o usuário clicar no botão Cancelar . Isso é útil porque o processamento de impressão pode incluir operações com uso intensivo de processador. Dividir esse processamento em etapas pode impedir o processamento de impressão de bloquear ou atrasar outros threads ou processos. Dividir o processamento em etapas lógicas também possibilita encerrar o processamento de impressão de forma limpa a qualquer momento, de modo que terminar um trabalho de impressão antes de terminar não deixará nenhum recurso órfão.

    Esta é uma lista de exemplo de etapas de impressão.

    HRESULT PrintStep_1_StartJob (PPRINTTHREADINFO threadInfo);
    HRESULT PrintStep_2_DoOnePackage (PPRINTTHREADINFO threadInfo);
    HRESULT PrintStep_3_DoOneDoc (PPRINTTHREADINFO threadInfo);
    HRESULT PrintStep_4_DoOnePage (PPRINTTHREADINFO threadInfo);
    HRESULT PrintStep_5_ClosePackage (PPRINTTHREADINFO threadInfo);
    HRESULT PrintStep_6_CloseJob (PPRINTTHREADINFO threadInfo);
    
  • Verificar se há um evento de cancelamento entre as etapas

    Quando o usuário clica no botão Cancelar , o thread da interface do usuário sinaliza o evento de cancelamento. O thread de processamento deve marcar o evento cancel periodicamente para saber quando um usuário clicou no botão Cancelar. As instruções WaitForSingleObject executam esse marcar e também dão a outros programas a chance de serem executados para que o processamento do trabalho de impressão não bloqueie ou atrase outros threads ou processos.

    O exemplo de código a seguir ilustra um dos testes para ver se o evento de cancelamento ocorreu.

    waitStatus = WaitForSingleObject (
                    threadInfo->quitEvent, 
                    stepDelay);
    if (WAIT_OBJECT_0 == waitStatus)
    {
        hr = E_ABORT;
    }
    
  • Enviar atualizações de status para o thread da interface do usuário

    Como cada etapa de processamento de impressão, o thread de processamento de impressão envia mensagens de atualização para a caixa de diálogo de progresso da impressão para que possa atualizar a barra de progresso. Observe que a caixa de diálogo progresso da impressão está em execução no thread da interface do usuário.

    O exemplo de código a seguir ilustra uma das chamadas de mensagem de atualização.

    // Update print status
    PostMessage (
        threadInfo->parentWindow, 
        USER_PRINT_STATUS_UPDATE, 
        0L, 
        0L);
    

Iniciando o Thread de Impressão

O thread de processamento de impressão é executado até que a função PrintThreadProc retorne. As etapas a seguir iniciam o thread de impressão.

  1. Prepare os dados e os elementos da interface do usuário para impressão.

    Antes de iniciar o thread de processamento de impressão, você deve inicializar os elementos de dados que descrevem o trabalho de impressão e os elementos da interface do usuário. Esses elementos incluem o estado do cursor, para que o cursor de espera seja exibido adequadamente. Você também deve configurar a barra de progresso para refletir o tamanho do trabalho de impressão. Estas etapas de preparação são descritas em detalhes em Como coletar informações de trabalho de impressão do usuário.

    O exemplo de código a seguir mostra como configurar a barra de progresso para refletir o tamanho do trabalho de impressão solicitado pelo usuário.

    // Compute the number of steps in this job.
    stepCount = (((
                // One copy of a document contains
                //  one step for each page +
                //  one step for the document
                ((threadInfo->documentContent)->pages + 1) * 
                // Each copy of the document includes
                //  two steps to open and close the document
                threadInfo->copies) + 2) * 
                // Each job includes one step to start the job
                threadInfo->packages) + 1;
    // Send the total number of steps to the progress bar control.
    SendMessage (
        dlgInfo->progressBarWindow, 
        PBM_SETRANGE32, 
        0L, 
        (stepCount));
    
  2. Inicie o thread de processamento de impressão.

    Chame CreateThread para iniciar o thread de processamento.

    O exemplo de código a seguir inicia o thread de processamento.

    // Start the printing thread
    threadInfo->printThreadHandle = CreateThread (
                    NULL, 
                    0L, 
                    (LPTHREAD_START_ROUTINE)PrintThreadProc,
                    (LPVOID)threadInfo, 
                    0L, 
                    &threadInfo->printThreadId);
    
  3. Verifique se o processamento de impressão falhou ao iniciar.

    CreateThread retornará um identificador para o thread criado se o thread tiver sido criado com êxito. A função PrintThreadProc iniciada no novo thread verifica algumas condições antes de iniciar o processamento real do trabalho de impressão. Se detectar erros nessas verificações, PrintThreadProc poderá retornar sem processar dados de trabalho de impressão. O thread da interface do usuário pode marcar se o thread de processamento foi iniciado com êxito aguardando o identificador de thread por um período maior do que o necessário para executar os testes iniciais, mas não mais do que o necessário. Quando o thread é encerrado, o identificador para o thread fica sinalizado. O código verifica o estado do thread por um curto período de tempo depois de iniciar o thread de processamento. A função WaitForSingleObject retorna quando o tempo limite ocorre ou o identificador de thread é sinalizado. Se a função WaitForSingleObject retornar um WAIT_OBJECT_0 status, o thread saiu mais cedo e, portanto, você deve fechar a caixa de diálogo de progresso da impressão, como mostra o exemplo de código a seguir.

    // Make sure the printing thread started OK
    //  by waiting to see if it is still running after
    //  a short period of time. This time delay should be
    //  long enough to know that it's running but shorter
    //  than the shortest possible print job.
    waitStatus = WaitForSingleObject (
        threadInfo->printThreadHandle, 
        THREAD_START_WAIT);
    
    // If the object is signaled, that means that the
    //  thread terminated before the timeout period elapsed.
    if (WAIT_OBJECT_0 == waitStatus)
    {
        // The thread exited, so post close messages.
        PostMessage (hDlg, USER_PRINT_CLOSING, 0L, (LPARAM)E_FAIL);
        PostMessage (hDlg, USER_PRINT_COMPLETE, 0L, (LPARAM)E_FAIL);
    }
    

Parando o thread de impressão

Quando o usuário clica no botão Cancelar na caixa de diálogo de progresso da impressão, o thread de impressão é notificado para que ele possa parar de processar o trabalho de impressão de maneira ordenada. Embora o botão Cancelar e o evento quitEvent sejam aspectos importantes desse processamento, você deve projetar toda a sequência de processamento para ser interrompida com êxito. Isso significa que as etapas na sequência não devem deixar nenhum recurso alocado que não seja liberado se um usuário cancelar a sequência antes de ser concluída.

O exemplo de código a seguir mostra como o programa de exemplo verifica o quitEvent antes de processar cada página no documento que está sendo impresso. Se o quitEvent for sinalizado ou um erro tiver sido detectado, o processamento de impressão será interrompido.

// While no errors and the user hasn't clicked cancel...
while (((waitStatus = WaitForSingleObject (
                        threadInfo->quitEvent, 
                        stepDelay)) == WAIT_TIMEOUT) &&
        SUCCEEDED(hr))
{
    // ...print one page
    hr = PrintStep_4_DoOnePage (threadInfo);

    // Update print status
    PostMessage (
        threadInfo->parentWindow, 
        USER_PRINT_STATUS_UPDATE, 
        0L, 
        0L);
    

    if (threadInfo->currentPage < (threadInfo->documentContent)->pages)
    {
        // More pages, so continue to the next one
        threadInfo->currentPage++;
    }
    else
    {
        // Last page printed so exit loop and close
        break;
    }
}