如何在 UWP 裝置應用程式中顯示印表機狀態

在 Windows 8.1 中,使用者可以從 UWP 裝置應用程式的新式 UI 檢查其印表機狀態。 本主題使用列印設定和列印通知範例的 C# 版本來示範如何查詢印表機狀態並顯示它。 若要深入瞭解一般 UWP 裝置應用程式,請參閱 滿足 UWP 裝置應用程式

印表設定和列印通知範例的 C# 版本會使用 InkLevel.xaml 頁面來示範如何取得印表機狀態(在此案例中為筆跡層級),並加以顯示。 列印協助程式類別可用來建立裝置內容(IPrinterExtensionContext),並執行裝置查詢。 PrinterHelperClass.cs檔案位於 DeviceAppForPrintersLibrary 專案中,並使用 PrinterExtensionLibrary 專案中定義的 API。 印表機延伸模組連結庫提供方便的方式來存取 v4 印表驅動程式的印表機延伸模組介面。 如需詳細資訊,請參閱 印表機擴充功能庫概觀

注意

本主題中顯示的程式代碼範例是以列印設定和列印通知範例的 C# 版本為基礎。 此範例也適用於 JavaScript 和 C++。 請注意,因為 C++ 可以直接存取 COM,因此範例的 C++ 版本不包含程式碼庫專案。 下載範例以查看最新版本的程序代碼。

必要條件

開始之前:

  1. 請確定您的印表機是使用 v4 印表驅動程式安裝。 如需詳細資訊,請參閱 開發 v4 列印驅動程式

  2. 設定您的開發電腦。 如需下載工具及建立開發人員帳戶的相關信息,請參閱 開始使用

  3. 將您的應用程式與市集產生關聯。 如需相關信息,請參閱 步驟 1:建立 UWP 裝置應用程式

  4. 為您的印表機建立裝置元數據,使其與您的應用程式產生關聯。 如需詳細資訊,請參閱 步驟 2:建立裝置元數據

  5. 如果您要撰寫使用 C# 或 JavaScript 撰寫應用程式,請將 PrinterExtensionLibraryDeviceAppForPrintersLibrary 專案新增至 UWP 裝置應用程式解決方案。 您可以在 [列印設定] 和 [列印通知] 範例中找到這些專案。

    注意

    因為 C++ 可以直接存取 COM,C++ 應用程式不需要個別的連結庫才能使用以 COM 為基礎的印表機裝置內容。

步驟 1:尋找印表機

在建立裝置內容之前,應用程式必須判斷印表機的裝置識別碼。 若要這樣做,此範例會使用 EnumerateAssociatedPrinters 方法來搜尋連接至計算機的所有印表機。 然後,它會比較每個容器的 PackageFamilyName 屬性,檢查每個印表機的容器,並尋找關聯。

注意

您可以在 Microsoft Visual Studio 指令清單設計工具的 [封裝] 索引卷標下找到與應用程式相關聯的裝置 System.Devices.AppPackageFamilyName。

此範例顯示 EnumerateAssociatedPrinters 來自 InkLevel.xaml.cs 檔案的 方法:

async void EnumerateAssociatedPrinters(object sender, RoutedEventArgs e)
{
    // Reset output text and associated printer array.
    AssociatedPrinters.Items.Clear();
    BidiOutput.Text = "";

    // GUID string for printers.
    string printerInterfaceClass = "{0ecef634-6ef0-472a-8085-5ad023ecbccd}";
    string selector = "System.Devices.InterfaceClassGuid:=\"" + printerInterfaceClass + "\"";

    // By default, FindAllAsync does not return the containerId for the device it queries.
    // We have to add it as an additional property to retrieve. 
    string containerIdField = "System.Devices.ContainerId";
    string[] propertiesToRetrieve = new string[] { containerIdField };

    // Asynchronously find all printer devices.
    DeviceInformationCollection deviceInfoCollection = await DeviceInformation.FindAllAsync(selector, propertiesToRetrieve);

    // For each printer device returned, check if it is associated with the current app.
    for (int i = 0; i < deviceInfoCollection.Count; i++)
    {
        DeviceInformation deviceInfo = deviceInfoCollection[i];
        FindAssociation(deviceInfo, deviceInfo.Properties[containerIdField].ToString());
    }
}

FindAssociation由呼叫EnumerateAssociatedPrinters的方法會檢查印表機是否與目前應用程式相關聯。 換句話說,此方法會檢查應用程式是否為 UWP 裝置應用程式。 當應用程式與印表機定義於本機電腦上的裝置元數據時,就會有此關聯。

此範例顯示 FindAssociation 來自 InkLevel.xaml.cs 檔案的 方法:

async void FindAssociation(DeviceInformation deviceInfo, string containerId)
{

    // Specifically telling CreateFromIdAsync to retrieve the AppPackageFamilyName. 
    string packageFamilyName = "System.Devices.AppPackageFamilyName";
    string[] containerPropertiesToGet = new string[] { packageFamilyName };

    // CreateFromIdAsync needs braces on the containerId string.
    string containerIdwithBraces = "{" + containerId + "}";

    // Asynchronously getting the container information of the printer.
    PnpObject containerInfo = await PnpObject.CreateFromIdAsync(PnpObjectType.DeviceContainer, containerIdwithBraces, containerPropertiesToGet);

    // Printers could be associated with other device apps, only the ones with package family name
    // matching this app's is associated with this app. The packageFamilyName for this app will be found in this app's packagemanifest
    string appPackageFamilyName = "Microsoft.SDKSamples.DeviceAppForPrinters.CS_8wekyb3d8bbwe";
    var prop = containerInfo.Properties;

    // If the packageFamilyName of the printer container matches the one for this app, the printer is associated with this app.
    string[] packageFamilyNameList = (string[])prop[packageFamilyName];
    if (packageFamilyNameList != null)
    {
        for (int j = 0; j < packageFamilyNameList.Length; j++)
        {
            if (packageFamilyNameList[j].Equals(appPackageFamilyName))
            {
                AddToList(deviceInfo);
            }
        }
    }
}

找到關聯時, FindAssociation 方法會使用 AddToList 方法,將裝置標識元新增至相關聯的裝置標識符清單。 這些標識符會儲存在名為 AssociatedPrinters的 ComboBox 中。

此範例顯示 AddToList 來自 InkLevel.xaml.cs 檔案的 方法:

void AddToList(DeviceInformation deviceInfo)
{
    // Creating a new display item so the user sees the friendly name instead of the interfaceId.
    ComboBoxItem item = new ComboBoxItem();
    item.Content = deviceInfo.Properties["System.ItemNameDisplay"] as string;
    item.DataContext = deviceInfo.Id;
    AssociatedPrinters.Items.Add(item);

    // If this is the first printer to be added to the combo box, select it.
    if (AssociatedPrinters.Items.Count == 1)
    {
        AssociatedPrinters.SelectedIndex = 0;
    }
}

步驟 2:顯示狀態

方法 GetInkStatus 會使用異步事件型模式,從印表機要求資訊。 這個方法會使用相關聯的裝置標識碼來取得可用來取得裝置狀態的裝置內容。 對方法的 printHelper.SendInkLevelQuery() 呼叫會起始裝置查詢。 當回應傳回時, OnInkLevelReceived 會呼叫 方法並更新UI。

注意

此 C# 範例遵循與 JavaScript 範例不同的模式,因為 C# 可讓您將發送器傳送至 PrintHelperClass,以便將事件訊息張貼回 UI 線程。

此範例顯示GetInkStatus來自 InkLevel.xaml.cs 檔案的 OnInkLevelReceived 方法:

void GetInkStatus(object sender, RoutedEventArgs e)
{
    if (AssociatedPrinters.Items.Count > 0)
    {
        // Get the printer that the user has selected to query.
        ComboBoxItem selectedItem = AssociatedPrinters.SelectedItem as ComboBoxItem;

        // The interfaceId is retrieved from the detail field.
        string interfaceId = selectedItem.DataContext as string;

        try
        {
            // Unsubscribe existing ink level event handler, if any.
            if (printHelper != null)
            {
                printHelper.OnInkLevelReceived -= OnInkLevelReceived;
                printHelper = null;
            }

            object context = Windows.Devices.Printers.Extensions.PrintExtensionContext.FromDeviceId(interfaceId);printHelper.SendInkLevelQuery()

            // Use the PrinterHelperClass to retrieve the bidi data and display it.
            printHelper = new PrintHelperClass(context);
            try
            {
                printHelper.OnInkLevelReceived += OnInkLevelReceived;
                printHelper.SendInkLevelQuery();

                rootPage.NotifyUser("Ink level query successful", NotifyType.StatusMessage);
            }
            catch (Exception)
            {
                rootPage.NotifyUser("Ink level query unsuccessful", NotifyType.ErrorMessage);
            }
        }
        catch (Exception)
        {
            rootPage.NotifyUser("Error retrieving PrinterExtensionContext from InterfaceId", NotifyType.ErrorMessage);
        }
    }
}

private void OnInkLevelReceived(object sender, string response)
{
    BidiOutput.Text = response;
}

列印協助程式類別會負責將 Bidi 查詢傳送至裝置並接收回應。

此範例顯示 SendInkLevelQuery 來自 PrintHelperClass.cs 檔案的 方法和其他方法。 請注意,這裡只會顯示一些列印協助程序類別方法。 下載列印設定和列印通知範例,以查看完整的程序代碼。

public void SendInkLevelQuery()
{
    printerQueue.OnBidiResponseReceived += OnBidiResponseReceived;

    // Send the query.
    string queryString = "\\Printer.Consumables";
    printerQueue.SendBidiQuery(queryString);
}

private void OnBidiResponseReceived(object sender, PrinterQueueEventArgs responseArguments)
{
    // Invoke the ink level event with appropriate data.
    dispatcher.RunAsync(
        Windows.UI.Core.CoreDispatcherPriority.Normal,
        () =>
        {
            OnInkLevelReceived(sender, ParseResponse(responseArguments));
        });
}

private string ParseResponse(PrinterQueueEventArgs responseArguments)
{
    if (responseArguments.StatusHResult == (int)HRESULT.S_OK)
        return responseArguments.Response;
    else
        return InvalidHResult(responseArguments.StatusHResult);
}

private string InvalidHResult(int result)
{
    switch (result)
    {
        case unchecked((int)HRESULT.E_INVALIDARG):
            return "Invalid Arguments";
        case unchecked((int)HRESULT.E_OUTOFMEMORY):
            return "Out of Memory";
        case unchecked((int)HRESULT.ERROR_NOT_FOUND):
            return "Not found";
        case (int)HRESULT.S_FALSE:
            return "False";
        case (int)HRESULT.S_PT_NO_CONFLICT:
            return "PT No Conflict";
        default:
            return "Undefined status: 0x" + result.ToString("X");
    }
}

測試

您必須先使用裝置元數據連結到印表機,才能測試 UWP 裝置應用程式。

您需要印表機的裝置元數據套件複本,才能將裝置應用程式資訊新增至該套件。 如果您沒有裝置元數據,您可以使用裝置元數據撰寫精靈來建置它,如步驟 2:為您的 UWP 裝置應用程式建立裝置元數據主題中所述。

注意

若要使用 裝置元數據撰寫精靈,您必須先安裝 Microsoft Visual Studio Professional、Microsoft Visual Studio Ultimate 或 適用於 Windows 8.1 的獨立 SDK,才能完成本主題中的步驟。 安裝 Microsoft Visual Studio Express for Windows 會安裝不包含精靈的 SDK 版本。

下列步驟會建置您的應用程式並安裝裝置元數據。

  1. 啟用測試簽署。

    1. 按兩下 DeviceMetadataWizard.exe,從 %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 啟動裝置元數據撰寫精靈

    2. 從 [ 工具] 功能表中,選取 [ 啟用測試簽署]。

  2. 重新啟動電腦

  3. 開啟方案 (.sln) 檔案來建置方案。 按 F7,或從範例載入後,從頂端功能表移至 [建>置方案 ]。

  4. 中斷連線並卸載印表機。 需要此步驟,Windows 會在下次偵測到裝置時讀取更新的裝置元數據。

  5. 編輯並儲存裝置元數據。 若要將裝置應用程式連結至您的裝置,您必須將裝置應用程式與裝置產生關聯。

    注意

    如果您尚未建立裝置元數據,請參閱 步驟 2:建立 UWP 裝置應用程式的裝置元數據。

    1. 如果裝置元數據撰寫精靈尚未開啟,請按兩下DeviceMetadataWizard.exe,從 %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 加以啟動。

    2. 按兩下 [ 編輯裝置元數據]。 這可讓您編輯現有的裝置元數據套件。

    3. 在 [ 開啟 ] 對話框中,找出與您的 UWP 裝置應用程式相關聯的裝置元數據套件。 (它有 devicemetadata-ms 擴展名。)

    4. 在 [ 指定 UWP 裝置應用程式資訊 ] 頁面上,於 [UWP 裝置應用程式] 方塊中 輸入 Microsoft Store 應用程式 資訊。 按兩下 [匯入 UWP 應用程式指令清單檔案],自動輸入套件名稱發行者名稱和 UWP 應用程式識別碼

    5. 如果您的 app 正在註冊印表機通知,請填寫 [ 通知處理程式] 方塊 。 在 [事件標識符] 中,輸入列印事件處理程序的名稱。 在 [事件資產] 中,輸入該程序代碼所在的檔名。

    6. 完成時,請按 [下一步] ,直到到達 [完成 ] 頁面為止。

    7. 在 [ 檢閱裝置元數據套件 ] 頁面上,確定所有設定都正確,然後選取 [ 將裝置元數據套件複製到本機計算機上的 元數據存放區] 複選框。 然後按一下 [儲存] 。

  6. 重新連線印表機,讓 Windows 在裝置連線時讀取更新的裝置元數據。

疑難排解

問題:列舉與應用程式相關聯的裝置時找不到印表機

如果列舉相關聯的印表機時找不到印表機:

  • 可能的原因: 測試簽署未開啟。 如需開啟偵錯的相關信息,請參閱本主題中的偵錯一節。

  • 可能的原因: 應用程式未查詢正確的套件系列名稱。 檢查程式代碼中的套件系列名稱。 在 Microsoft Visual Studio 中開啟 package.appxmanifest,並確定您要查詢的套件系列名稱符合 [封裝] 索引卷標的 [套件系列名稱] 欄位中的套件系列名稱。

  • 可能的原因: 裝置元數據未與套件系列名稱相關聯。 使用 [ 裝置元數據撰寫精靈 ] 開啟裝置元數據,並檢查套件系列名稱。 按兩下DeviceMetadataWizard.exe,從 %ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 啟動精靈

問題:找到與應用程式相關聯的印表機,但無法查詢 Bidi 資訊

如果在列舉相關聯的印表機時找到印表機,但 Bidi 查詢會傳回錯誤...

  • 可能的原因: 套件系列名稱錯誤。 檢查程式代碼中的套件系列名稱。 在 Visual Studio 中開啟 package.appxmanifest,並確定您要查詢的套件系列名稱符合 [封裝] 索引卷標的 [套件系列名稱] 字段中的套件系列名稱。

  • 可能的原因: 印表機是使用 v3 印表機安裝,而不是 v4 印表機。 若要查看已安裝的版本,請開啟 PowerShell 並輸入下列命令:

    get-printer | Select Name, {(get-printerdriver -Name $_.DriverName).MajorVersion}
    

開發 v4 列印驅動程式

印表機延伸模組介面 (v4 印表驅動程式)

雙向通訊

開始使用UWP應用程式

建立 UWP 裝置應用程式 (逐步指南)

建立 UWP 裝置應用程式的裝置元資料(逐步指南)