如何:啟動和停止列印執行緒
本主題描述如何啟動和停止列印工作處理執行緒。
概觀
若要防止列印活動封鎖使用者介面回應,請建立個別執行緒來處理列印工作。 啟動程式時啟動的執行緒是處理使用者互動所產生的視窗訊息的執行緒,因此是 UI 執行緒。 程式必須處理這些訊息,使用者介面不會延遲,以及時回應使用者的滑鼠和鍵盤輸入。 若要讓程式能夠快速回應這些訊息,可以在任何一則訊息期間執行的處理量受到限制。 當使用者要求需要大量處理時,不同的執行緒必須執行該處理,以允許程式處理後續的使用者互動。
在個別執行緒中處理資料需要程式來協調使用者介面執行緒和處理執行緒的作業。 例如,當處理執行緒正在處理資料時,主執行緒不得改變該資料。 其中一個管理方式是透過安全線程同步處理物件,例如旗號、事件和 Mutex。
同時,在處理執行緒執行時,必須防止某些使用者互動。 在範例程式中,應用程式資料會受到保護,而且使用者互動受限於模式進度對話方塊管理列印工作處理。 強制回應對話方塊可防止使用者在列印資料時與程式的主視窗互動,以防止使用者在列印資料時改變應用程式程式資料。
在列印工作正在處理時,使用者可以執行的唯一動作是取消列印工作。 此限制會由游標圖形向使用者發出訊號。 當游標位於 [取消] 按鈕上方時,會顯示箭頭游標,表示使用者可以按一下此按鈕。 當游標位於程式視窗區域的任何其他部分上方時,會顯示等候游標,這表示程式忙碌且無法回應使用者輸入。
建立列印執行緒程式
建議您在列印處理時納入這些功能。
列印處理分為步驟
您可以將列印處理分割成離散的處理步驟,當使用者按一下 [ 取消 ] 按鈕時,可以中斷。 這很有用,因為列印處理可以包含大量處理器的作業。 將此處理中斷為步驟,可防止列印處理封鎖或延遲其他執行緒或進程。 將處理分成邏輯步驟也可讓您在任何時間點完全終止列印處理,如此一來,在完成列印工作之前結束列印工作將不會留下任何孤立的資源。
這是列印步驟的範例清單。
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);
檢查步驟之間的取消事件
當使用者按一下 [ 取消] 按鈕時,使用者介面執行緒會發出取消事件的訊號。 處理執行緒必須定期檢查取消事件,以瞭解使用者何時按一下 [ 取消 ] 按鈕。 WaitForSingleObject語句會執行這項檢查,而且它們也會讓其他程式有機會執行,讓列印工作處理不會封鎖或延遲其他執行緒或進程。
下列程式碼範例說明其中一個測試,以查看是否已發生取消事件。
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
將狀態更新傳送至使用者介面執行緒
在每個列印處理步驟中,列印處理執行緒會將更新訊息傳送至列印進度對話方塊,以便更新進度列。 請注意,列印進度對話方塊正在 UI 執行緒中執行。
下列程式碼範例說明其中一個更新訊息呼叫。
// Update print status PostMessage ( threadInfo->parentWindow, USER_PRINT_STATUS_UPDATE, 0L, 0L);
啟動列印執行緒
列印處理執行緒會執行,直到 PrintThreadProc 函式傳回為止。 下列步驟會啟動列印執行緒。
準備用於列印的資料和使用者介面元素。
開始列印處理執行緒之前,您必須初始化描述列印工作和使用者介面元素的資料元素。 這些專案包含資料指標狀態,以便適當地顯示等候資料指標。 您也必須設定進度列以反映列印工作的大小。 這些準備步驟詳述 于如何:從使用者收集列印工作資訊。
下列程式碼範例示範如何設定進度列,以反映使用者所要求的列印工作大小。
// 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));
啟動列印處理執行緒。
呼叫 CreateThread 以啟動處理執行緒。
下列程式碼範例會啟動處理執行緒。
// Start the printing thread threadInfo->printThreadHandle = CreateThread ( NULL, 0L, (LPTHREAD_START_ROUTINE)PrintThreadProc, (LPVOID)threadInfo, 0L, &threadInfo->printThreadId);
檢查列印處理是否在啟動時失敗。
如果成功建立執行緒,CreateThread會傳回所建立執行緒的控制碼。 在新執行緒中啟動的 PrintThreadProc 函式會先檢查一些條件,再啟動實際的列印工作處理。 如果它偵測到這些檢查中的任何錯誤,PrintThreadProc 可能會傳回而不處理任何列印工作資料。 UI 執行緒可以檢查處理執行緒是否成功啟動,方法是等候執行緒控制碼一段時間超過執行初始測試所花費的時間,但不再必要。 當執行緒結束時,執行緒的控制碼就會變成訊號。 程式碼會線上程啟動處理執行緒之後,檢查執行緒的狀態一段時間。 WaitForSingleObject函式會在逾時發生或執行緒控制碼發出訊號時傳回。 如果 WaitForSingleObject 函式傳回 WAIT_OBJECT_0 狀態,執行緒會提早結束,因此您應該關閉列印進度對話方塊,如下列程式碼範例所示。
// 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); }
停止列印執行緒
當使用者按一下 [列印進度] 對話方塊中的 [ 取消] 按鈕時,會通知列印執行緒,以便停止以循序方式處理列印工作。 雖然 [取消] 按鈕和 quitEvent 事件是此處理的重要層面,但您必須設計整個處理順序以順利中斷。 這表示,如果使用者在完成之前取消序列,則順序中的步驟不得保留任何未釋放的已配置資源。
下列程式碼範例示範範例程式在處理所列印檔案中的每個頁面之前,如何檢查 quitEvent 。 如果 quitEvent 收到訊號或偵測到錯誤,列印處理就會停止。
// 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;
}
}