다음을 통해 공유


프린터 확장

Important

최신 인쇄 플랫폼은 Windows에서 프린터와 통신하는 데 선호되는 수단입니다. 프린터 장치 개발을 위해 Windows 10 및 11의 인쇄 환경을 사용자 지정하려면 MICROSOFT의 IPP 받은 편지함 클래스 드라이버와 PSA(인쇄 지원 앱)를 사용하는 것이 좋습니다.

자세한 내용은 최신 인쇄 플랫폼 및 인쇄 지원 앱 디자인 가이드를 참조하세요.

프린터 확장 앱은 사용자가 Windows 데스크톱에서 기존 애플리케이션을 실행할 때 인쇄 기본 설정 및 프린터 알림을 지원합니다.

프린터 확장은 모든 COM 지원 언어로 빌드할 수 있지만 Microsoft .NET Framework 4를 사용하여 빌드되도록 최적화되어 있습니다. 프린터 확장은 XCopy가 가능하고 운영 체제에 포함된 것 이외의 외부 런타임에 대한 종속성이 없는 경우(예: .NET) 인쇄 드라이버 패키지와 함께 배포될 수 있습니다. 프린터 확장 앱이 이러한 조건을 충족하지 않는 경우 setup.exe 또는 MSI 패키지에 배포하고 v4 매니페스트에 지정된 PrinterExtensionUrl 지시문을 사용하여 프린터의 디바이스 스테이지 환경에 보급할 수 있습니다. 프린터 확장 앱이 MSI 패키지를 통해 배포되는 경우 패키지에 인쇄 드라이버를 추가하거나 패키지에서 나가고 드라이버를 별도로 배포할 수 있습니다. PrinterExtensionUrl은 프린터 기본 설정 환경에 표시됩니다.

IT 관리자에게는 프린터 확장 배포를 관리하는 몇 가지 옵션이 있습니다. 애플리케이션이 setup.exe 또는 MSI에 패키지된 경우 IT 관리자는 Microsoft Endpoint Configuration Manager와 같은 표준 소프트웨어 배포 도구를 사용하거나 표준 OS 이미지에 애플리케이션을 포함할 수 있습니다. IT 관리자는 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\print queue name>\PrinterDriverData\PrinterExtensionUrl을 편집하는 경우 v4 매니페스트에 지정된 PrinterExtensionUrl<을 재정의할 수도 있습니다.

또한 기업에서 프린터 확장을 모두 차단하도록 선택하는 경우 "컴퓨터 구성\관리 템플릿\프린터\v4 프린터 드라이버가 프린터 확장 응용 프로그램을 표시하도록 허용하지 않음"이라는 그룹 정책을 통해 이 작업을 수행할 수 있습니다.

프린터 확장 빌드

프린터 확장을 개발하는 경우 주의해야 하는 6가지 주요 초점 영역이 있습니다. 이러한 포커스 영역은 다음 목록에 표시됩니다.

  • 등록

  • 이벤트 사용

  • OnDriverEvent 처리기

  • 인쇄 기본 설정

  • 프린터 알림

  • 프린터 관리

등록

프린터 확장은 레지스트리 키 집합을 지정하거나 v4 매니페스트 파일의 PrinterExtensions 섹션에서 애플리케이션 정보를 지정하여 인쇄 시스템에 등록됩니다.

프린터 확장에 대해 각기 다른 진입점을 지원하는 지정된 GUID가 있습니다. v4 매니페스트 파일에서 이러한 GUID를 사용할 필요는 없지만 v4 드라이버 설치에 레지스트리 형식을 사용하려면 GUID 값을 알고 있어야 합니다. 다음 표에서는 두 진입점에 대한 GUID 값을 보여줍니다.

진입점 GUID
인쇄 기본 설정 {EC8F261F-267C-469F-B5D6-3933023C29CC}
프린터 알림 {23BB1328-63DE-4293-915B-A6A23D929ACB}

프린터 드라이버 외부에 설치된 프린터 확장은 레지스트리를 사용하여 등록해야 합니다. 이렇게 하면 스풀러의 상태 또는 클라이언트 컴퓨터의 v4 구성 모듈에 관계없이 프린터 확장을 설치할 수 있습니다.

PrintNotify 서비스가 시작되면 [OfflineRoot] 경로에서 레지스트리 키를 확인하고 보류 중인 등록 또는 등록 취소를 처리합니다. 보류 중인 등록 또는 등록 취소가 완료되면 레지스트리 키가 실시간으로 삭제됩니다. 스크립트 또는 반복 프로세스를 사용하여 레지스트리 키를 배치하는 경우 \[PrinterDriverId] 키를 지정할 때마다 \[PrinterExtensionID] 키를 다시 만들어야 할 수 있습니다. 불완전하거나 형식이 잘못된 키는 삭제되지 않습니다.

이 등록은 처음 설치할 때만 필요합니다. 다음 예제에서는 프린터 확장을 등록 하는 데 사용 하는 올바른 레지스트리 키 형식을 보여 줍니다.

참고 항목

[OfflineRoot] 는 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\OfflinePrinterExtensions의 약식으로 사용됩니다.

[OfflineRoot]
    \[PrinterExtensionId] {GUID}
           AppPath=[PrinterExtensionAppPath] {String}
           \[PrinterDriverId] {GUID}
                  \[PrinterExtensionReasonGuid]
(default) = ["0"|"1"] {REG_SZ 0:Unregister, 1:Register}
                  \…
                  \[PrinterExtensionReasonGuidN]
           \[PrinterDriverId2]
                  \[PrinterExtensionReasonGuid2.1]
                  \…
                  \[PrinterExtensionReasonGuid2.Z]
           …
           \[PrinterDriverIdM]
    \[PrinterExtensionId2]
    …
    \[PrinterExtensionIdT]

예를 들어 다음 키 집합은 프린터 확장 프로그램을 {PrinterExtensionIDGuid} PrinterExtensionID 및 {PrinterDriverID1Guid} 및 {PrinterDriverID2Guid} PrinterDriverID에 대한 "C:\Program Files\Fabrikam\pe.exe" 실행 파일에 대한 정규화된 경로를 프린터 기본 설정 및 프린터 알림 이유로 등록합니다.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "1"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "1"

동일한 프린터 확장을 제거하려면 다음 키 집합을 지정해야 합니다.

[OfflineRoot]
    \{PrinterExtensionIDGuid}
           AppPath="C:\Program Files\Fabrikam\pe.exe"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"
           \{PrinterDriverID1Guid}
                 \{EC8F261F-267C-469F-B5D6-3933023C29CC}
            (default) = "0"
                 \{23BB1328-63DE-4293-915B-A6A23D929ACB}
            (default) = "0"

프린터 확장은 사용자가 시작한 컨텍스트와 이벤트 시작 컨텍스트 모두에서 실행할 수 있으므로 프린터 확장이 작동하는 컨텍스트를 확인할 수 있습니다. 예를 들어 앱이 알림 또는 인쇄 기본 설정에 대해 시작된 경우 모든 큐의 상태를 열거하지 않도록 허용할 수 있습니다. 드라이버와 별도로 설치된 프린터 확장(예: MSI 또는 setup.exe)은 시작 메뉴 바로 가기 또는 등록 중에 레지스트리에 채워진 AppPath 항목에서 명령줄 스위치를 사용하는 것이 좋습니다. 드라이버와 함께 설치된 프린터 확장은 DriverStore에 설치되므로 인쇄 기본 설정 또는 프린터 알림 이벤트 외부에서 시작되지 않습니다. 따라서 이 경우 명령줄 스위치를 지정하는 것은 지원되지 않습니다.

프린터 확장이 현재 PrinterDriverID에 등록되면 AppPath에 PrinterDriverID를 포함해야 합니다. 예를 들어 이름이 printerextension.exe 프린터 확장 앱과 PrinterDriverID 값 이 {GUID}인 경우 [PrinterExtensionAppPath]는 다음 예제와 같습니다.

"C:\program files\fabrikam\printerextension.exe {GUID}"

이벤트 사용

런타임에 프린터 확장은 현재 PrinterDriverID에 대한 이벤트 트리거를 사용하도록 설정해야 합니다. 이는 args[] 배열을 통해 앱에 전달된 PrinterDriverID이며 인쇄 시스템에서 인쇄 기본 설정 또는 프린터 알림과 같은 이유로 적절한 이벤트 컨텍스트를 제공할 수 있습니다.

따라서 애플리케이션은 현재 PrinterDriverID에 대한 새 PrinterExtensionManager를 만들고, OnDriverEvent 이벤트를 처리하는 대리자를 등록하고, PrinterDriverID를 사용하여 EnableEvents 메서드를 호출해야 합니다. 다음 코드 조각에서는 이 방법을 보여 줍니다.

PrinterExtensionManager mgr = new PrinterExtensionManager();
mgr.OnDriverEvent += OnDriverEvent;
mgr.EnableEvents(new Guid(PrinterDriverID1));

앱이 5초 이내에 EnableEvents를 호출하지 않으면 Windows는 시간 초과를 수행하고 표준 UI를 시작합니다. 이를 완화하기 위해 프린터 확장은 다음을 비롯한 최신 성능 모범 사례를 따라야 합니다.

  • EnableEvents를 호출할 때까지 앱 초기화를 최대한 지연합니다. 그런 다음, 초기화 중에 비동기 메서드를 사용하고 UI 스레드를 차단하지 않음으로써 UI 응답성의 우선 순위를 지정합니다.

  • ngen을 사용하여 설치하는 동안 네이티브 이미지를 생성합니다. 자세한 내용은 네이티브 이미지 생성기를 참조 하세요.

  • 성능 측정 도구를 사용하여 로드에 대한 성능 문제를 찾습니다. 자세한 내용은 Windows 성능 분석 도구를 참조 하세요.

DriverEvent 처리기

OnDriverEvent 처리기가 등록되고 이벤트를 사용하도록 설정한 후 인쇄 기본 설정 또는 프린터 알림을 처리하기 위해 프린터 확장이 시작된 경우 처리기가 호출됩니다. 앞의 코드 조각에서 OnDriverEvent라는 메서드가 이벤트 처리기로 등록되었습니다. 다음 코드 조각 에서 PrinterExtensionEventArgs 매개 변수는 인쇄 기본 설정 및 프린터 알림 시나리오를 생성할 수 있도록 하는 개체입니다. PrinterExtensionEventArgs는 IPrinterExtensionEventArgs래퍼입니다.

static void OnDriverEvent(object sender, PrinterExtensionEventArgs eventArgs)
{
    //
    // Display the print preferences window.
    //

    if (eventArgs.ReasonId.Equals(PrinterExtensionReason.PrintPreferences))
    {
        PrintPreferenceWindow printPreferenceWindow = new PrintPreferenceWindow();
        printPreferenceWindow.Initialize(eventArgs);

        //
        // Set the caller application's window as parent/owner of the newly created printing preferences window.
        //

        WindowInteropHelper wih = new WindowInteropHelper(printPreferenceWindow);
        wih.Owner = eventArgs.WindowParent;

        //
        // Display a modal/non-modal window based on the 'WindowModal' parameter.
        //

        if (eventArgs.WindowModal)
        {
            printPreferenceWindow.ShowDialog();
        }
        else
        {
            printPreferenceWindow.Show();
        }
    }

    //
    // Handle driver events.
    //

    else if (eventArgs.ReasonId.Equals(PrinterExtensionReason.DriverEvent))
    {
        // Handle driver events here.
    }
}

작동 중단 또는 느린 프린터 확장과 관련된 잘못된 사용자 환경을 방지하기 위해 Windows는 앱이 시작된 후 짧은 시간 내에 EnableEvents가 호출되지 않는 경우 시간 제한을 구현합니다. 디버깅을 사용하도록 설정하려면 PrintNotify 서비스에 연결된 디버거가 있는 경우 이 시간 제한이 비활성화됩니다.

그러나 대부분의 경우 관심 있는 모든 앱 관련 코드는 OnDriverEvent 콜백 중 또는 그 이후에 실행됩니다. 개발하는 동안 OnDriverEvent 콜백에서 인쇄 기본 설정 또는 프린터 알림 환경을 시작하기 전에 MessageBox를 표시하는 것이 유용할 수도 있습니다. MessageBox가 나타나면 Visual Studio로 돌아가서 프로세스에 대한 디버그>연결을 선택하고 프로세스의 이름을 선택합니다. 마지막으로 MessageBox로 돌아가 확인을 선택하여 다시 시작합니다. 이렇게 하면 예외가 표시되고 해당 시점부터 중단점에 도달합니다.

새 ReasonIds는 나중에 지원될 수 있습니다. 따라서 프린터 확장은 ReasonID를 명시적으로 확인해야 하며 마지막으로 알려진 ReasonID를 검색하기 위해 "else" 문을 사용하면 안 됩니다. ReasonID가 수신되고 알 수 없는 경우 앱은 정상적으로 종료되어야 합니다.

인쇄 기본 설정은 PrintSchemaEventArgs.Ticket 개체에 의해 구동됩니다. 이 개체는 디바이스의 기능 및 옵션을 설명하는 PrintTicket 및 PrintCapabilities 문서를 모두 캡슐화합니다. 기본 XML도 사용할 수 있지만 개체 모델을 사용하면 이러한 형식으로 더 쉽게 작업할 수 있습니다.

IPrintSchemaTicket 또는 IPrintSchemaCapabilities 개체 내에는 기능(IPrintSchemaFeature) 및 옵션(IPrintSchemaOption)이 있습니다. 기능 및 옵션에 사용되는 인터페이스는 원본에 관계없이 동일하지만 기본 XML의 결과로 동작이 약간 달라집니다. 예를 들어 PrintCapabilities 문서는 기능당 많은 옵션을 지정하는 반면 PrintTicket 문서는 선택한 옵션(또는 기본값) 옵션만 지정합니다. 마찬가지로 PrintCapabilities 문서는 지역화된 표시 문자열을 지정하지만 PrintTicket 문서는 지정하지 않습니다.

WPF의 데이터 바인딩에 대한 자세한 내용은 데이터 바인딩 개요를 참조 하세요.

성능을 최대화하기 위해 GetPrintCapabilities에 대한 호출은 PrintCapabilities 문서를 업데이트해야 하는 경우에만 수행하는 것이 좋습니다.

사용자가 데이터 바인딩된 ComboBox 컨트롤을 사용하여 선택할 때 PrintTicket 개체가 자동으로 업데이트됩니다. 사용자가 마침내 확인을 클릭하면 비동기 유효성 검사 및 완료 체인이 시작됩니다. 이 비동기 패턴은 UI 스레드에서 장기 실행 작업이 발생하고 인쇄 기본 설정 UI 또는 인쇄 중인 앱에서 중단을 일으키지 않도록 하기 위해 광범위하게 사용됩니다. 다음은 사용자가 확인을 클릭한 후 PrintTicket 변경 내용을 처리하는 데 사용되는 단계 목록입니다.

  1. PrintSchemaTicket는 IPrintSchemaTicket::ValidateAsync 메서드를 사용하여 비동기적으로 유효성을 검사합니다.

  2. 비동기 유효성 검사가 완료되면 CLR(공용 언어 런타임)이 PrintTicketValidateCompleted 메서드를 호출합니다.

    1. 유효성 검사에 성공하면 CommitPrintTicketAsync 메서드를 호출하고 CommitPrintTicketAsync는 IPrintSchemaTicket::CommitAsync 메서드를 호출합니다. 또한 PrintTicket 업데이트가 성공적으로 완료되면 PrintTicketCommitCompleted 메서드를 호출합니다. 이 메서드는 PrinterExtensionEventArgs.Request.Complete 메서드를 호출하여 인쇄 기본 설정이 완료되었음을 나타내는 편리한 메서드를 호출한 다음 앱을 닫습니다.

    2. 그렇지 않으면 제약 조건 상황을 처리하기 위해 사용자에게 UI를 제공합니다.

사용자가 인쇄 기본 설정 창을 직접 취소하거나 닫은 경우 프린터 확장은 오류 로그에 대한 적절한 HRESULT 값과 메시지를 사용하여 IPrinterExtensionEventArgs.Request.Cancel을 호출합니다.

이전 단락에 설명된 대로 프린터 확장 프로세스가 닫혀 있고 Complete 또는 Cancel 메서드를 호출하지 않은 경우 인쇄 시스템은 자동으로 Microsoft 제공 UI 사용으로 대체됩니다.

디바이스 상태 정보를 검색하기 위해 프린터 확장 프로그램에서 Bidi를 사용하여 인쇄 장치를 쿼리할 수 있습니다. 예를 들어 잉크 상태 또는 디바이스에 대한 다른 종류의 상태를 표시하기 위해 프린터 확장은 IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery 메서드를 사용하여 디바이스에 Bidi 쿼리를 실행할 수 있습니다. 최신 Bidi 상태를 가져오는 것은 OnBidiResponseReceived 이벤트에 대한 이벤트 처리기를 설정하고 유효한 Bidi 쿼리를 사용하여 SendBidiQuery 메서드를 호출하는 2단계 프로세스입니다. 다음 코드 조각은 이 2단계 프로세스를 보여줍니다.

PrinterQueue.OnBidiResponseReceived += new
EventHandler<PrinterQueueEventArgs>(OnBidiResponseReceived);
PrinterQueue.SendBidiQuery("\\Printer.consumables");

Bidi 응답을 받으면 다음 이벤트 처리기가 호출됩니다. 또한 이 이벤트 처리기에는 모의 잉크 상태 구현이 있으며, 디바이스를 사용할 수 없는 경우 개발에 유용할 수 있습니다. PrinterQueueEventArgs 개체에는 HRESULT 및 Bidi XML 응답이 모두 포함됩니다. Bidi XML 응답에 대한 자세한 내용은 Bidi 요청 및 응답 스키마를 참조 하세요.

private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs e)
{
    if (e.StatusHResult != (int)HRESULT.S_OK)
    {
        MockInkStatus();
        return;
    }

    //
    // Display the ink levels from the data.
    //

    BidiHelperSource = new BidiHelper(e.Response);
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs("BidiHelperSource"));
    }
    InkStatusTitle = "Ink status (Live data)";
}

프린터 알림

프린터 알림은 인쇄 기본 설정과 정확히 동일한 방식으로 호출됩니다. OnDriverEvent 처리기에서 IPrinterExtensionEventArgs가 ReasonID가 DriverEvents GUID와 일치한다는 것을 나타내는 경우 이 이벤트를 처리하는 환경을 빌드할 수 있습니다.

다음 변수는 기능 프린터 알림 환경을 처리하는 데 가장 유용합니다.

  • PrinterExtensionEventArgs.BidiNotification – 이벤트를 트리거한 Bidi XML을 전달합니다.

  • PrinterExtensionEventArgs.DetailedReasonId – 드라이버 이벤트 xml 파일의 eventID GUID를 포함합니다.

알림에 대한 IPrinterExtensionEventArgs 개체의 가장 중요한 특성은 BidiNotification 속성입니다. 이렇게 하면 이벤트가 트리거된 Bidi XML이 전달됩니다. Bidi XML 응답에 대한 자세한 내용은 Bidi 요청 및 응답 스키마를 참조 하세요.

프린터 관리

프린터를 관리/유지 관리하기 위한 허브로 사용할 수 있는 앱으로 프린터 확장의 역할을 지원하기 위해 현재 프린터 확장이 등록된 인쇄 큐를 열거하고 각 큐의 상태를 가져올 수 있습니다. 이는 PrinterExtensionSample 프로젝트에서는 표시되지 않지만 이벤트 처리기를 등록하기 위해 App.xaml.cs Main 메서드에 다음 코드 조각을 추가할 수 있습니다.

mgr.OnPrinterQueuesEnumerated += new EventHandler<PrinterQueuesEnumeratedEventArgs>(mgr_OnPrinterQueuesEnumerated);

큐가 열거되면 이벤트 처리기가 호출되고 상태 작업이 수행될 수 있습니다. 이 이벤트는 사용자가 연 이후 더 많은 큐를 설치한 경우에도 열거된 인쇄 큐 목록이 최신인지 확인하기 위해 앱의 수명 동안 주기적으로 발생합니다. 따라서 이벤트 처리기가 실행될 때마다 새 창을 만들지 않는 것이 중요하며 다음 코드 조각에 표시됩니다.

static void mgr_OnPrinterQueuesEnumerated(object sender, PrinterQueuesEnumeratedEventArgs e)
{
    foreach (IPrinterExtensionContext pContext in e)
    {
        // show status
    }
}

프린터 확장을 사용하여 유지 관리 작업을 수행하려면 다음 의사 코드에 설명된 대로 레거시 WritePrinter API를 사용하는 것이 좋습니다.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

프린터 확장 성능 모범 사례

최상의 사용자 환경을 보장하기 위해 프린터 확장을 최대한 빨리 로드하도록 설계해야 합니다. 프린터 확장 샘플 프로젝트는 .NET 애플리케이션입니다. 즉, 런타임에 컴파일해야 하는 IL(중간 언어)에 기본 프로세서 아키텍처에 적합한 형식으로 빌드됩니다. 설치하는 동안 앱이 네이티브 시스템 아키텍처용으로 컴파일되었는지 확인하기 위해 모범 사례에 따라 프린터 확장을 설치하는 것이 좋습니다. 코드 컴파일 및 설치 모범 사례에 대한 자세한 내용은 데스크톱 애플리케이션의 시작 성능 향상을 참조 하세요.

또한 프린터 확장은 EnableEvents 메서드가 호출될 때까지 리소스 로드와 같은 초기화 작업을 연기할 것을 권장합니다. 이렇게 하면 프린터 확장에 대한 5초 제한 시간 이전에 앱이 EnableEvents를 호출할 가능성이 최소화됩니다.

OnDriverEvent 호출 후 프린터 확장은 UI를 초기화하고 가능한 한 빨리 그려 응답성을 보장하기 위해 가능한 비동기 메서드를 사용해야 합니다. 프린터 확장은 인쇄 기본 설정 또는 프린터 알림에 대한 초기 창 상태를 만들기 위해 네트워크 호출 또는 Bidi에 종속되지 않아야 합니다.

사용자가 PrintTicket에 영향을 주는 화면 UI를 사용하여 선택할 수 있으므로 프린터 확장은 가능한 한 빨리 변경 내용의 유효성을 검사하기 위해 IPrintSchemaTicket::ValidateAsync 메서드를 사용해야 합니다. 마지막으로 프린터 확장은 PrintTicket 변경 내용을 커밋하기 위해 IPrintSchemaTicket::CommitAsync 메서드를 사용해야 합니다.

프린터 확장은 호출된 프로세스에서 항상 프로세스에서 실행됩니다. 따라서 프린터 확장을 개발할 때 창 동작을 염두에 두어야 합니다.

  • IPrinterExtensionEventArgs WindowParent 속성은 앱을 호출한 창에 대한 핸들을 지정합니다.
  • IPrinterExtensionEventArgs WindowModal 속성은 프린터 확장(인쇄 기본 설정 모드)을 모달로 실행할지 여부를 지정합니다.

프린터 확장 샘플은 일반적으로 맨 위 창으로 시작되는 UI를 만드는 방법을 보여 줍니다. 그러나 일부 경우에는 UI를 호출한 프로세스가 다른 무결성 수준에서 실행되는 경우 또는 프로세스가 다른 프로세서 아키텍처에 대해 컴파일되는 경우와 같이 포그라운드에 UI가 표시되지 않습니다. 이 경우 프린터 확장은 FlashWindowEx를 호출하여 작업 표시줄에서 아이콘을 깜박임으로써 사용자에게 포그라운드에 올 수 있는 권한을 요청해야 합니다.

Bidi 요청 및 응답 스키마

데이터 바인딩 개요

데스크톱 애플리케이션의 시작 성능 향상

네이티브 이미지 생성기

스키마 인터페이스 인쇄

Windows 성능 분석 도구