UWP デバイス アプリでプリンターの状態を表示する方法

Windows 8.1 では、ユーザーは UWP デバイス アプリの最新 UI からプリンターの状態を確認できます。 このトピックでは、C# バージョンの印刷設定と印刷通知のサンプルを使用して、プリンターの状態を照会して表示する方法について説明します。 UWP デバイス アプリ全般の詳細については、「UWP デバイス アプリの概要」を参照してください。

C# バージョンの印刷設定と印刷通知のサンプルでは、InkLevel.xaml ページを使用して、プリンターの状態 (この場合はインク レベル) を取得して表示する方法を示します。 印刷ヘルパー クラスを使用して、デバイス コンテキスト (IPrinterExtensionContext) を作成し、デバイス クエリを実行します。 PrinterHelperClass.cs ファイルは DeviceAppForPrintersLibrary プロジェクト内にあり、PrinterExtensionLibrary プロジェクトで定義されている API を使用します。 プリンター拡張機能ライブラリは、v4 印刷ドライバーのプリンター拡張機能インターフェイスにアクセスするための便利な方法を提供します。 詳細については、「プリンター拡張機能ライブラリの概要」を参照してください。

Note

このトピックに示すコード例は、C# バージョンの印刷設定と印刷通知のサンプルに基づいています。 このサンプルは、JavaScript と C++ でも使用できます。 C++ は COM に直接アクセスできるため、C++ バージョンのサンプルにはコード ライブラリ プロジェクトが含まれていないことにご注意ください。 サンプルをダウンロードして、最新バージョンのコードを確認してください。

前提条件

開始する前に、以下の操作を行います。

  1. v4 プリンター ドライバーを使用してプリンターがインストールされていることを確認します。 詳しくは、「v4 プリンター ドライバーの開発」をご覧ください。

  2. 開発マシンを設定します。 ツールのダウンロードと開発者アカウントの作成については、「概要」を参照してください。

  3. ストアにアプリを関連付けます。 詳細については、「手順 1: UWP デバイス アプリを作成する」を参照してください。

  4. プリンターをアプリに関連付けるプリンターのデバイス メタデータを作成します。 詳細については、「手順 2: デバイス メタデータを作成する」を参照してください。

  5. C# または JavaScript を使用してアプリを作成する場合は、PrinterExtensionLibrary プロジェクトと DeviceAppForPrintersLibrary プロジェクトを UWP デバイス アプリ ソリューションに追加します。 これらの各プロジェクトは、印刷設定と印刷通知のサンプルで確認できます。

    Note

    C++ は COM に直接アクセスできるため、C++ アプリには、COM ベースのプリンター デバイス コンテキストを操作するための別のライブラリは必要ありません。

手順 1: プリンターを見つける

デバイス コンテキストを作成する前に、アプリでプリンターのデバイス ID を確認する必要があります。 そのためには、EnumerateAssociatedPrinters メソッドを使用して、PC に接続されているすべてのプリンターを検索します。 次に、各プリンターのコンテナーを確認し、各コンテナーの PackageFamilyName プロパティを比較して関連付けを探します。

Note

アプリに関連付けられているデバイスの System.Devices.AppPackageFamilyName は、Microsoft Visual Studio のマニフェスト デザイナーの [パッケージ] タブにあります。

この例は、EnumerateAssociatedPrintersInkLevel.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 デバイス アプリかどうかを確認します。 この関連付けが存在するのは、アプリとプリンターがローカル PC のデバイス メタデータで定義されている場合です。

この例は、FindAssociationInkLevel.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 メソッドを使用して、関連付けられているデバイス ID の一覧にデバイス ID を追加します。 これらの ID は、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 メソッドは、非同期イベントベース パターンを使用して、プリンターに情報を要求します。 このメソッドでは関連付けられているデバイス ID を使用して、デバイスの状態を取得するために使用できるデバイス コンテキストを取得します。 printHelper.SendInkLevelQuery() メソッドの呼び出しで、デバイス クエリが開始されます。 応答が返されると、OnInkLevelReceived メソッドが呼び出され、UI が更新されます。

Note

この C# の例は、JavaScript サンプルとは異なるパターンに従います。C# では、イベント メッセージを UI スレッドにポスト バックできるように、ディスパッチャーを PrintHelperClass に送信できるためです。

この例は、GetInkStatusInkLevel.xaml.csOnInkLevelReceived ファイルの メソッドと メソッドを示します。

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 クエリのデバイスへの送信と応答の受信が行われます。

この例は、SendInkLevelQueryPrintHelperClass.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 デバイス アプリのデバイス メタデータを作成する」トピックの説明に従い、デバイス メタデータ作成ウィザードを使用して作成できます。

Note

デバイス メタデータ作成ウィザードを使用するには、このトピックの手順を完了する前に、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 キーを押すか、上部のメニューから [Build] -> [ソリューションの構築] に移動します。

  4. プリンターを切断してアンインストールします。 この手順は、次回デバイスが検出されたときに Windows が更新されたデバイス メタデータを読み取るために必要です。

  5. デバイス メタデータを編集して保存します。 デバイス アプリをデバイスにリンクするには、デバイス アプリをデバイスに関連付ける必要があります。

    Note

    デバイス メタデータをまだ作成していない場合は、「手順 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 アプリ ID] が自動的に入力されます。

    5. アプリでプリンター通知を登録する場合は、[通知ハンドラー] ボックスを入力します。 [イベント ID] に、印刷イベント ハンドラーの名前を入力します。 [イベント アセット] に、そのコードが駐在するファイルの名前を入力します。

    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 を開き、クエリを実行するパッケージ ファミリ名が 、"パッケージ ファミリ名" フィールドの [パッケージ] タブにある名前と一致していることを確認してください。

  • 考えられる原因: プリンターが v4 プリンターではなく v3 プリンターを使用してインストールされました。 インストールされているバージョンを確認するには、PowerShell を開き、次のコマンドを入力します。

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

v4 印刷ドライバーの開発

プリンター拡張機能インターフェイス (v4 プリンター ドライバー)

双方向通信

UWP アプリの概要

UWP デバイス アプリを作成する (ステップ バイ ステップ ガイド)

UWP デバイス アプリのデバイス メタデータを作成する (ステップ バイ ステップ ガイド)