Bagikan melalui


Ekstensi printer

Penting

Platform cetak modern adalah sarana komunikasi pilihan Windows dengan printer. Kami menyarankan agar Anda menggunakan driver kelas kotak masuk IPP Microsoft, bersama dengan Print Support Apps (PSA), untuk menyesuaikan pengalaman cetak di Windows 10 dan 11 untuk pengembangan perangkat printer.

Untuk informasi selengkapnya, lihat Panduan Desain Aplikasi Dukungan Cetak v1 dan v2.

Aplikasi ekstensi printer mendukung preferensi cetak dan pemberitahuan printer saat pengguna menjalankan aplikasi yang ada di desktop Windows.

Ekstensi printer dapat dibangun dalam bahasa yang mampu COM, tetapi dioptimalkan untuk dibangun menggunakan Microsoft .NET Framework 4. Ekstensi printer dapat didistribusikan dengan paket driver cetak, jika mampu XCopy dan tidak memiliki dependensi pada runtime eksternal selain yang disertakan dengan sistem operasi, misalnya, .NET. Jika aplikasi ekstensi printer tidak memenuhi kriteria ini, aplikasi tersebut dapat didistribusikan dalam paket setup.exe atau MSI, dan diiklankan dalam pengalaman Tahap Perangkat printer dengan menggunakan arahan PrinterExtensionUrl yang ditentukan dalam manifes v4. Ketika aplikasi ekstensi printer didistribusikan melalui paket MSI, Anda memiliki opsi untuk menambahkan driver cetak ke paket atau membiarkannya keluar dan mendistribusikan driver secara terpisah. PrinterExtensionUrl ditampilkan pada pengalaman preferensi printer.

Administrator TI memiliki beberapa opsi untuk mengelola distribusi ekstensi printer. Jika aplikasi dikemas dalam setup.exe atau MSI, maka administrator TI dapat menggunakan alat distribusi perangkat lunak standar seperti Microsoft Endpoint Configuration Manager, atau mereka dapat menyertakan aplikasi dalam gambar OS standar mereka. Administrator TI juga dapat mengambil alih PrinterExtensionUrl yang ditentukan dalam manifes v4, jika mereka mengedit HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers\<print queue name>\PrinterDriverData\PrinterExtensionUrl.

Dan jika perusahaan memilih untuk memblokir ekstensi printer sama sekali, ini dapat dilakukan melalui kebijakan grup yang disebut "Computer Configuration\Administrative Templates\Printers\Do not allow v4 printer drivers to show printer extension applications".

Membangun ekstensi printer

Saat Anda mengembangkan ekstensi printer, ada enam area utama fokus yang harus Anda waspadai. Area fokus ini diperlihatkan dalam daftar berikut.

  • Pendaftaran

  • Mengaktifkan Peristiwa

  • OnDriverEvent Handler

  • Preferensi Cetak

  • Pemberitahuan Printer

  • Mengelola Printer

Pendaftaran

Ekstensi printer terdaftar dengan sistem cetak dengan menentukan sekumpulan kunci registri atau dengan menentukan informasi aplikasi di bagian PrinterExtensions dari file manifes v4.

Ada GUID tertentu yang mendukung masing-masing titik masuk yang berbeda untuk ekstensi printer. Anda tidak perlu menggunakan GUID ini dalam file manifes v4, tetapi Anda harus mengetahui nilai GUID untuk menggunakan format registri untuk penginstalan driver v4. Tabel berikut ini memperlihatkan nilai GUID untuk dua titik entri.

Titik Entri GUID
Preferensi Cetak {EC8F261F-267C-469F-B5D6-3933023C29CC}
Pemberitahuan Printer {23BB1328-63DE-4293-915B-A6A23D929ACB}

Ekstensi printer yang dipasang di luar pengandar pencetak perlu didaftarkan menggunakan registri. Ini memastikan bahwa ekstensi printer dapat diinstal terlepas dari status penampung, atau modul konfigurasi v4 pada komputer klien.

Setelah layanan PrintNotify dimulai, layanan akan memeriksa kunci registri di bawah jalur [OfflineRoot] dan memproses pendaftaran atau unregistrasi yang tertunda. Setelah pendaftaran atau unregistrasi yang tertunda selesai, kunci registri akan dihapus secara real time. Jika Anda menggunakan skrip atau proses berulang untuk menempatkan kunci registri, Anda mungkin perlu membuat ulang kunci \[PrinterExtensionID] setiap kali Anda menentukan kunci \[PrinterDriverId]. Kunci yang tidak lengkap atau salah bentuk tidak dihapus.

Pendaftaran ini hanya diperlukan pada penginstalan pertama. Contoh berikut menunjukkan format kunci registri yang benar yang digunakan untuk mendaftarkan ekstensi printer.

Catatan

[OfflineRoot] digunakan sebagai singkatan untuk 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]

Misalnya, kumpulan kunci berikut akan mendaftarkan ekstensi printer dengan {PrinterExtensionIDGuid} PrinterExtensionID dan jalur yang sepenuhnya memenuhi syarat ke printer "C:\Program Files\Fabrikam\pe.exe" yang dapat dieksekusi untuk PrinterDriverID1Guid} dan {PrinterDriverID2Guid} PrinterDriverID, dengan alasan preferensi printer dan pemberitahuan printer.

[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"

Untuk menghapus instalan ekstensi printer yang sama, kumpulan kunci berikut harus ditentukan.

[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"

Karena ekstensi printer dapat berjalan dalam konteks yang diluncurkan pengguna dan konteks yang diluncurkan peristiwa, berguna untuk dapat menentukan konteks di mana ekstensi printer Anda beroperasi. Ini dapat memungkinkan aplikasi untuk, misalnya, tidak menghitung status pada semua antrean jika telah diluncurkan untuk pemberitahuan atau preferensi cetak. Microsoft menyarankan agar ekstensi printer yang diinstal secara terpisah dari driver (misalnya, dengan MSI atau setup.exe) harus menggunakan sakelar baris perintah baik pada pintasan menu Mulai, atau di entri AppPath yang diisi di registri selama pendaftaran. Karena ekstensi printer yang diinstal dengan pengandar diinstal ke DriverStore, ekstensi ini tidak akan diluncurkan di luar preferensi cetak atau peristiwa pemberitahuan printer. Oleh karena itu, menentukan sakelar baris perintah tidak didukung dalam kasus ini.

Ketika ekstensi printer mendaftar untuk PrinterDriverID saat ini, ekstensi printer harus menyertakan PrinterDriverID dalam AppPath. Misalnya, untuk aplikasi ekstensi printer dengan nama printerextension.exe, dan nilai PrinterDriverID {GUID}, [PrinterExtensionAppPath] akan terlihat seperti contoh berikut:

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

Mengaktifkan peristiwa

Pada runtime, ekstensi printer harus mengaktifkan pemicu peristiwa untuk PrinterDriverID saat ini. Ini adalah PrinterDriverID yang diteruskan ke aplikasi melalui array args[], dan memungkinkan sistem cetak untuk menyediakan konteks peristiwa yang sesuai untuk menangani alasan seperti preferensi cetak atau pemberitahuan printer.

Jadi aplikasi harus membuat PrinterExtensionManager baru untuk PrinterDriverID saat ini, mendaftarkan delegasi untuk menangani peristiwa OnDriverEvent, dan memanggil metode EnableEvents dengan PrinterDriverID. Cuplikan kode berikut mengilustrasikan pendekatan ini.

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

Jika aplikasi tidak memanggil EnableEvents dalam waktu 5 detik, Windows akan habis dan meluncurkan UI standar. Untuk mengurangi hal ini, ekstensi printer harus mengikuti praktik terbaik performa terbaru, termasuk yang berikut ini:

  • Tunda inisialisasi aplikasi sebanyak mungkin, sampai Anda memanggil EnableEvents. Setelah ini, prioritaskan respons UI dengan menggunakan metode asinkron dan tidak memblokir utas UI selama inisialisasi.

  • Gunakan ngen untuk menghasilkan gambar asli selama penginstalan. Untuk informasi selengkapnya, lihat Generator Gambar Asli.

  • Gunakan alat pengukuran performa untuk menemukan masalah performa saat memuat. Untuk informasi selengkapnya, lihat Alat Analisis Performa Windows.

Handler DriverEvent

Setelah handler OnDriverEvent terdaftar dan peristiwa diaktifkan, jika ekstensi printer diluncurkan untuk menangani preferensi cetak atau pemberitahuan printer, maka handler akan dipanggil. Dalam cuplikan kode sebelumnya, metode yang disebut OnDriverEvent didaftarkan sebagai penanganan aktivitas. Dalam cuplikan kode berikut, parameter PrinterExtensionEventArgs adalah objek yang memungkinkan preferensi cetak dan skenario pemberitahuan printer dibuat. PrinterExtensionEventArgs adalah pembungkus untuk 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.
    }
}

Untuk mencegah pengalaman pengguna buruk yang terkait dengan ekstensi printer crash atau lambat, Windows menerapkan batas waktu jika EnableEvents tidak dipanggil dalam waktu singkat setelah aplikasi diluncurkan. Untuk mengaktifkan penelusuran kesalahan, batas waktu ini dinonaktifkan jika ada debugger yang dilampirkan ke layanan PrintNotify.

Namun, dalam kebanyakan kasus, semua kode terkait aplikasi tempat kami tertarik, berjalan selama atau setelah panggilan balik OnDriverEvent. Selama pengembangan, mungkin juga berguna untuk menampilkan MessageBox sebelum memulai preferensi cetak atau pengalaman pemberitahuan printer dari panggilan balik OnDriverEvent. Saat Kotak Pesan muncul, kembali ke Visual Studio dan pilih Lampirkan Debug>ke Proses dan pilih nama proses Anda. Terakhir, kembali ke Kotak Pesan Anda dan pilih OK untuk melanjutkan. Ini akan memastikan bahwa Anda melihat pengecualian dan mencapai titik henti apa pun dari titik tersebut dan seterusnya.

ReasonIds baru dapat didukung di masa mendatang. Akibatnya, ekstensi printer harus secara eksplisit memeriksa ReasonID dan tidak boleh menggunakan pernyataan "else" untuk mendeteksi ReasonID terakhir yang diketahui. Jika ReasonID diterima dan tidak diketahui, aplikasi harus keluar dengan baik.

Preferensi cetak didorong oleh objek PrintSchemaEventArgs.Ticket. Objek ini merangkum dokumen PrintTicket dan PrintCapabilities yang menjelaskan fitur dan opsi untuk perangkat. Meskipun XML yang mendasar juga tersedia, model objek membuat bekerja dengan format ini lebih mudah.

Di dalam setiap objek IPrintSchemaTicket atau IPrintSchemaCapabilities ada fitur (IPrintSchemaFeature) dan opsi (IPrintSchemaOption). Meskipun antarmuka yang digunakan untuk fitur dan opsi sama terlepas dari asalnya, perilaku sedikit bervariasi sebagai akibat dari XML yang mendasarinya. Misalnya, dokumen PrintCapabilities menentukan banyak opsi per fitur, sementara dokumen PrintTicket hanya menentukan opsi yang dipilih (atau default). Demikian pula, dokumen PrintCapabilities menentukan string tampilan yang dilokalkan, sedangkan dokumen PrintTicket tidak.

Untuk informasi selengkapnya tentang pengikatan data di WPF, lihat Gambaran Umum Pengikatan Data.

Untuk memaksimalkan performa, Microsoft merekomendasikan bahwa panggilan ke GetPrintCapabilities hanya boleh dilakukan ketika perlu memperbarui dokumen PrintCapabilities.

Saat pengguna membuat pilihan menggunakan kontrol ComboBox terikat data, objek PrintTicket diperbarui secara otomatis. Ketika pengguna akhirnya mengklik OK, rantai validasi dan penyelesaian asinkron dimulai. Pola asinkron ini digunakan secara ekstensif untuk mencegah tugas jangka panjang terjadi pada utas UI dan menyebabkan macet di UI preferensi cetak atau aplikasi yang sedang dicetak. Berikut ini adalah daftar langkah-langkah yang digunakan untuk memproses perubahan PrintTicket setelah pengguna mengklik OK.

  1. PrintSchemaTicket divalidasi secara asinkron menggunakan metode IPrintSchemaTicket::ValidateAsync.

  2. Ketika validasi asinkron selesai, Common Language Runtime (CLR) memanggil metode PrintTicketValidateCompleted.

    1. Jika validasi berhasil, ia memanggil metode CommitPrintTicketAsync, dan CommitPrintTicketAsync memanggil metode IPrintSchemaTicket::CommitAsync. Dan ketika pembaruan PrintTicket berhasil diselesaikan, ini memanggil metode PrintTicketCommitCompleted, yang memanggil metode kenyamanan yang memanggil metode PrinterExtensionEventArgs.Request.Complete untuk menunjukkan bahwa preferensi cetak selesai, dan kemudian menutup aplikasi.

    2. Jika tidak, ia menyajikan UI kepada pengguna untuk menangani situasi batasan.

Jika pengguna mengklik batalkan atau menutup jendela preferensi cetak secara langsung, ekstensi printer memanggil IPrinterExtensionEventArgs.Request.Cancel dengan nilai dan pesan HRESULT yang sesuai untuk log kesalahan.

Jika proses untuk ekstensi printer telah ditutup dan tidak disebut metode Selesai atau Batal seperti yang dijelaskan dalam paragraf sebelumnya, maka sistem cetak akan secara otomatis kembali menggunakan UI yang disediakan Microsoft.

Untuk mengambil informasi status perangkat, ekstensi printer dapat menggunakan Bidi untuk mengkueri perangkat cetak. Misalnya, untuk menampilkan status tinta atau jenis status lain tentang perangkat, ekstensi printer dapat menggunakan metode IPrinterExtensionEventArgs.PrinterQueue.SendBidiQuery untuk mengeluarkan kueri Bidi ke perangkat. Mendapatkan status Bidi terbaru adalah proses dua langkah yang melibatkan pengaturan penanganan aktivitas untuk peristiwa OnBidiResponseReceived, dan memanggil metode SendBidiQuery dengan kueri Bidi yang valid. Cuplikan kode berikut menunjukkan proses dua langkah ini.

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

Ketika respons Bidi diterima, penanganan aktivitas berikut dipanggil. Penanganan aktivitas ini juga memiliki implementasi status tinta yang ditiru, yang mungkin berguna untuk pengembangan ketika perangkat tidak tersedia. Objek PrinterQueueEventArgs menyertakan respons XML HRESULT dan Bidi. Untuk informasi selengkapnya tentang respons XML Bidi, lihat Skema Permintaan dan Respons 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)";
}

Pemberitahuan printer

Pemberitahuan printer dipanggil dengan cara yang sama persis seperti preferensi cetak. Di handler OnDriverEvent, jika IPrinterExtensionEventArgs menunjukkan bahwa ReasonID cocok dengan GUID DriverEvents, maka kita dapat membangun pengalaman untuk menangani peristiwa ini.

Variabel berikut ini paling membantu dalam menangani pengalaman pemberitahuan printer fungsi.

  • PrinterExtensionEventArgs.BidiNotification – Ini membawa XML Bidi yang menyebabkan peristiwa dipicu.

  • PrinterExtensionEventArgs.DetailedReasonId – Ini berisi GUID eventID dari file xml peristiwa driver.

Atribut terpenting dari objek IPrinterExtensionEventArgs untuk pemberitahuan adalah properti BidiNotification. Ini membawa XML Bidi yang menyebabkan peristiwa dipicu. Untuk informasi selengkapnya tentang respons XML Bidi, lihat Skema Permintaan dan Respons Bidi.

Mengelola printer

Untuk mendukung peran ekstensi printer sebagai aplikasi yang dapat digunakan sebagai hub untuk mengelola/memelihara printer, Anda dapat menghitung antrean cetak tempat ekstensi printer saat ini didaftarkan, dan mendapatkan status untuk setiap antrean. Ini tidak ditunjukkan dalam proyek PrinterExtensionSample, tetapi cuplikan kode berikut dapat ditambahkan ke dalam metode Utama App.xaml.cs untuk mendaftarkan penanganan aktivitas.

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

Setelah antrean dijumlahkan, penanganan aktivitas dipanggil dan operasi status dapat berlangsung. Kejadian ini diaktifkan secara berkala selama masa pakai aplikasi untuk memastikan bahwa daftar antrean cetak yang dijumlahkan adalah saat ini, bahkan jika pengguna telah menginstal lebih banyak antrean sejak dibuka. Akibatnya, penting bahwa penanganan aktivitas tidak membuat jendela baru setiap kali dijalankan, dan ini ditampilkan dalam cuplikan kode berikut.

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

Untuk melakukan tugas pemeliharaan menggunakan ekstensi printer, Microsoft merekomendasikan agar API WritePrinter warisan digunakan sebagaimana diuraikan oleh kode pseudo berikut.

OpenPrinter
    StartDocPrinter
        StartPagePrinter
          WritePrinter
        EndPagePrinter
    EndDocPrinter
ClosePrinter

Praktik terbaik performa ekstensi printer

Untuk memastikan pengalaman pengguna terbaik, ekstensi printer harus dirancang untuk memuat secepat mungkin. Proyek Sampel Ekstensi Printer adalah aplikasi .NET, yang berarti bahwa ia dibangun ke dalam bahasa perantara (IL) yang harus dikompilasi pada runtime ke dalam format yang sesuai untuk arsitektur prosesor asli. Selama penginstalan, Microsoft merekomendasikan agar ekstensi printer diinstal sesuai dengan praktik terbaik, untuk memastikan bahwa aplikasi telah dikompilasi untuk arsitektur sistem asli. Untuk informasi selengkapnya tentang kompilasi kode dan praktik terbaik penginstalan, lihat Meningkatkan Performa Peluncuran untuk Aplikasi Desktop Anda.

Microsoft juga merekomendasikan agar ekstensi printer menunda tugas inisialisasi seperti memuat sumber daya hingga setelah metode EnableEvents dipanggil. Ini meminimalkan kemungkinan aplikasi memanggil EnableEvents sebelum batas waktu 5 detik untuk ekstensi printer.

Setelah panggilan OnDriverEvent, ekstensi printer harus menginisialisasi UI mereka dan menggambar secepat mungkin, memanfaatkan metode asinkron jika memungkinkan untuk memastikan responsivitas. Ekstensi printer seharusnya tidak memiliki dependensi pada panggilan jaringan atau Bidi untuk membuat status jendela awal untuk preferensi cetak atau pemberitahuan printer.

Karena pengguna membuat pilihan menggunakan UI pada layar yang memengaruhi PrintTicket, ekstensi printer harus menggunakan metode IPrintSchemaTicket::ValidateAsync untuk memvalidasi perubahan sedini mungkin. Terakhir, ekstensi printer harus menggunakan metode IPrintSchemaTicket::CommitAsync untuk menerapkan perubahan PrintTicket.

Ekstensi printer selalu dijalankan di luar proses dari proses yang memanggilnya. Jadi Anda harus mengingat perilaku jendela saat mengembangkan ekstensi printer:

Sampel Ekstensi Printer menunjukkan cara membuat UI yang umumnya diluncurkan sebagai jendela paling atas. Tetapi dalam beberapa kasus, UI tidak akan ditampilkan di latar depan, seperti ketika proses yang menyebabkan UI dipanggil berjalan pada tingkat integritas yang berbeda, atau ketika proses dikompilasi untuk arsitektur prosesor yang berbeda. Dalam hal ini, ekstensi printer harus memanggil FlashWindowEx untuk meminta izin pengguna untuk datang ke latar depan dengan mem-flash ikon di taskbar.

Skema Permintaan dan Respons Bidi

Gambaran Umum Pengikatan Data

Meningkatkan Performa Peluncuran untuk Aplikasi Desktop Anda

Generator Gambar Asli

Cetak Antarmuka Skema

Alat Analisis Performa Windows