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.
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));
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);
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;
}
}