Guide pratique pour démarrer et arrêter un thread d’impression
Cette rubrique explique comment démarrer et arrêter le thread de traitement du travail d’impression.
Vue d’ensemble
Pour empêcher les activités d’impression de bloquer la réponse de l’interface utilisateur, créez un thread distinct pour traiter le travail d’impression. Le thread démarré au démarrage du programme est le thread qui gère les messages de fenêtre résultant de l’interaction de l’utilisateur, et est donc le thread d’interface utilisateur. Le programme doit traiter ces messages sans délai pour que l’interface utilisateur réponde à l’entrée de la souris et du clavier de l’utilisateur en temps opportun. Pour que le programme puisse répondre rapidement à ces messages, la quantité de traitement qui peut être effectuée au cours d’un message est limitée. Lorsqu’une requête utilisateur nécessite un traitement étendu, un thread différent doit effectuer ce traitement pour permettre au programme de gérer l’interaction utilisateur suivante.
Le traitement des données dans un thread distinct nécessite que le programme coordonne l’opération du thread d’interface utilisateur et du thread de traitement. Par exemple, pendant que le thread de traitement traite les données, le thread main ne doit pas modifier ces données. Une façon de gérer cela consiste à utiliser des objets de synchronisation thread-safe tels que des sémaphores, des événements et des mutex.
En même temps, certaines interactions utilisateur doivent être empêchées pendant l’exécution du thread de traitement. Dans l’exemple de programme, les données d’application sont protégées et l’interaction utilisateur est limitée en faisant en sorte que le traitement du travail d’impression soit géré par la boîte de dialogue de progression modale. Une boîte de dialogue modale empêche l’utilisateur d’interagir avec la fenêtre main du programme, ce qui empêche l’utilisateur de modifier les données du programme d’application pendant l’impression des données.
La seule action que l’utilisateur peut effectuer pendant le traitement d’un travail d’impression consiste à annuler le travail d’impression. Cette restriction est signalée à l’utilisateur par la forme du curseur. Lorsque le curseur se trouve sur le bouton Annuler , un curseur de flèche s’affiche, ce qui indique que l’utilisateur peut cliquer sur ce bouton. Lorsque le curseur se trouve au-dessus d’une autre partie de la zone de fenêtre du programme, un curseur d’attente s’affiche, ce qui indique que le programme est occupé et ne peut pas répondre aux entrées de l’utilisateur.
Création de la procédure de thread d’impression
Nous vous recommandons d’inclure ces fonctionnalités lors du traitement de l’impression.
Le traitement de l’impression est divisé en étapes
Vous pouvez diviser le traitement de l’impression en étapes de traitement discrètes que vous pouvez interrompre si l’utilisateur clique sur le bouton Annuler . Cela est utile, car le traitement de l’impression peut inclure des opérations gourmandes en processeur. Le fractionnement de ce traitement en étapes peut empêcher le traitement d’impression de bloquer ou de retarder d’autres threads ou processus. Fractionner le traitement en étapes logiques permet également de terminer proprement le traitement d’impression à tout moment, de sorte que la fin d’un travail d’impression avant sa fin ne laisse aucune ressource orpheline.
Voici un exemple de liste d’étapes d’impression.
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);
Rechercher un événement d’annulation entre les étapes
Lorsque l’utilisateur clique sur le bouton Annuler , le thread d’interface utilisateur signale l’événement d’annulation. Le thread de traitement doit case activée régulièrement l’événement d’annulation pour savoir quand un utilisateur a cliqué sur le bouton Annuler. Les instructions WaitForSingleObject exécutent cette case activée et donnent également à d’autres programmes la possibilité de s’exécuter afin que le traitement du travail d’impression ne bloque pas ou ne retarde pas d’autres threads ou processus.
L’exemple de code suivant illustre l’un des tests pour voir si l’événement d’annulation s’est produit.
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
Envoyer status mises à jour au thread d’interface utilisateur
À chaque étape de traitement d’impression, le thread de traitement d’impression envoie des messages de mise à jour à la boîte de dialogue progression de l’impression afin qu’il puisse mettre à jour la barre de progression. Notez que la boîte de dialogue Progression de l’impression est en cours d’exécution dans le thread d’interface utilisateur.
L’exemple de code suivant illustre l’un des appels de message de mise à jour.
// Update print status PostMessage ( threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);
Démarrage du thread d’impression
Le thread de traitement d’impression s’exécute jusqu’à ce que la fonction PrintThreadProc retourne. Les étapes suivantes démarrent le thread d’impression.
Préparez les données et les éléments d’interface utilisateur pour l’impression.
Avant de démarrer le thread de traitement d’impression, vous devez initialiser les éléments de données qui décrivent le travail d’impression et les éléments de l’interface utilisateur. Ces éléments incluent l’état du curseur, de sorte que le curseur d’attente s’affiche correctement. Vous devez également configurer la barre de progression pour refléter la taille du travail d’impression. Ces étapes de préparation sont décrites en détail dans How To: Collect Print Job Information from the User.
L’exemple de code suivant montre comment configurer la barre de progression pour refléter la taille du travail d’impression demandé par l’utilisateur.
// 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));
Démarrez le thread de traitement d’impression.
Appelez CreateThread pour démarrer le thread de traitement.
L’exemple de code suivant démarre le thread de traitement.
// Start the printing thread threadInfo->printThreadHandle = CreateThread ( NULL, 0L, (LPTHREAD_START_ROUTINE)PrintThreadProc, (LPVOID)threadInfo, 0L, &threadInfo->printThreadId);
Vérifiez si le traitement de l’impression a échoué au démarrage.
CreateThread retourne un handle au thread créé si le thread a été créé avec succès. La fonction PrintThreadProc qui a été démarrée dans le nouveau thread vérifie certaines conditions avant de démarrer le traitement réel du travail d’impression. S’il détecte des erreurs dans ces vérifications, PrintThreadProc peut retourner sans traiter les données de travail d’impression. Le thread d’interface utilisateur peut case activée si le thread de traitement a démarré correctement en attendant le handle de thread pendant une période plus longue que nécessaire pour effectuer les tests initiaux, mais pas plus que nécessaire. Lorsque le thread se ferme, le handle du thread est signalé. Le code vérifie l’état du thread pendant une courte période après le démarrage du thread de traitement. La fonction WaitForSingleObject retourne lorsque le délai d’expiration se produit ou lorsque le handle de thread est signalé. Si la fonction WaitForSingleObject retourne une WAIT_OBJECT_0 status, le thread s’est arrêté tôt et vous devez donc fermer la boîte de dialogue progression de l’impression, comme le montre l’exemple de code suivant.
// 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); }
Arrêt du thread d’impression
Lorsque l’utilisateur clique sur le bouton Annuler dans la boîte de dialogue progression de l’impression, le thread d’impression est averti afin qu’il puisse arrêter de traiter le travail d’impression de manière ordonnée. Bien que le bouton Annuler et l’événement quitEvent soient des aspects importants de ce traitement, vous devez concevoir l’intégralité de la séquence de traitement pour qu’elle soit interrompue avec succès. Cela signifie que les étapes de la séquence ne doivent pas laisser de ressources allouées qui ne sont pas libérées si un utilisateur annule la séquence avant qu’elle ne soit terminée.
L’exemple de code suivant montre comment l’exemple de programme vérifie l’événement quitEvent avant de traitement de chaque page du document en cours d’impression. Si l’événement quitEvent est signalé ou qu’une erreur a été détectée, le traitement de l’impression s’arrête.
// 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;
}
}