I have a windows service application written in C# .NET. This application is used to generate a report pdf by printing document to local software printer that generates PDFs. This works well on Windows XP and Windows 7. Unfortunately I found that on Windows 8 it fails. Then I found out that printing to any (even physical) printer on Windows 8 fails when I print from my service. What is missing in my program to work? I'm printing this way:
FlowDocument document = MyDocument;
var source = document as IDocumentPaginatorSource;
var documentPaginator = source.DocumentPaginator;
using (var printServer = new LocalPrintServer())
{
PrintQueue queue = printServer.GetPrintQueue(printerName);
XpsDocumentWriter docWriter = PrintQueue.CreateXpsDocumentWriter(queue);
// Print ticket - Approach 1
// PrintTicket printTicket = queue.DefaultPrintTicket.Clone();
// Print ticket - Approach 2
var printTicket = new PrintTicket
{
PageOrientation = PageOrientation.Landscape,
PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4), // set size of media (paper)
};
documentPaginator.PageSize = new Size(document.PageWidth, document.PageHeight);
docWriter.Write(documentPaginator, printTicket);
}
Service is set to 'system account' without 'interacting with desktop' (but I tried that too or to login as local user).
This results into exception on Windows 8. When using 'Print ticket - Approach 1':
System.Printing.PrintQueueException: PrintTicket provider failed to bind to printer. Win32 error: -2147467231
at MS.Internal.Printing.Configuration.PTProvider..ctor(String deviceName, Int32 maxVersion, Int32 clientVersion)
at MS.Internal.Printing.Configuration.PTProviderBase.Create(String deviceName, Int32 maxVersion, Int32 clientVersion)
at System.Printing.PrintTicketManager..ctor(String deviceName, Int32 clientPrintSchemaVersion)
at System.Printing.PrintQueue.get_DefaultPrintTicket()
Using 'Print ticket - Approach 2':
Exception encountered: System.Printing.PrintQueueException: Fehler beim Binden des PrintTicket-Anbieters an den Drucker. Win32-Fehler: -2147467231
bei MS.Internal.Printing.Configuration.PTProvider..ctor(String deviceName, Int32 maxVersion, Int32 clientVersion)
bei MS.Internal.Printing.Configuration.PTProviderBase.Create(String deviceName, Int32 maxVersion, Int32 clientVersion)
bei System.Printing.PrintTicketManager..ctor(String deviceName, Int32 clientPrintSchemaVersion)
bei System.Printing.PrintQueue.get_UserPrintTicket()
bei System.Printing.PrintQueue.get_CurrentJobSettings()
bei System.Printing.PrintQueue.CreateSerializationManager(Boolean isBatchMode, Boolean mustSetJobIdentifier)
bei System.Windows.Xps.XpsDocumentWriter.BeginWrite(Boolean batchMode, Boolean asyncMode, Boolean setPrintTicketHandler, PrintTicket printTicket, PrintTicketLevel printTicketLevel, Boolean printJobIdentifierSet)
bei System.Windows.Xps.XpsDocumentWriter.Write(DocumentPaginator documentPaginator, PrintTicket printTicket)
I would say that service is able to find those printers because when I have tried to print to non-existing printer and I got "invalid printer name" exception.
Here I'll keep for myself some related questions: Printing from a Windows Service, Printing from a Windows Service, http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/b74bd27d-1cc8-4fca-a6de-2cd1371cf3b7/,
Mildly related: Printing from a .NET Service,
edit:
In case someone is interested in trying - here is my sample service application that tries to print simple document to printer selected in config file: http://bin.mypage.sk/FILES/PrintTestService.rar
edit2:
Interesting. When I tried a different printing code there is no error:
using (var printDocument = new PrintDocument())
{
printDocument.PrinterSettings.PrinterName = printerName;
printDocument.Print();
}
Unfortunately this is a older GDI+ code using the System.Drawing.Graphics library which is not compatible with my code that produces paginated document in form of System.Windows.Media.Visual objects. So I can't use it to print my document unless I'd like to spend two weeks of creating the pagination of my document from scratch.
edit3:
There is discussion about this issue here: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/96e7fc10-3f08-4808-b748-692e30377293 There is a 'workaround' for it to use 'anyCPU' platform. This workaround really works (I tried it) but it is not usable in my case when my service needs to be x86. I have contacted MS support through our company to find a real solution.
At http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/fdcfa0fa-50aa-4a61-be79-5b4c8f65fbf7/ we see that this was reported to Microsoft and confirmed as a bug in Windows 8 and Windows Server 2012.
This bug is triggered when trying to print from a 32bit process in non-standard user session (like e.g. a service).
According to Microsoft, this a bug was resolved in Windows 8.1 and Windows Server 2012 R2. However, we could still reproduce it on Windows 8.1.
On the same site, a workaround is given by Microsoft. This workaround solved the problem for us on Windows 8.1. It probably also works on Windows 8 and Windows Server 2012.
The workaround goes as follows:
Open Regedit and go to HKEY_CLASSES_ROOT\CLSID{BA7C0D29-81CA-4901-B450-634E20BB8C34}
Check the value of the "AppID" Registry Entry. In our case this was {AA0B85DA-FDDF-4272-8D1D-FF9B966D75B0}
Since this is a bug in Windows, you cannot fix it in your code. The workaround might have side effects, but we haven't seen any so far in our scenario.
Solution: To wait for Microsoft to fix this bug. I have reported the bug (REG:113040910349062), they have confirmed it, but fix won't be soon (in next months) and there is no date specified for it.
Workaround that we use: Create a small application that handles the printing, compile it as 'anyCPU' application and run it from service application (which I can't compile as 'anyCPU' - I need 'x86' because of its dependencies). This workaround was tested and works. But it is dirty + causes overhead + extra care.
Edit: Microsoft provides a hotfix that should fix this problem in Windows 8 : http://support.microsoft.com/kb/2872151/EN-US ( "You cannot print from a 32-bit WPF application in an x64-based version of Windows 8 or Windows Server 2012" )
Edit (2013-11-04): we have tested the hotfix and it does not work on Windows 8 or Windows 8.1. Currently we are again in a phase of communicating with Microsoft.
Edit (2014-beginning of the year): we have received a response from Microsoft that they don't know how to fix this bug and the bug is closed. So only solution for us at the moment is having a small app exe compiled as AnyCPU which is called by our x86 service. This approach works, but causes slowdown and our product is also harder to maintain because of this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With