Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo argomento si spiega come avviare e arrestare il thread di elaborazione del processo di stampa.
Panoramica
Per impedire alle attività di stampa di bloccare la risposta dell'interfaccia utente, creare un thread separato per elaborare il processo di stampa. Il thread avviato all'avvio del programma è il thread che gestisce i messaggi della finestra risultanti dall'interazione dell'utente ed è quindi il thread dell'interfaccia utente. Il programma deve elaborare questi messaggi senza ritardo affinché l'interfaccia utente risponda all'input del mouse e della tastiera dell'utente in modo tempestivo. Per consentire al programma di rispondere rapidamente a questi messaggi, la quantità di elaborazione che può essere eseguita durante qualsiasi messaggio è limitata. Quando una richiesta utente richiede un'elaborazione completa, un thread diverso deve eseguire tale elaborazione per consentire la successiva interazione dell'utente da gestire dal programma.
L'elaborazione dei dati in un thread separato richiede al programma di coordinare il funzionamento del thread dell'interfaccia utente e del thread di elaborazione. Ad esempio, mentre il thread di elaborazione elabora i dati, il thread principale non deve modificare tali dati. Un modo per gestirlo consiste nell'usare oggetti di sincronizzazione thread-safe, ad esempio semafori, eventi e mutex.
Allo stesso tempo, è necessario impedire un'interazione dell'utente mentre il thread di elaborazione è in esecuzione. Nel programma di esempio i dati dell'applicazione sono protetti e l'interazione dell'utente è limitata dal fatto che l'elaborazione del processo di stampa venga gestita dalla finestra di dialogo di stato modale. Una finestra di dialogo modale impedisce all'utente di interagire con la finestra principale del programma, impedendo all'utente di modificare i dati del programma dell'applicazione durante la stampa dei dati.
L'unica azione che l'utente può eseguire durante l'elaborazione di un processo di stampa consiste nell'annullare il processo di stampa. Questa restrizione viene segnalata all'utente dalla forma del cursore. Quando il cursore si trova sul pulsante Annulla, viene visualizzato un cursore a forma di freccia, il che indica che l'utente può fare clic su questo pulsante. Quando il cursore si trova su qualsiasi altra parte dell'area della finestra del programma, viene visualizzato un cursore di attesa, che indica che il programma è occupato e non può rispondere all'input dell'utente.
Procedura di Creazione del Thread di Stampa
È consigliabile includere queste funzionalità durante l'elaborazione della stampa.
l'elaborazione di stampa è suddivisa in passaggi
È possibile dividere l'elaborazione di stampa in passaggi di elaborazione discreti che è possibile interrompere se l'utente fa clic sul pulsante annulla . Ciò è utile perché l'elaborazione di stampa può includere operazioni a elevato utilizzo del processore. L'interruzione di questa elaborazione in passaggi può impedire all'elaborazione di stampa di bloccare o ritardare altri thread o processi. L'interruzione dell'elaborazione in passaggi logici consente anche di terminare in modo pulito l'elaborazione di stampa in qualsiasi punto, in modo che la fine di un processo di stampa prima del completamento non lasci le risorse orfane.
Questo è un esempio di elenco dei passaggi di stampa.
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);
Verificare la presenza di un evento di annullamento tra i passaggi
Quando l'utente fa clic sul pulsante Annulla, il thread dell'interfaccia utente segnala l'evento di annullamento. Il thread di elaborazione deve controllare periodicamente l'evento di annullamento per sapere quando un utente ha fatto clic sul pulsante Annulla. Le istruzioni WaitForSingleObject eseguono questo controllo e offrono anche ad altri programmi la possibilità di eseguire in modo che l'elaborazione del processo di stampa non blocchi o ritardi altri thread o processi.
Nell'esempio di codice seguente viene illustrato uno dei test per verificare se si è verificato l'evento di annullamento.
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
Inviare gli aggiornamenti dello stato al thread dell'interfaccia utente
Come ogni passaggio di elaborazione di stampa, il thread di elaborazione di stampa invia messaggi di aggiornamento alla finestra di dialogo di stato di stampa in modo che possa aggiornare la barra di stato. Si noti che la finestra di dialogo di avanzamento stampa è in esecuzione nel thread dell'interfaccia utente.
Nell'esempio di codice seguente viene illustrata una delle chiamate al messaggio di aggiornamento.
// Update print status PostMessage ( threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);
Avvio del thread di stampa
Il thread di elaborazione di stampa viene eseguito fino a quando non viene restituita la funzione PrintThreadProc. I passaggi seguenti avviano il thread di stampa.
Preparare i dati e gli elementi dell'interfaccia utente per la stampa.
Prima di avviare il thread di elaborazione di stampa, è necessario inizializzare gli elementi dati che descrivono il processo di stampa e gli elementi dell'interfaccia utente. Questi elementi includono lo stato del cursore, in modo che il cursore di attesa venga visualizzato in modo appropriato. È inoltre necessario configurare l'indicatore di stato in modo da riflettere le dimensioni del processo di stampa. Questi passaggi di preparazione sono descritti in dettaglio in Procedura: Raccogliere informazioni sul processo di stampa dall'utente.
Nell'esempio di codice seguente viene illustrato come configurare l'indicatore di stato in modo da riflettere le dimensioni del processo di stampa richiesto dall'utente.
// 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));
Avvia il thread di elaborazione di stampa.
Chiamare CreateThread per avviare il thread di elaborazione.
Nell'esempio di codice seguente viene avviato il thread di elaborazione.
// Start the printing thread threadInfo->printThreadHandle = CreateThread ( NULL, 0L, (LPTHREAD_START_ROUTINE)PrintThreadProc, (LPVOID)threadInfo, 0L, &threadInfo->printThreadId);
Verificare se l'elaborazione di stampa non è riuscita all'avvio.
CreateThread restituisce un handle al thread creato se il thread è stato creato correttamente. La funzione PrintThreadProc avviata nel nuovo thread controlla alcune condizioni prima di avviare l'elaborazione effettiva del processo di stampa. Se rileva errori in questi controlli, PrintThreadProc potrebbe restituire senza elaborare alcun dato del processo di stampa. Il thread dell'interfaccia utente può verificare se il thread di elaborazione è stato avviato correttamente attendendo l'handle del thread per un periodo di tempo più lungo del necessario per eseguire i test iniziali, ma non più del necessario. Quando il thread viene terminato, l'handle del thread diventa segnalato. Il codice controlla lo stato del thread per un breve periodo di tempo dopo l'avvio del thread di elaborazione. La funzione WaitForSingleObject restituisce quando si verifica il timeout o quando viene segnalato l'handle del thread. Se la funzione WaitForSingleObject restituisce lo stato WAIT_OBJECT_0, il thread è terminato in anticipo e quindi dovresti chiudere il dialogo di avanzamento della stampa, come illustrato nell'esempio di codice seguente.
// 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); }
Interruzione del thread di stampa
Quando l'utente fa clic sul pulsante Annulla nella finestra di dialogo progresso della stampa, il thread di stampa riceve una notifica in modo che possa interrompere l'elaborazione del processo di stampa in modo ordinato. Mentre il pulsante Annulla e l'evento quitEvent sono aspetti importanti di questa elaborazione, è necessario progettare l'intera sequenza di elaborazione in modo che possa essere interrotta correttamente. Ciò significa che i passaggi della sequenza non devono lasciare risorse allocate che non vengono liberate se un utente annulla la sequenza prima del completamento.
Nell'esempio di codice seguente viene illustrato come il programma di esempio controlla il quitEvent prima di elaborare ogni pagina del documento da stampare. Se il quitEvent viene segnalato o è stato rilevato un errore, l'elaborazione di stampa viene arrestata.
// 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;
}
}