如何在 UWP 设备应用中执行打印机维护

在 Windows 8.1 中,UWP 设备应用可以执行打印机维护,例如对齐打印头和清洁喷头。 本主题使用 C# 版本的打印作业管理和打印机维护示例来演示如何使用双向通信 (Bidi) 来执行此类设备维护。 要了解有关 UWP 设备应用的一般详细信息,请参阅初识 UWP 设备应用

C# 版本的打印作业管理和打印机维护示例演示了使用 DeviceAppForPrinters2 项目中 DeviceMaintenance.xaml.cs 文件的打印机维护。 为了使用 Bidi,该示例使用 PrinterExtensionLibrary 项目中的打印机扩展库。 打印机扩展库提供了一种访问 v4 打印驱动程序的打印机扩展接口的便捷方法。 有关详细信息,请参阅打印机扩展库概述

注意

本主题中显示的代码示例基于 C# 版本的打印作业管理和打印机维护示例。 此示例还有 JavaScript 和 C++ 版本。 请注意,由于 C++ 可以直接访问 COM,因此该示例的 C++ 版本不包括代码库项目。 下载示例以查看最新版本的代码。

打印机维护

Windows 8.1 在 v4 打印机驱动程序中引入了新的打印机扩展接口,你可以使用这些接口来实现设备维护:IPrinterBidiSetRequestCallbackIPrinterExtensionAsyncOperationIPrinterQueue2。 这些接口可以异步将 Bidi 请求发送到端口监视器,以便将其转换为设备和协议特定的命令,然后将其发送到打印机。 有关详细信息,请参阅设备维护(v4 打印机驱动程序)

提示

C# 和 JavaScript 应用不能直接使用 COM API。 如果要编写 C# 或 JavaScript UWP 设备应用,请使用打印机扩展库访问这些接口(如本主题所示)。

先决条件

准备工作:

  1. 请确保使用 v4 打印驱动程序安装打印机。 有关详细信息,请参阅开发 v4 打印驱动程序

  2. 设置你的开发电脑。 有关下载工具和创建开发人员帐户的信息,请参阅入门

  3. 将应用与商店相关联。 请参阅创建 UWP 设备应用以获取相关信息。

  4. 为你的打印机创建设备元数据,将其与你的应用关联起来。 请参阅创建设备元数据以了解详细信息。

  5. 为你的应用的主页构建 UI。 所有 UWP 设备应用都可以从“开始”启动,并以全屏方式显示。 使用“开始”体验,以符合设备特定品牌和功能的方式突出你的产品或服务。 它可以使用的 UI 控件类型没有特殊限制。 要开始设计全屏体验,请参阅 Microsoft Store 设计原则

  6. 如果要使用 C# 或 JavaScript 编写应用,请将 PrinterExtensionLibrary 项目添加到你的 UWP 设备应用解决方案。 你可以在打印作业管理和打印机维护示例中找到此项目。

注意

由于 C++ 可以直接访问 COM,因此 C++ 应用不需要单独的库来处理基于 COM 的打印机设备上下文。

步骤 1:准备 Bidi 请求

设备维护接口要求你的 Bidi 请求是字符串形式的 XML 数据。 你可以在应用中任何合理的地方构建 Bidi 请求。 例如,你可以将 Bidi 请求保存为字符串常量,或根据用户输入动态创建。 打印作业管理和打印机维护示例在方法中 OnNavigatedTo 构造默认请求。 有关 Bidi 的详细信息,请参阅双向通信

此示例来自 OnNavigatedToDeviceMaintenance.xaml.cs 文件的 方法。

string defaultBidiQuery =
    "<bidi:Set xmlns:bidi=\"http://schemas.microsoft.com/windows/2005/03/printing/bidi\">\r\n" +
    "    <Query schema='\\Printer.Maintenance:CleanHead'>\r\n" +
    "        <BIDI_BOOL>false</BIDI_BOOL>\r\n" +
    "    </Query>\r\n" +
    "</bidi:Set>";

步骤 2:查找打印机

应用必须先找到打印机,然后应用才能将命令发送到打印机。 为此,打印作业管理和打印机维护 示例包括一个名为 PrinterEnumeration 的类(在 PrinterEnumeration.cs 文件中)。 此类通过设备元数据查找与应用关联的所有打印机,并返回一个 PrinterInfo 对象列表,其中包含每个打印机的名称和设备 ID。

此示例来自 EnumeratePrinters_ClickDeviceMaintenance.xaml.cs 文件的 方法。 它演示示例如何使用 PrinterEnumeration 类获取关联的打印机列表。

private async void EnumeratePrinters_Click(object sender, RoutedEventArgs e)
{
    try
    {
        rootPage.NotifyUser("Enumerating printers. Please wait", NotifyType.StatusMessage);

        // Retrieve the running app's package family name, and enumerate associated printers.
        string currentPackageFamilyName = Windows.ApplicationModel.Package.Current.Id.FamilyName;

        // Enumerate associated printers.
        PrinterEnumeration pe = new PrinterEnumeration(currentPackageFamilyName);
        List<PrinterInfo> associatedPrinters = await pe.EnumeratePrintersAsync();

        // Update the data binding source on the combo box that displays the list of printers.
        PrinterComboBox.ItemsSource = associatedPrinters;
        if (associatedPrinters.Count > 0)
        {
            PrinterComboBox.SelectedIndex = 0;
            rootPage.NotifyUser(associatedPrinters.Count + " printers enumerated", NotifyType.StatusMessage);
        }
        else
        {
            rootPage.NotifyUser(DisplayStrings.NoPrintersEnumerated, NotifyType.ErrorMessage);
        }
    }
    catch (Exception exception)
    {
        rootPage.NotifyUser("Caught an exception: " + exception.Message, NotifyType.ErrorMessage);
    }
}

提示

有关 PrinterEnumerationPrinterInfo 类的详细信息,请参阅 PrinterEnumeration.cs 文件。

步骤 3:发送 Bidi 请求

要发送 Bidi 请求,设备维护接口需要 Bidi 字符串和回调。 在 SendBidiRequest_Click 方法中,示例首先使用对象 PrinterInfo 创建名为 context 的打印机扩展上下文对象。 然后创建一个 PrinterBidiSetRequestCallback 对象,并添加一个事件处理程序来处理回调 OnBidiResponseReceived 的事件。 最后,使用打印机扩展上下文的 SendBidiSetRequestAsync 方法发送 Bidi 字符串和回调。

此示例来自 SendBidiRequest_ClickDeviceMaintenance.xaml.cs 文件的 方法。

private void SendBidiRequest_Click(object sender, RoutedEventArgs e)
{
    try
    {
        PrinterInfo queue = (PrinterInfo)PrinterComboBox.SelectedItem;

        // Retrieve a COM IPrinterExtensionContext object, using the static WinRT factory.
        // Then instantiate one "PrinterExtensionContext" object that allows operations on the COM object.
        Object comContext = Windows.Devices.Printers.Extensions.PrintExtensionContext.FromDeviceId(queue.DeviceId);
        PrinterExtensionContext context = new PrinterExtensionContext(comContext);

        // Create an instance of the callback object, and perform an asynchronous 'bidi set' operation.
        PrinterBidiSetRequestCallback callback = new PrinterBidiSetRequestCallback();

        // Add an event handler to the callback object's OnBidiResponseReceived event.
        // The event handler will be invoked once the Bidi response is received.
        callback.OnBidiResponseReceived += OnBidiResponseReceived;

        // Send the Bidi "Set" query asynchronously.
        IPrinterExtensionAsyncOperation operationContext
            = context.Queue.SendBidiSetRequestAsync(BidiQueryInput.Text, callback);

        // Note: The 'operationContext' object can be used to cancel the operation if required.
    }
    catch (Exception exception)
    {
        rootPage.NotifyUser("Caught an exception: " + exception.Message, NotifyType.ErrorMessage);
    }
}

步骤 4:接收 Bidi 响应

当 Bidi “set” 操作完成时,将调用类型为的 PrinterBidiSetRequestCallback 回调对象。 此回调负责 HRESULT 响应中的错误处理,然后触发 OnBidiResponseReceived 事件,并通过事件参数发送 Bidi 响应。

此示例显示 DeviceMaintenance.xaml.cs 文件中的 PrinterBidiSetRequestCallback 类定义。

internal class PrinterBidiSetRequestCallback : IPrinterBidiSetRequestCallback
{
    /// <summary>
    /// This method is invoked when the asynchronous Bidi "Set" operation is completed.
    /// </summary>
    public void Completed(string response, int statusHResult)
    {
        string result;

        if (statusHResult == (int)HRESULT.S_OK)
        {
            result = "The response is \r\n" + response;
        }
        else
        {
            result = "The HRESULT received is: 0x" + statusHResult.ToString("X") + "\r\n" +
                     "No Bidi response was received";
        }

        // Invoke the event handlers when the Bidi response is received.
        OnBidiResponseReceived(null, result);
    }

    /// <summary>
    /// This event will be invoked when the Bidi 'set' response is received.
    /// </summary>
    public event EventHandler<string> OnBidiResponseReceived;
}

然后,Bidi 响应将发送到 OnBidiResponseReceived 方法,其中 Dispatcher 用于在 UI 线程上显示结果。

此示例来自 OnBidiResponseReceivedDeviceMaintenance.xaml.cs 文件的 方法。

internal async void OnBidiResponseReceived(object sender, string bidiResponse)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        BidiResponseOutput.Text = bidiResponse;
    });
}

测试

在测试 UWP 设备应用之前,必须使用设备元数据将其链接到你的打印机。

你需要一份打印机的设备元数据包副本,以便在其中添加设备应用信息。 如果没有设备元数据,可以使用设备元数据创作向导构建,如主题为 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. 编辑和保存设备元数据。 要将设备应用链接到你的设备,必须将设备应用与你的设备相关联。 注意:如果尚未创建设备元数据,请参阅为 UWP 设备应用创建设备元数据

    1. 如果设备元数据创作向导尚未打开,请双击 DeviceMetadataWizard.exe%ProgramFiles(x86)%\Windows Kits\8.1\bin\x86 将其打开。

    2. 单击“编辑设备元数据”。 这样,就可以编辑现有的设备元数据包。

    3. 在“打开”对话框中,找到与 UWP 设备应用关联的设备元数据包。 (它具有 devicemetadata-ms 文件扩展名。)

    4. 在“指定 UWP 设备应用信息”页上,在“UWP 设备应用”框中输入 Microsoft Store 应用信息。 单击“导入 UWP 应用清单文件”,自入“包名称”、“发布者名称”和“UWP 应用 ID”。

    5. 如果你的应用正在注册打印机通知,请填写“通知处理程序”框。 在”事件 ID“中,输入打印事件处理程序的名称。 在”事件资产“中,输入代码所在的文件的名称。

    6. 完成后,单击“下一步”,直到进入“完成”页。

    7. 在“查看设备元数据包”页上,确保所有设置都正确,并选择“将设备元数据包复制到本地计算机上的元数据存储”复选框中。 然后单击保存

  6. 重新连接打印机,以便 Windows 在设备连接时读取更新的设备元数据。

设备维护(v4 打印机驱动程序)

开发 v4 打印驱动程序

双向通信

UWP 应用入门

创建 UWP 设备应用(分步指南)

为 UWP 设备应用创建设备元数据(分步指南)