Compartir a través de


Cómo: Iniciar y detener un subproceso de impresión

En este tema se describe cómo iniciar y detener el subproceso de procesamiento de trabajos de impresión.

Información general

Para evitar que las actividades de impresión bloqueen la respuesta de la interfaz de usuario, cree un subproceso independiente para procesar el trabajo de impresión. El subproceso que se inicia cuando se inicia el programa, es el subproceso que controla los mensajes de ventana resultantes de la interacción del usuario y, por tanto, es el subproceso de la interfaz de usuario. El programa debe procesar estos mensajes sin demora para que la interfaz de usuario responda a la entrada del mouse y del teclado del usuario de forma oportuna. Para que el programa pueda responder rápidamente a estos mensajes, la cantidad de procesamiento que se puede realizar durante cualquier mensaje es limitada. Cuando una solicitud de usuario requiere un procesamiento extenso, un subproceso diferente debe realizar ese procesamiento para permitir que el programa controle la interacción del usuario posterior.

El procesamiento de datos en un subproceso independiente requiere que el programa coordine el funcionamiento del subproceso de la interfaz de usuario y el subproceso de procesamiento. Por ejemplo, mientras el subproceso de procesamiento procesa los datos, el subproceso principal no debe modificar esos datos. Una manera de administrar esto es a través de objetos de sincronización seguros para subprocesos, como semáforos, eventos y exclusión mutua.

Al mismo tiempo, se debe evitar alguna interacción del usuario mientras se ejecuta el subproceso de procesamiento. En el programa de ejemplo, los datos de la aplicación están protegidos y la interacción del usuario está limitada al disponer de que el cuadro de diálogo de progreso modal administre el procesamiento del trabajo de impresión. Un cuadro de diálogo modal impide que el usuario interactúe con la ventana principal del programa, lo que impide que el usuario modifique los datos del programa de la aplicación mientras se imprimen los datos.

La única acción que el usuario puede realizar mientras se procesa un trabajo de impresión es cancelar el trabajo de impresión. Esta restricción se señala al usuario mediante la forma del cursor. Cuando el cursor está sobre el botón Cancelar , se muestra un cursor de flecha, que indica que el usuario puede hacer clic en este botón. Cuando el cursor está sobre cualquier otra parte del área de ventana del programa, se muestra un cursor de espera, que indica que el programa está ocupado y no puede responder a la entrada del usuario.

Crear el procedimiento de subproceso de impresión

Se recomienda incluir estas características durante el procesamiento de impresión.

  • El procesamiento de impresión se divide en pasos

    Puede dividir el procesamiento de impresión en pasos de procesamiento discretos que puede interrumpir si el usuario hace clic en el botón Cancelar . Esto es útil porque el procesamiento de impresión puede incluir operaciones que consumen muchos procesadores. Dividir este procesamiento en pasos puede impedir que el procesamiento de impresión bloquee o retrase otros subprocesos o procesos. Dividir el procesamiento en pasos lógicos también permite finalizar limpiamente el procesamiento de impresión en cualquier momento, de modo que finalizar un trabajo de impresión antes de que haya terminado no dejará ningún recurso huérfano.

    Se trata de una lista de ejemplos de pasos de impresión.

    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);
    
  • Buscar un evento de cancelación entre pasos

    Cuando el usuario hace clic en el botón Cancelar , el subproceso de la interfaz de usuario señala el evento cancel. El subproceso de procesamiento debe comprobar periódicamente el evento cancel para saber cuándo un usuario ha hecho clic en el botón Cancelar . Las instrucciones WaitForSingleObject realizan esta comprobación y también ofrecen a otros programas la oportunidad de ejecutarse para que el procesamiento del trabajo de impresión no bloquee ni retrase otros subprocesos o procesos.

    En el ejemplo de código siguiente se muestra una de las pruebas para ver si se ha producido el evento cancel.

    waitStatus = WaitForSingleObject (
                    threadInfo->quitEvent, 
                    stepDelay);
    if (WAIT_OBJECT_0 == waitStatus)
    {
        hr = E_ABORT;
    }
    
  • Envío de actualizaciones de estado al subproceso de la interfaz de usuario

    Como cada paso de procesamiento de impresión, el subproceso de procesamiento de impresión envía mensajes de actualización al cuadro de diálogo progreso de impresión para que pueda actualizar la barra de progreso. Tenga en cuenta que el cuadro de diálogo de progreso de impresión se ejecuta en el subproceso de la interfaz de usuario.

    En el ejemplo de código siguiente se muestra una de las llamadas de mensaje de actualización.

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

Iniciar el subproceso de impresión

El subproceso de procesamiento de impresión se ejecuta hasta que se devuelve la función PrintThreadProc. Los pasos siguientes inician el subproceso de impresión.

  1. Prepare los datos y los elementos de la interfaz de usuario para la impresión.

    Antes de iniciar el subproceso de procesamiento de impresión, debe inicializar los elementos de datos que describen el trabajo de impresión y los elementos de la interfaz de usuario. Estos elementos incluyen el estado del cursor para que el cursor de espera se muestre correctamente. También debe configurar la barra de progreso para reflejar el tamaño del trabajo de impresión. Estos pasos de preparación se describen en detalle en Cómo: Recopilar información del trabajo de impresión del usuario.

    En el ejemplo de código siguiente se muestra cómo configurar la barra de progreso para reflejar el tamaño del trabajo de impresión solicitado por el usuario.

    // 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 el subproceso de procesamiento de impresión.

    Llame a CreateThread para iniciar el subproceso de procesamiento.

    En el ejemplo de código siguiente se inicia el subproceso de procesamiento.

    // Start the printing thread
    threadInfo->printThreadHandle = CreateThread (
                    NULL, 
                    0L, 
                    (LPTHREAD_START_ROUTINE)PrintThreadProc,
                    (LPVOID)threadInfo, 
                    0L, 
                    &threadInfo->printThreadId);
    
  3. Compruebe si se produjo un error en el procesamiento de impresión al iniciarse.

    CreateThread devuelve un identificador al subproceso creado si el subproceso se creó correctamente. La función PrintThreadProc que se inició en el nuevo subproceso comprueba algunas condiciones antes de iniciar el procesamiento real del trabajo de impresión. Si detecta errores en estas comprobaciones, PrintThreadProc podría devolver sin procesar los datos del trabajo de impresión. El subproceso de interfaz de usuario puede comprobar si el subproceso de procesamiento se inició correctamente esperando al identificador de subproceso durante un período de tiempo mayor que el necesario para realizar las pruebas iniciales, pero no más de lo necesario. Cuando se cierra el subproceso, se señala el identificador al subproceso. El código comprueba el estado del subproceso durante un breve período de tiempo después de iniciar el subproceso de procesamiento. La función WaitForSingleObject devuelve cuando se produce el tiempo de espera o se señala el identificador del subproceso. Si la función WaitForSingleObject devuelve un estado de WAIT_OBJECT_0 , el subproceso salió temprano y, por tanto, debe cerrar el cuadro de diálogo de progreso de impresión, como se muestra en el ejemplo de código siguiente.

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

Detener el subproceso de impresión

Cuando el usuario hace clic en el botón Cancelar en el cuadro de diálogo progreso de impresión, se notifica al subproceso de impresión para que pueda detener el procesamiento del trabajo de impresión de manera ordenada. Aunque el botón Cancelar y el evento quitEvent son aspectos importantes de este procesamiento, debe diseñar toda la secuencia de procesamiento para que se interrumpa correctamente. Esto significa que los pasos de la secuencia no deben dejar ningún recurso asignado que no se libere si un usuario cancela la secuencia antes de que se hubiera completado.

En el ejemplo de código siguiente se muestra cómo el programa de ejemplo comprueba el quitEvent antes de procesar cada página del documento que se está imprimiendo. Si se señala el evento quitEvent o se detecta un error, el procesamiento de impresión se detiene.

// 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;
    }
}