方法: 印刷スレッドを開始および停止する
このトピックでは、印刷ジョブ処理スレッドを開始および停止する方法について説明します。
概要
印刷アクティビティがユーザー インターフェイスの応答をブロックしないようにするには、別のスレッドを作成して印刷ジョブを処理します。 プログラムの起動時に開始されるスレッドは、ユーザー操作の結果として発生するウィンドウ メッセージを処理するスレッドであり、したがって UI スレッドです。 プログラムは、ユーザー インターフェイスがユーザーのマウスとキーボードの入力にタイムリーに応答するために、遅延なくこれらのメッセージを処理する必要があります。 プログラムがこれらのメッセージに迅速に応答できるようにするには、任意の 1 つのメッセージの間に実行できる処理の量が制限されます。 ユーザー要求で広範な処理が必要な場合、別のスレッドがその処理を実行して、後続のユーザー操作をプログラムで処理できるようにする必要があります。
別のスレッドでデータを処理するには、ユーザー インターフェイス スレッドと処理スレッドの操作を調整するプログラムが必要です。 たとえば、処理スレッドがデータを処理している間に、メイン スレッドでそのデータを変更することはできません。 これを管理する方法の 1 つは、セマフォ、イベント、ミューテックスなどのスレッド セーフな同期オブジェクトを使用することです。
同時に、処理スレッドの実行中に一部のユーザー操作を防止する必要があります。 サンプル プログラムでは、アプリケーション データが保護され、モーダル進行状況ダイアログ ボックスで印刷ジョブの処理を管理することで、ユーザーの操作が制限されます。 モーダル ダイアログ ボックスを使用すると、ユーザーはプログラムのメイン ウィンドウを操作できなくなります。これにより、データの印刷中にユーザーがアプリケーション プログラム のデータを変更できなくなります。
印刷ジョブの処理中にユーザーが実行できる唯一のアクションは、印刷ジョブを取り消す操作です。 この制限は、カーソル図形によってユーザーに通知されます。 カーソルが [キャンセル ] ボタンの上にある場合は、矢印カーソルが表示されます。これは、ユーザーがこのボタンをクリックできることを示します。 カーソルがプログラムのウィンドウ領域の他の部分の上にある場合は、待機カーソルが表示されます。これは、プログラムがビジー状態であり、ユーザー入力に応答できないことを示します。
印刷スレッド プロシージャの作成
印刷処理中にこれらの機能を含めることをお勧めします。
印刷処理はステップに分かれています
印刷処理は、ユーザーが [キャンセル ] ボタンをクリックした場合に中断できる個別の処理ステップに分割できます。 これは、印刷処理にプロセッサを集中的に使用する操作が含まれる可能性があるため、便利です。 この処理をステップに分割すると、印刷処理が他のスレッドやプロセスをブロックまたは遅延するのを防ぐことができます。 処理を論理的な手順に分割すると、印刷処理を任意の時点でクリーンに終了できるため、終了する前に印刷ジョブを終了しても、孤立したリソースは残りません。
印刷手順の一覧の例を次に示します。
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);
ステップ間でキャンセル イベントを確認する
ユーザーが [ キャンセル ] ボタンをクリックすると、ユーザー インターフェイス スレッドによって cancel イベントが通知されます。 処理スレッドは、キャンセル イベントを定期的にチェックして、ユーザーが [キャンセル] ボタンをクリックしたことを知る必要があります。 WaitForSingleObject ステートメントは、このチェックを実行します。また、印刷ジョブ処理が他のスレッドやプロセスをブロックまたは遅延しないように、他のプログラムで実行する機会も与えます。
次のコード例は、キャンセル イベントが発生したかどうかを確認するテストの 1 つを示しています。
waitStatus = WaitForSingleObject ( threadInfo->quitEvent, stepDelay); if (WAIT_OBJECT_0 == waitStatus) { hr = E_ABORT; }
ユーザー インターフェイス スレッドに状態の更新を送信する
印刷処理の各ステップとして、印刷処理スレッドは、進行状況バーを更新できるように、更新メッセージを印刷の進行状況ダイアログ ボックスに送信します。 UI スレッドで印刷の進行状況ダイアログ ボックスが実行されていることに注意してください。
次のコード例は、更新メッセージ呼び出しの 1 つを示しています。
// 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); }
印刷スレッドの停止
ユーザーが [印刷の進行状況] ダイアログ ボックスの [キャンセル ] ボタンをクリックすると、印刷スレッドに通知され、印刷ジョブの処理を順番に停止できます。 Cancel ボタンと 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;
}
}