共用方式為


如何:啟動和停止列印線程

本主題描述如何啟動和停止列印作業處理線程。

概述

若要防止列印活動封鎖使用者介面回應,請建立個別線程來處理列印作業。 啟動程式時啟動的線程是處理使用者互動所產生的視窗訊息線程,因此是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 函式傳回為止。 下列步驟會啟動列印線程。

  1. 準備用於列印的數據和使用者介面元素。

    開始列印處理線程之前,您必須初始化描述列印作業和使用者介面元素的數據元素。 這些元素包含游標狀態,以便正確地顯示等候游標。 您也必須設定進度列,以反映列印作業的大小。 這些準備步驟詳述於 如何:從使用者收集列印作業資訊

    下列程式代碼範例示範如何設定進度列,以反映使用者所要求的列印作業大小。

    // 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));
    
  2. 啟動列印處理線程。

    呼叫 createThread ,以啟動處理線程。

    下列程式代碼範例會啟動處理線程。

    // Start the printing thread
    threadInfo->printThreadHandle = CreateThread (
                    NULL, 
                    0L, 
                    (LPTHREAD_START_ROUTINE)PrintThreadProc,
                    (LPVOID)threadInfo, 
                    0L, 
                    &threadInfo->printThreadId);
    
  3. 檢查列印處理是否在啟動時失敗。

    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;
    }
}