Instrukcje: uruchamianie i zatrzymywanie wątku drukowania
W tym temacie opisano sposób uruchamiania i zatrzymywania wątku przetwarzania zadań drukowania.
Przegląd
Aby zapobiec blokowaniu odpowiedzi interfejsu użytkownika przez działania drukowania, utwórz oddzielny wątek, aby przetworzyć zadanie drukowania. Wątek, który jest uruchamiany po uruchomieniu programu, to wątek, który obsługuje komunikaty okna wynikające z interakcji użytkownika i dlatego jest wątkiem interfejsu użytkownika. Program musi przetwarzać te komunikaty bez opóźnień, aby interfejs użytkownika reagował na dane wejściowe myszy i klawiatury użytkownika w odpowiednim czasie. Aby program mógł szybko reagować na te komunikaty, ilość przetwarzania, którą można wykonać podczas jednego komunikatu, jest ograniczona. Gdy żądanie użytkownika wymaga obszernego przetwarzania, inny wątek musi wykonać to przetwarzanie, aby umożliwić obsługę kolejnych interakcji użytkownika przez program.
Przetwarzanie danych w osobnym wątku wymaga, aby program koordynował działanie wątku interfejsu użytkownika i wątku przetwarzania. Na przykład, gdy wątek przetwarzania przetwarza dane, główny wątek nie może zmieniać tych danych. Jednym ze sposobów zarządzania tym jest korzystanie z bezpiecznych wątkowo obiektów synchronizacji, takich jak semafory, zdarzenia i muteksy.
Jednocześnie niektóre interakcje użytkownika muszą być blokowane podczas uruchamiania wątku przetwarzania. W przykładowym programie dane aplikacji są chronione, a interakcja użytkownika jest ograniczona poprzez zarządzanie przetwarzaniem zadań drukowania przez modalne okno dialogowe postępu. Modalne okno dialogowe uniemożliwia użytkownikowi interakcję z głównym oknem programu, co uniemożliwia użytkownikowi zmianę danych programu podczas drukowania danych programu.
Jedyną akcją, która użytkownik może wykonać podczas przetwarzania zadania drukowania, jest anulowanie zadania drukowania. To ograniczenie jest sygnalizowane użytkownikowi przez kształt kursora. Gdy kursor znajduje się nad przyciskiem Anuluj, zostanie wyświetlony kursor strzałki wskazujący, że użytkownik może kliknąć ten przycisk. Gdy kursor znajduje się nad inną częścią obszaru okna programu, zostanie wyświetlony kursor oczekiwania, który wskazuje, że program jest zajęty i nie może odpowiedzieć na dane wejściowe użytkownika.
Tworzenie procedury wątku drukowania
Zalecamy uwzględnienie tych funkcji podczas przetwarzania wydruku.
przetwarzanie wydruku jest podzielone na kroki
Przetwarzanie wydruku można podzielić na dyskretne kroki przetwarzania, które można przerwać, jeśli użytkownik kliknie przycisk Anuluj. Jest to przydatne, ponieważ przetwarzanie wydruku może obejmować operacje intensywnie korzystające z procesora. Podzielenie tego przetwarzania na kroki może zapobiec blokowaniu lub opóźnianiu procesu drukowania, co mogłoby wpływać na inne wątki lub procesy. Podzielenie przetwarzania na kroki logiczne umożliwia również czyste zakończenie procesu drukowania w dowolnym momencie, dzięki czemu zakończenie zadania drukowania przed zakończeniem nie spowoduje pozostawienia żadnych porzuconych zasobów.
Jest to przykładowa lista kroków drukowania.
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);
Sprawdź zdarzenie anulowania między krokami
Gdy użytkownik kliknie przycisk Anuluj, wątek interfejsu użytkownika sygnalizuje zdarzenie anulowania. Wątek przetwarzania musi okresowo sprawdzać zdarzenie anulowania, aby wiedzieć, kiedy użytkownik kliknął przycisk Anuluj. Instrukcje WaitForSingleObject wykonują tę kontrolę, a także dają innym programom szansę uruchomienia, tak aby przetwarzanie zadania drukowania nie blokowało ani nie opóźniało innych wątków ani procesów.
Poniższy przykład kodu ilustruje jeden z testów, aby sprawdzić, czy zdarzenie anulowania wystąpiło.
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
Wysyłanie aktualizacji stanu do wątku interfejsu użytkownika
W każdym kroku przetwarzania wydruku wątek przetwarzania wydruku wysyła komunikaty aktualizacji do okna dialogowego postępu drukowania, aby można było zaktualizować pasek postępu. Zwróć uwagę, że w wątku interfejsu użytkownika jest uruchomione okno dialogowe postępu drukowania.
Poniższy przykład kodu ilustruje jedno z wywołań komunikatów aktualizacji.
// Update print status PostMessage ( threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);
Uruchamianie wątku drukowania
Wątek przetwarzania wydruku działa do momentu, gdy funkcja PrintThreadProc zwróci wartość. W poniższych krokach rozpoczyna się wątek drukowania.
Przygotowanie danych i elementów interfejsu użytkownika do drukowania.
Przed rozpoczęciem wątku przetwarzania wydruku należy zainicjować elementy danych opisujące zadanie drukowania i elementy interfejsu użytkownika. Te elementy zawierają stan kursora, dzięki czemu kursor oczekiwania będzie odpowiednio wyświetlany. Należy również skonfigurować pasek postępu, aby odzwierciedlał rozmiar zadania drukowania. Te kroki przygotowawcze są szczegółowo opisane w Jak to zrobić: Zbieranie informacji o zadaniu drukowania od użytkownika.
W poniższym przykładzie kodu pokazano, jak skonfigurować pasek postępu w celu odzwierciedlenia rozmiaru zadania drukowania żądanego przez użytkownika.
// 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));
Uruchom wątek przetwarzania wydruku.
Wywołaj CreateThread, aby uruchomić wątek przetwarzania.
Poniższy przykład kodu uruchamia wątek przetwarzania.
// Start the printing thread threadInfo->printThreadHandle = CreateThread ( NULL, 0L, (LPTHREAD_START_ROUTINE)PrintThreadProc, (LPVOID)threadInfo, 0L, &threadInfo->printThreadId);
Sprawdź, czy przetwarzanie wydruku nie powiodło się podczas uruchamiania.
CreateThread zwraca dojście do utworzonego wątku, jeśli wątek został utworzony pomyślnie. Funkcja PrintThreadProc, która została uruchomiona w nowym wątku, sprawdza pewne warunki przed rozpoczęciem rzeczywistego przetwarzania zadania drukowania. Jeśli wykryje jakiekolwiek błędy w tych sprawdzeniach, funkcja PrintThreadProc może zakończyć działanie bez przetwarzania żadnych danych zadania drukowania. Wątek interfejsu użytkownika może sprawdzić, czy wątek przetwarzania został uruchomiony pomyślnie, oczekując na uchwyt wątku przez dłuższy czas niż trwa wykonywanie testów początkowych, ale nie dłużej niż jest to konieczne. Gdy wątek zakończy się, uchwyt do wątku zostanie zasygnalizowany. Kod sprawdza stan wątku przez krótki czas po uruchomieniu wątku przetwarzania. Funkcja WaitForSingleObject zwraca wartość, gdy nastąpi wystąpienie limitu czasu lub uchwyt wątku jest zasygnalizowany. Jeśli funkcja WaitForSingleObject zwraca stan WAIT_OBJECT_0, wątek został zamknięty wcześnie i dlatego należy zamknąć okno dialogowe postępu drukowania, jak pokazano w poniższym przykładzie kodu.
// 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); }
Zatrzymywanie wątku drukowania
Gdy użytkownik kliknie przycisk Anuluj w oknie dialogowym postępu drukowania, wątek drukowania zostanie powiadomiony, aby umożliwić zatrzymanie przetwarzania zadania drukowania w sposób uporządkowany. Podczas gdy przycisk Anuluj i zdarzenie quitEvent są ważnym aspektem tego przetwarzania, należy zaprojektować całą sekwencję przetwarzania tak, aby mogła zostać pomyślnie przerwana. Oznacza to, że kroki w sekwencji nie mogą pozostawiać przydzielonych zasobów, które nie są zwalniane, jeśli użytkownik anuluje sekwencję przed jej ukończeniem.
Poniższy przykład kodu pokazuje, jak przykładowy program sprawdza quitEvent przed przetwarzaniem każdej strony w wydrukowanym dokumencie. Jeśli zasygnalizowano quitEvent lub wykryto błąd, proces drukowania zostanie zatrzymany.
// 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;
}
}