共用方式為


如何在 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 中的 Manifest 設計器的 [封裝] 索引標籤下,您可以找到與您的應用程式相關聯裝置的 System.Devices.AppPackageFamilyName。

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

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 中。

此範例顯示AddToListInkLevel.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 線程。

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

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 或 獨立 SDK for Windows 8.1,才能完成本主題中的步驟。 安裝 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 市集應用程式資訊。 按下 [匯入 UWP 應用程式指令清單檔],自動輸入 套件名稱發行者名稱UWP 應用程式識別碼

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

    6. 完成後,依次點擊 [下一步],直到到達 [完成] 頁面。

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

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

故障排除

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

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

  • 可能的原因: 測試簽署未開啟。 如需開啟偵錯的相關資訊,請參閱本主題中的偵錯部分。

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

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

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

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

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

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

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

開發 v4 列印驅動程式

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

雙向通訊

開始使用UWP app

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

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