Share via


방법: 인쇄 스레드 시작 및 중지

이 항목에서는 인쇄 작업 처리 스레드를 시작하고 중지하는 방법을 설명합니다.

개요

인쇄 작업이 사용자 인터페이스 응답을 차단하지 않도록 하려면 인쇄 작업을 처리하는 별도의 스레드를 만듭니다. 프로그램이 시작될 때 시작되는 스레드는 사용자 상호 작용으로 인해 발생하는 창 메시지를 처리하는 스레드이므로 UI 스레드입니다. 프로그램은 사용자 인터페이스가 사용자의 마우스 및 키보드 입력에 적시에 응답하도록 지연 없이 이러한 메시지를 처리해야 합니다. 프로그램이 이러한 메시지에 신속하게 응답할 수 있도록 한 메시지 중에 수행할 수 있는 처리 양이 제한됩니다. 사용자 요청에 광범위한 처리가 필요한 경우 프로그램에서 후속 사용자 상호 작용을 처리할 수 있도록 다른 스레드가 해당 처리를 수행해야 합니다.

별도의 스레드에서 데이터를 처리하려면 프로그램에서 사용자 인터페이스 스레드 및 처리 스레드의 작업을 조정해야 합니다. 예를 들어 처리 스레드가 데이터를 처리하는 동안 기본 스레드는 해당 데이터를 변경하지 않아야 합니다. 이를 관리하는 한 가지 방법은 세마포, 이벤트 및 뮤텍스와 같은 스레드로부터 안전한 동기화 개체를 사용하는 것입니다.

동시에 처리 스레드가 실행되는 동안 일부 사용자 상호 작용을 방지해야 합니다. 샘플 프로그램에서 애플리케이션 데이터가 보호되고 모달 진행률 대화 상자에서 인쇄 작업 처리를 관리하여 사용자 상호 작용이 제한됩니다. 모달 대화 상자를 사용하면 사용자가 프로그램의 기본 창과 상호 작용할 수 없으므로 데이터가 인쇄되는 동안 사용자가 애플리케이션 프로그램 데이터를 변경할 수 없습니다.

인쇄 작업을 처리하는 동안 사용자가 수행할 수 있는 유일한 작업은 인쇄 작업을 취소하는 것입니다. 이 제한은 커서 셰이프에 의해 사용자에게 신호를 보냅니다. 취소 단추 위에 커서가 있으면 화살표 커서가 표시됩니다. 이는 사용자가 이 단추를 클릭할 수 있음을 나타냅니다. 커서가 프로그램 창 영역의 다른 부분에 있는 경우 프로그램이 사용 중이며 사용자 입력에 응답할 수 없음을 나타내는 대기 커서가 표시됩니다.

인쇄 스레드 프로시저 만들기

인쇄 처리 중 이러한 기능을 포함하는 것이 좋습니다.

  • 인쇄 처리는 단계로 나뉩니다.

    사용자가 취소 단추를 클릭하면 중단할 수 있는 개별 처리 단계로 인쇄 처리를 나눌 수 있습니다. 인쇄 처리에 프로세서 집약적 작업이 포함될 수 있기 때문에 유용합니다. 이 처리를 단계로 나누면 인쇄 처리가 다른 스레드 또는 프로세스를 차단하거나 지연하지 못하게 할 수 있습니다. 또한 처리를 논리적 단계로 분리하면 인쇄 작업을 완료하기 전에 인쇄 작업을 종료해도 분리된 리소스를 남기지 않도록 언제든지 인쇄 처리를 완전히 종료할 수 있습니다.

    다음은 인쇄 단계의 예제 목록입니다.

    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 스레드는 초기 테스트를 수행하는 데 걸리는 시간보다 길지만 더 이상 필요하지 않은 기간 동안 스레드 핸들을 대기하여 처리 스레드가 성공적으로 시작되었는지 여부를 검사 수 있습니다. 스레드가 종료되면 스레드에 대한 핸들이 신호를 수신합니다. 코드는 처리 스레드를 시작한 후 짧은 시간 동안 스레드의 상태를 확인합니다. Timeout이 발생하거나 스레드 핸들이 신호를 받으면 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;
    }
}