PSA: Media Type Default Configuration Issue Affecting Hardware Developers

Table of Contents

Media Type Default Configuration Issue Affecting Hardware Developers

This article addresses a specific issue encountered by hardware developers working with Print Support Apps (PSAs). The problem revolves around the default setting for MediaType within the PSA, which, unexpectedly, is not consistently applied in certain defined scenarios. This inconsistency can lead to confusion and unexpected behavior when users interact with print settings through the PSA interface. Understanding and implementing the provided workaround is crucial for ensuring a smooth and predictable user experience for IPP-based printers utilizing PSAs on Windows platforms.

Symptoms

Consider a scenario frequently encountered during the development of Print Support Apps for IPP (Internet Printing Protocol) based printers. This situation specifically arises when developers are working on PSAs intended for deployment on Windows 10 version 22H2 or Windows 11 version 23H2 operating systems. The issue manifests itself when you extend the printer’s capabilities by adding custom PageMediaType options to the Print Device Capabilities (PDC). This modification is typically performed within the PrintSupportExtensionSession.PrintDeviceCapabilitiesChanged event handler of your PSA, allowing for dynamic adjustments to the printer’s advertised features.

In this particular setup, the expected behavior is that the default MediaType option, as defined in the Print Schema, should be automatically selected when the print settings screen within the PSA is initially displayed to the user. However, the observed symptom is that this default selection fails to occur upon the first display of the print settings. Instead, another option might be unexpectedly presented as the default. Interestingly, this issue appears to be transient. Once the user saves the print settings, even without making changes, the problem resolves itself, and subsequent displays of the print settings screen correctly respect the default setting.

To illustrate this with a concrete example, imagine you are adding a custom media type called ContosoMediaType to the available PageMediaType options. Within your Print Ticket configuration, you correctly define AutoSelect as the default option for PageMediaType by setting its default attribute to true. According to the Print Schema, AutoSelect should be the pre-selected option. However, when the print settings are initially presented to the user via the PSA, another media type option, perhaps the first one in the list or a previously selected option, is displayed as the default instead of the intended AutoSelect. This discrepancy between the configured default and the presented default is the core symptom of this issue.

<!-- media-type-supported -->
<psk:PageMediaType psf2:psftype="Feature">
    <psk:AutoSelect psf2:psftype="Option" psf2:default="true"/>  <!--Set the default value for AutoSelect-->
    <psk:PhotographicGlossy psf2:psftype="Option" psf2:default="false"/>
    <psk:Photographic psf2:psftype="Option" psf2:default="false"/>
    <contoso:ContosoMediaType psf2:psftype="Option" psf2:default="false"/>
</psk:PageMediaType>

This XML snippet demonstrates the intended configuration. AutoSelect is clearly marked as the default. Despite this, the PSA may not initially reflect this default setting, leading to a potentially confusing user experience until settings are saved. This behavior is specific to the first display of the settings after the PSA is invoked and before any settings are saved.

Cause

The root cause of this unexpected behavior lies within the internal workings of the Windows Print Workflow service, specifically in how it handles Print Device Capabilities (PDC) and print ticket conversions. The issue is traced back to a component named Windows.Graphics.Printing.Workflow.dll. This DLL is responsible for processing PDC data and translating it into a print ticket format that the printing system can understand and utilize. It appears that within this conversion process, specifically in the initial rendering of the print settings dialog in the PSA, the default MediaType setting is not correctly interpreted or applied from the PDC.

Essentially, when the PSA initially retrieves the PDC and attempts to display the print settings to the user, the Windows.Graphics.Printing.Workflow.dll fails to accurately reflect the default MediaType option defined within the PDC. This is not a problem with the PSA code itself, nor with the Print Schema definition of the default value. Instead, it is an issue inherent in the Windows Print Workflow service’s component responsible for PDC to print ticket conversion and initial settings dialog rendering.

This issue is characterized as a bug within the Windows Print Workflow service. It is not related to incorrect PSA implementation or misconfiguration of the Print Schema. The problem is internal to the operating system’s print subsystem, specifically the module responsible for handling PDC and print ticket transformations during the initial display of print settings in a PSA context. The fact that the issue disappears after saving settings suggests that the subsequent processing of settings correctly handles the default value, but the initial rendering path has a defect in default value application.

Workaround

To mitigate this issue and ensure the correct default MediaType is selected when the print settings dialog is initially presented in your PSA, a workaround involving local storage is recommended. This workaround leverages the PSA’s background task and local application settings to temporarily store and retrieve the default MediaType option. The core idea is to programmatically save the default option when the PDC is updated and then restore this saved value when the print settings dialog is initialized. This ensures that even if the Windows Print Workflow service initially fails to apply the default from the PDC, the PSA itself explicitly sets the correct default based on the saved value.

The following steps outline the implementation of this workaround, using C# code examples based on generic PSA samples provided by Microsoft.

Step 1: Implement Local Storage Utility Methods

First, you need to add helper methods to a utility class, named LocalStorageUtil in this example, within the background task of your PSA. These methods will handle saving, retrieving, and checking the status of the default value loading process from the application’s local settings.

// Method to check if the default option has already been set in the PrintTicket
public static bool IsAlreadyLoadedDefaultValue()
{
    try
    {
        return (bool)ApplicationData.Current.LocalSettings.Values["IsLoadedDefaultValue"];
    }
    catch (NullReferenceException e)
    {
        return false;
    }
}

// Method to indicate that the default option has been applied to the PrintTicket
public static void LoadedDefaultValue(bool _bLoaded)
{
    ApplicationData.Current.LocalSettings.Values["IsLoadedDefaultValue"] = _bLoaded;
}

// Method to save the default option value of the specified feature
public static void SetPdcDefaultValue(string _Feature, string _DefaultValue)
{
    System.Diagnostics.Debug.WriteLine("SetPdcDefaultValue: Feature=" + _Feature + ", Default=" + _DefaultValue);
    ApplicationData.Current.LocalSettings.Values[_Feature] = _DefaultValue;
}

// Method to return the default option value of the specified feature
public static string GetPdcDefaultValue(string _Feature)
{
    try
    {
        return (string)ApplicationData.Current.LocalSettings.Values[_Feature];
    }
    catch (NullReferenceException e)
    {
        System.Diagnostics.Debug.WriteLine(e.Message);
        return null;
    }
}

These methods provide the following functionalities:

  • IsAlreadyLoadedDefaultValue(): Checks if the default value has already been loaded from local storage. This prevents repeatedly applying the workaround.
  • LoadedDefaultValue(bool _bLoaded): Sets a flag to indicate whether the default value has been loaded.
  • SetPdcDefaultValue(string _Feature, string _DefaultValue): Saves the default value for a specific feature (e.g., “PageMediaType”) to local storage.
  • GetPdcDefaultValue(string _Feature): Retrieves the saved default value for a feature from local storage.

Step 2: Save Default Values in PrintDeviceCapabilitiesChanged Event Handler

Next, within the PrintSupportExtensionSession.PrintDeviceCapabilitiesChanged event handler of your PSA, call a SaveDefaultValues() method (which you’ll need to implement) to save the default values extracted from the PDC. This ensures that whenever the PDC is updated, the PSA captures and stores the intended default settings.

private void OnSessionPrintDeviceCapabilitiesChanged(PrintSupportExtensionSession sender, PrintSupportPrintDeviceCapabilitiesChangedEventArgs args)
{
    var pdc = args.GetCurrentPrintDeviceCapabilities();
    // Add the custom namespace URI to the XML document.
    pdc.DocumentElement.SetAttribute("xmlns:contoso", "http://schemas.contoso.com/keywords");
    // Add the custom media type.
    AddCustomMediaType(ref pdc, "http://schemas.contoso.com/keywords", "contoso:ContosoMediaType");

    // Call the method to save the default values of the PDC.
    SaveDefaultValues(ref pdc);

    args.UpdatePrintDeviceCapabilities(pdc);
    args.SetPrintDeviceCapabilitiesUpdatePolicy(
        PrintSupportPrintDeviceCapabilitiesUpdatePolicy.CreatePeriodicRefresh(System.TimeSpan.FromMinutes(1)));
    args.GetDeferral().Complete();
}

The SaveDefaultValues(ref pdc) method (not shown in detail here, but assumed to iterate through PDC features and use LocalStorageUtil.SetPdcDefaultValue to store default options) is crucial. It is invoked after any modifications to the PDC, such as adding custom media types, and before the PDC update is applied.

Step 3: Initialize ComboBox Items Using Saved Default Values

Finally, when initializing the ComboBox controls in your PSA’s UI to represent print settings features, you need to modify the initialization logic to check for and use the saved default values from LocalStorageUtil. This step ensures that the ComboBoxes are initially set to the correct default option, overriding any potential misbehavior from the Windows Print Workflow service.

private ComboBox CreatePrintTicketFeatureComboBox(PrintTicketFeature feature, bool useDefaultEventHandler = true)
{
    if (feature == null)
    {
        return null;
    }

    var comboBox = new ComboBox
    {
        // Header is displayed in the UI, on top of the ComboBox.
        Header = feature.DisplayName
    };
    // Construct a new list since IReadOnlyList does not support the 'IndexOf' method.
    var options = new ObservableCollection<PrintTicketOption>(feature.Options);
    // Provide the combo box with a list of options to select from.
    comboBox.ItemsSource = options;
    // Set the selected option to the option set in the print ticket.
    PrintTicketOption selectedOption;

    // Load the default value of the PDC from LocalStorageUtil only once.
    string defaultOption = LocalStorageUtil.GetPdcDefaultValue(feature.Name);
    if (!LocalStorageUtil.IsAlreadyLoadedDefaultValue() && defaultOption != null)
    {
        selectedOption = options[0]; // Initialize with the first option as a fallback
        foreach (var option in options)
        {
            if (option.Name == defaultOption)
            {
                selectedOption = option;
                break;
            }
        }
    }
    else
    {
        var featureOption = feature.GetSelectedOption();
        try
        {
            selectedOption = options.Single((option) => (
                option.Name == featureOption.Name && option.XmlNamespace == featureOption.XmlNamespace));
        }
        // Catch exceptions, because there can be multiple features with the "None" feature name.
        // We need to handle those features separately.
        catch (System.SystemException exception)
        {
            var nameAttribute = featureOption.XmlNode.Attributes.GetNamedItem("name");
            var attribute = featureOption.XmlNode.OwnerDocument.CreateAttribute("name");

            selectedOption = options.Single((option) => (
                option.DisplayName == featureOption.DisplayName
                && option.Name == featureOption.Name
                && option.XmlNamespace == featureOption.XmlNamespace));
        }
    }

    comboBox.SelectedIndex = options.IndexOf(selectedOption);

    // Indicate that we have completed loading the default value from LocalStorageUtil.
    LocalStorageUtil.LoadedDefaultValue(true);
    // ...
}

In this modified CreatePrintTicketFeatureComboBox method, before determining the selected option directly from the PrintTicketFeature, the code now checks LocalStorageUtil for a saved default value using LocalStorageUtil.GetPdcDefaultValue(feature.Name). If a saved default value exists and has not been loaded yet (!LocalStorageUtil.IsAlreadyLoadedDefaultValue()), the code attempts to find and select the corresponding option in the ComboBox based on the saved default option name. This ensures that the ComboBox is initialized with the correct default value, effectively working around the initial default setting issue.

By implementing these steps, hardware developers can effectively work around the default MediaType configuration issue in Windows PSAs. This workaround ensures that the intended default print settings are correctly presented to users, providing a consistent and expected printing experience despite the underlying bug in the Windows Print Workflow service. Remember to thoroughly test your PSA after implementing this workaround to ensure it functions as expected in various scenarios.

We encourage you to share your experiences and questions regarding this issue and the proposed workaround in the comments below. Your feedback is valuable in improving the collective understanding and resolution of such development challenges.

Post a Comment