Vorgehensweise: Starten und Beenden eines Druckthreads
In diesem Thema wird beschrieben, wie Sie den Druckauftragsverarbeitungsthread starten und beenden.
Übersicht
Um zu verhindern, dass Druckaktivitäten die Antwort der Benutzeroberfläche blockieren, erstellen Sie einen separaten Thread, um den Druckauftrag zu verarbeiten. Der Thread, der beim Starten des Programms gestartet wird, ist der Thread, der die Fenstermeldungen verarbeitet, die sich aus der Benutzerinteraktion ergeben, und ist daher der UI-Thread. Das Programm muss diese Nachrichten ohne Verzögerung verarbeiten, damit die Benutzeroberfläche rechtzeitig auf die Maus- und Tastatureingaben des Benutzers reagieren kann. Damit das Programm schnell auf diese Nachrichten reagieren kann, ist der Umfang der Verarbeitung, die während einer einzelnen Nachricht ausgeführt werden kann, begrenzt. Wenn eine Benutzeranforderung eine umfangreiche Verarbeitung erfordert, muss diese Verarbeitung von einem anderen Thread ausgeführt werden, damit nachfolgende Benutzerinteraktionen vom Programm verarbeitet werden können.
Für die Verarbeitung von Daten in einem separaten Thread muss das Programm den Betrieb des Benutzeroberflächenthreads und des Verarbeitungsthreads koordinieren. Während der Verarbeitungsthread beispielsweise die Daten verarbeitet, darf der Standard Thread diese Daten nicht ändern. Eine Möglichkeit, dies zu verwalten, sind threadsichere Synchronisierungsobjekte wie Semaphore, Ereignisse und Mutexe.
Gleichzeitig muss eine Benutzerinteraktion verhindert werden, während der Verarbeitungsthread ausgeführt wird. Im Beispielprogramm sind die Anwendungsdaten geschützt, und die Benutzerinteraktion wird eingeschränkt, indem die Druckauftragsverarbeitung über das Dialogfeld "Modal progress" verwaltet wird. Ein modales Dialogfeld verhindert, dass der Benutzer mit dem Standard Fenster des Programms interagiert, was verhindert, dass der Benutzer die Anwendungsprogrammdaten ändert, während die Daten gedruckt werden.
Die einzige Aktion, die der Benutzer während der Verarbeitung eines Druckauftrags ausführen kann, ist das Abbrechen des Druckauftrags. Diese Einschränkung wird dem Benutzer durch das Cursor-Shape signalisiert. Wenn sich der Cursor über der Schaltfläche Abbrechen befindet, wird ein Pfeilcursor angezeigt, der angibt, dass der Benutzer auf diese Schaltfläche klicken kann. Wenn sich der Cursor über einem anderen Teil des Fensterbereichs des Programms befindet, wird ein Wartecursor angezeigt, der angibt, dass das Programm ausgelastet ist und nicht auf Benutzereingaben reagieren kann.
Erstellen der Druckthreadprozedur
Es wird empfohlen, diese Features während der Druckverarbeitung zu verwenden.
Die Druckverarbeitung ist in Schritte unterteilt
Sie können die Druckverarbeitung in diskrete Verarbeitungsschritte unterteilen, die Sie unterbrechen können, wenn der Benutzer auf die Schaltfläche Abbrechen klickt. Dies ist nützlich, da die Druckverarbeitung prozessorintensive Vorgänge umfassen kann. Die Aufteilung dieser Verarbeitung in Schritte kann verhindern, dass die Druckverarbeitung andere Threads oder Prozesse blockiert oder verzögert. Die Aufteilung der Verarbeitung in logische Schritte ermöglicht es auch, die Druckverarbeitung jederzeit sauber zu beenden, sodass das Beenden eines Druckauftrags vor Abschluss keine verwaisten Ressourcen zurückblischt.
Dies ist eine Beispielliste mit Druckschritten.
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);
Suchen nach einem Abbruchereignis zwischen den Schritten
Wenn der Benutzer auf die Schaltfläche Abbrechen klickt, signalisiert der Benutzeroberflächethread das Abbrechen-Ereignis. Der Verarbeitungsthread muss das Abbruchereignis regelmäßig überprüfen, um zu erfahren, wann ein Benutzer auf die Schaltfläche Abbrechen geklickt hat. Die WaitForSingleObject-Anweisungen führen diese Überprüfung durch und geben auch anderen Programmen die Möglichkeit, auszuführen, damit die Druckauftragsverarbeitung andere Threads oder Prozesse nicht blockiert oder verzögert.
Im folgenden Codebeispiel wird einer der Tests veranschaulicht, um festzustellen, ob das Abbruchereignis aufgetreten ist.
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
Senden status Updates an den Benutzeroberflächenthread
Wie bei jedem Druckverarbeitungsschritt sendet der Druckverarbeitungsthread Aktualisierungsmeldungen an das Dialogfeld Druckstatus, sodass die Statusleiste aktualisiert werden kann. Beachten Sie, dass das Dialogfeld Druckstatus im UI-Thread ausgeführt wird.
Im folgenden Codebeispiel wird einer der Aufrufe der Updatemeldung veranschaulicht.
// Update print status PostMessage ( threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);
Starten des Druckthreads
Der Druckverarbeitungsthread wird so lange ausgeführt, bis die PrintThreadProc-Funktion zurückgegeben wird. Mit den folgenden Schritten wird der Druckthread gestartet.
Bereiten Sie die Daten- und Benutzeroberflächenelemente für den Druck vor.
Bevor Sie den Druckverarbeitungsthread starten, müssen Sie die Datenelemente initialisieren, die den Druckauftrag und die Elemente der Benutzeroberfläche beschreiben. Zu diesen Elementen gehört der Cursorzustand, sodass der Wartecursor entsprechend angezeigt wird. Außerdem müssen Sie die Statusanzeige so konfigurieren, dass sie die Größe des Druckauftrags widerspiegelt. Diese Vorbereitungsschritte werden ausführlich unter Vorgehensweise: Sammeln von Druckauftragsinformationen vom Benutzer beschrieben.
Das folgende Codebeispiel zeigt, wie Sie die Statusleiste so konfigurieren, dass sie die Größe des Druckauftrags widerspiegelt, den der Benutzer angefordert hat.
// 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));
Starten Sie den Druckverarbeitungsthread.
Rufen Sie CreateThread auf, um den Verarbeitungsthread zu starten.
Im folgenden Codebeispiel wird der Verarbeitungsthread gestartet.
// Start the printing thread threadInfo->printThreadHandle = CreateThread ( NULL, 0L, (LPTHREAD_START_ROUTINE)PrintThreadProc, (LPVOID)threadInfo, 0L, &threadInfo->printThreadId);
Überprüfen Sie, ob beim Start ein Fehler bei der Druckverarbeitung aufgetreten ist.
CreateThread gibt ein Handle an den erstellten Thread zurück, wenn der Thread erfolgreich erstellt wurde. Die PrintThreadProc-Funktion, die im neuen Thread gestartet wurde, überprüft einige Bedingungen, bevor sie mit der eigentlichen Druckauftragsverarbeitung beginnt. Wenn fehler bei diesen Überprüfungen erkannt werden, kann PrintThreadProc zurückgeben, ohne Druckauftragsdaten zu verarbeiten. Der UI-Thread kann überprüfen, ob der Verarbeitungsthread erfolgreich gestartet wurde, indem er für einen Zeitraum auf das Threadhandle wartet, der länger ist als für die Durchführung der anfänglichen Tests, aber nicht länger als erforderlich. Wenn der Thread beendet wird, wird der Handle für den Thread signalisiert. Der Code überprüft den Zustand des Threads für einen kurzen Zeitraum, nachdem er den Verarbeitungsthread gestartet hat. Die WaitForSingleObject-Funktion gibt zurück, wenn entweder das Timeout auftritt oder das Threadhandle signalisiert wird. Wenn die WaitForSingleObject-Funktion einen WAIT_OBJECT_0 status zurückgibt, wurde der Thread vorzeitig beendet. Daher sollten Sie den Druckstatusdialog schließen, wie im folgenden Codebeispiel gezeigt.
// 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); }
Beenden des Druckthreads
Wenn der Benutzer im Dialogfeld Druckstatus auf die Schaltfläche Abbrechen klickt, wird der Druckthread benachrichtigt, sodass er die Verarbeitung des Druckauftrags in geordneter Weise beenden kann. Während die Schaltfläche Abbrechen und das quitEvent-Ereignis wichtige Aspekte dieser Verarbeitung sind, müssen Sie die gesamte Verarbeitungssequenz so entwerfen, dass sie erfolgreich unterbrochen wird. Dies bedeutet, dass die Schritte in der Sequenz keine zugeordneten Ressourcen verlassen dürfen, die nicht freigegeben werden, wenn ein Benutzer die Sequenz abbricht, bevor sie abgeschlossen wurde.
Das folgende Codebeispiel zeigt, wie das Beispielprogramm das quitEvent überprüft, bevor es jede Seite im gedruckten Dokument verarbeitet. Wenn das quitEvent signalisiert oder ein Fehler erkannt wurde, wird die Druckverarbeitung beendet.
// 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;
}
}