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.
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.
i had the same experience printing from a windows service but my problem was in windows server 2008, i think its because windows services are not meant to have this functionalities. I read a lot of blogs etc and didn't finda ny solution. i finally found a way in which the service invokes another exe to print using the microsoft task library.
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.