I want to create a UWP Printer application to set up a virtual printer that I then can select from windows print dialog following the Print Support App design guide as good as I can.
I expect that at least the "MyCustomPrinter" (defined in Package.appxmanifest further down) shows up in the list when I want to print something, but it doesn't.
I have never done this before (driver/psa app) so this is completely new to me and confuses me alot.
My problem is that the printer is just not shown in the device list and the Print Support App design guide might be one of the worst and most confusing documentations I have ever seen.
In any case I can't make heads or tails of it.
So what I did was to create a blank uwp app by selecting the "Blank App (Universal Windows)" in Visual Studio 2022 to create my application.
I would like to share a large part of my code at this point to make sure that I do not withhold any important information from you
Here we go:
App.xaml
<Application
x:Class="PrintingApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PrintingApp">
</Application>
App.xaml.cs
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Graphics.Printing.PrintSupport;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace PrintingApp
{
/// <summary>
/// Provides application-specific behavior to supplement the default <see cref="Application"/> class.
/// </summary>
sealed partial class App : Application
{
Deferral settingsDeferral;
protected override void OnActivated(IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.PrintSupportSettingsUI)
{
// Get the activation arguments
var settingsEventArgs = args as PrintSupportSettingsActivatedEventArgs;
PrintSupportSettingsUISession settingsSession = settingsEventArgs.Session;
// Take deferral
this.settingsDeferral = settingsEventArgs.GetDeferral();
// Create root frame
var rootFrame = new Frame();
// Choose the page to be shown based upon where the application is being launched from
switch (settingsSession.LaunchKind)
{
case SettingsLaunchKind.UserDefaultPrintTicket:
{
// Show settings page when launched for default printer settings
rootFrame.Navigate(typeof(DefaultSettingsView), settingsSession);
}
break;
case SettingsLaunchKind.JobPrintTicket:
{
// Show settings page when launched from printing app
rootFrame.Navigate(typeof(MainPage), settingsSession);
}
break;
}
Window.Current.Content = rootFrame;
}
}
internal void ExitSettings()
{
settingsDeferral.Complete();
}
}
}
Package.appxmanifest
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap11="http://schemas.microsoft.com/appx/manifest/uap/windows10/11"
xmlns:printsupport="http://schemas.microsoft.com/appx/manifest/printsupport/windows10"
xmlns:printsupport2="http://schemas.microsoft.com/appx/manifest/printsupport/windows10/2"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:iot2="http://schemas.microsoft.com/appx/manifest/iot/windows10/2"
IgnorableNamespaces="uap mp desktop4 iot2 printsupport printsupport2 uap11">
<Identity
Name="bc0e7b39-aec8-4569-85ad-b42b6da40308"
Publisher="CN=CrazyEight"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="bc0e7b39-aec8-4569-85ad-b42b6da40308" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>PrintingApp</DisplayName>
<PublisherDisplayName>CrazyEight</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="PrintingApp.App"
desktop4:SupportsMultipleInstances="true"
iot2:SupportsMultipleInstances="true">
<uap:VisualElements
DisplayName="PrintingApp"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="PrintingApp"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
<Extensions>
<!--1. Virtueller Drucker-Workflow-->
<printsupport2:Extension
Category="windows.printSupportVirtualPrinterWorkflow"
EntryPoint="PrintingApp.Tasks.PrintSupportWorkflowBackgroundTask"
uap11:SupportsMultipleInstances="true">
<printsupport2:PrintSupportVirtualPrinter
DisplayName="MyCustomPrinter"
PreferredInputFormat="application/postscript"
OutputFileTypes="ps,pdf"
PdcFile="Config\PRINTERPDC1.xml">
<printsupport2:SupportedFormats>
<printsupport2:SupportedFormat Type="application/postscript" />
<printsupport2:SupportedFormat Type="application/pdf" MaxVersion="1.7" />
</printsupport2:SupportedFormats>
</printsupport2:PrintSupportVirtualPrinter>
</printsupport2:Extension>
<!--2. Print-Support-Extension-->
<printsupport:Extension
Category="windows.printSupportExtension"
EntryPoint="PrintingApp.Tasks.PrintSupportExtensionBackgroundTask"
uap11:SupportsMultipleInstances="true" >
</printsupport:Extension>
<!--3. Print-Support-SettingsUI-->
<printsupport:Extension
Category="windows.printSupportSettingsUI"
EntryPoint="PrintingApp.App"
uap11:SupportsMultipleInstances="true" />
<!--4. Print-Support-JobUI-->
<printsupport:Extension
Category="windows.printSupportJobUI"
EntryPoint="PrintingApp.Tasks.PrintSupportJobUIBackgroundTask"
uap11:SupportsMultipleInstances="true" />
</Extensions>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
</Capabilities>
</Package>
printerpdc1.xml (Build Action = Content and Copy to Output Directory = Always)
<?xml version="1.0" encoding="UTF-8"?>
<PrintDeviceCapabilities
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:psk="https://schemas.microsoft.com/windows/2003/08/printing/printschemakeywords"
xmlns:psf="https://schemas.microsoft.com/windows/2003/08/printing/printschemaframework"
xmlns:psf2="https://schemas.microsoft.com/windows/2013/12/printing/printschemaframework2"
xmlns="https://schemas.microsoft.com/windows/2013/12/printing/printschemaframework2"
version="2">
<CapabilitiesChangeID xsi:type="xsd:string">617e6418-6847-4fa1-bec6-3a3668d09f6d</CapabilitiesChangeID>
<!-- Hier folgen <Feature>- und <Option>-Definitionen -->
<!-- Required minimal feature: Page Media Size -->
<Feature name="psk:PageMediaSize">
<Option name="psk:ISOA4" />
</Feature>
<!-- Required minimal feature: Page Orientation -->
<Feature name="psk:Orientation">
<Option name="psk:Portrait" />
</Feature>
</PrintDeviceCapabilities>
PrintSupportWorkflowBackgroundTask
- other tasks have only
taskInstance.GetDeferral().Complete();implementation inRunmethod
using Windows.ApplicationModel.Background;
using Windows.Graphics.Printing.PrintSupport;
using Windows.Graphics.Printing.Workflow;
namespace PrintingApp.Tasks
{
/// <summary>
/// Background-Task für den virtuellen Drucker-Workflow.
/// </summary>
public sealed partial class PrintSupportWorkflowBackgroundTask : IBackgroundTask
{
BackgroundTaskDeferral taskDeferral;
public void Run(IBackgroundTaskInstance taskInstance)
{
// Take Task Deferral
taskDeferral = taskInstance.GetDeferral();
var jobTriggerDetails = taskInstance.TriggerDetails as PrintWorkflowJobTriggerDetails;
// This registers the virtual printer for the app
var workflowBackgroundSession = jobTriggerDetails.PrintWorkflowJobSession as PrintWorkflowJobBackgroundSession;
// Register for events
workflowBackgroundSession.JobStarting += this.OnJobStarting;
workflowBackgroundSession.PdlModificationRequested += this.OnPdlModificationRequested;
// Start Firing events
workflowBackgroundSession.Start();
taskDeferral.Complete();
}
private void OnPdlModificationRequested(PrintWorkflowJobBackgroundSession sender, PrintWorkflowPdlModificationRequestedEventArgs args)
{
// TODO
}
private void OnJobStarting(PrintWorkflowJobBackgroundSession session, PrintWorkflowJobStartingEventArgs args)
{
using (args.GetDeferral())
{
// Call SetSkipSystemRendering to skip conversion for XPS to PDL, so that PSA can directly manipulate the XPS file.
args.SetSkipSystemRendering();
}
}
}
}
csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{BFDA5F9B-D16A-4460-8F23-D55AACBEA9EA}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PrintingApp</RootNamespace>
<AssemblyName>PrintingApp</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22621.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<PackageCertificateKeyFile>PrintingApp_TemporaryKey.pfx</PackageCertificateKeyFile>
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
<GenerateTestArtifacts>True</GenerateTestArtifacts>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM64'">
<OutputPath>bin\ARM64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="DefaultSettingsView.xaml.cs">
<DependentUpon>DefaultSettingsView.xaml</DependentUpon>
</Compile>
<Compile Include="MainPage.xaml.cs">
<DependentUpon>MainPage.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tasks\PrintSupportExtensionBackgroundTask.cs" />
<Compile Include="Tasks\PrintSupportJobUIBackgroundTask.cs" />
<Compile Include="Tasks\PrintSupportSettingsUIBackgroundTask.cs" />
<Compile Include="Tasks\PrintSupportWorkflowBackgroundTask.cs" />
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Config\PRINTERPDC1.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="DefaultSettingsView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="MainPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.14</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="PrintingApp_TemporaryKey.pfx" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
