8
votes

I am trying to print an xps document to printers (network printer, some virtual local printers, xps and non xps based) with the following code.

C# Source:

static void Main(string[] args)
{
    PrintServer printServer = new PrintServer(@"\\printserver.csez.zohocorpin.com");
    foreach (PrintQueue queue in printServer.GetPrintQueues())
    {
        Console.WriteLine("Printer: {0}, Port: {1}, ShareName: {2}, status: {3}, PrintingIsCancelled: {4}", 
            queue.Name, queue.QueuePort.Name, queue.ShareName, queue.QueueStatus, queue.PrintingIsCancelled);
        Program program = new Program();

        Thread printingThread = new Thread(() => program.Print_XPXFile(queue, @"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
        // Set the thread that will use PrintQueue.AddJob to single threading.
        printingThread.SetApartmentState(ApartmentState.STA);

        printingThread.Start();
        printingThread.Join();
    }
}

public void Print_XPXFile(PrintQueue pQueue, String FilePath)
{
    // Create print server and print queue.
    bool fastCopy = pQueue.IsXpsDevice;
    FileInfo file = new FileInfo(FilePath);

    if (!file.Exists)
    {
        Console.WriteLine("There is no such file.");
    }
    else
    {
        Console.WriteLine("Adding {0} to {1} queue. share name : {2}", FilePath, pQueue.Name, pQueue.ShareName);

        try
        {
            // Print the Xps file while providing XPS validation and progress notifications.
            PrintSystemJobInfo xpsPrintJob = pQueue.AddJob(file.Name, FilePath, fastCopy);
            Console.WriteLine("Done adding.");
        }
        catch (PrintJobException e)
        {
            Console.WriteLine("\n\t{0} could not be added to the print queue.", file.Name);
            if (e.InnerException.Message == "File contains corrupted data.")
            {
                Console.WriteLine("\tIt is not a valid XPS file."); // Use the isXPS Conformance Tool to debug it.
            }
            else
            {
                Console.WriteLine("\tmessage : {0}", e.InnerException.Message); 
            }
        }
    }
}

When printing to Microsoft XPS Document Writer, Microsoft Print to PDF, etc it works fine.

I found that it is working fine with all XPS based printers. I even installed a XPS sample printer driver and added a virtual local printer to confirm this claim and as expected it worked.

For non-xps based printers, it actually gets stuck in the AddJob function. It neither throws any exception, nor it moves to the next statement.

I developed the code based on this msdn resource.

What is the cause and solution?

All thoughts are welcome.

2

2 Answers

7
votes

I can't seem to find the problem. But here is a more promising way to print XPS files:

        public static void PrintXPSToDefaultPrinter(string FilePath)
        {
            try
            {
                // Create the print dialog object and set options
                PrintDialog pDialog = new PrintDialog();
                pDialog.PageRangeSelection = PageRangeSelection.AllPages;
                pDialog.UserPageRangeEnabled = true;

                FileInfo file = new FileInfo(FilePath);
                XpsDocument xpsDocument = new XpsDocument(FilePath, FileAccess.ReadWrite);
                FixedDocumentSequence fixedDocSeq = xpsDocument.GetFixedDocumentSequence();

                pDialog.PrintDocument(fixedDocSeq.DocumentPaginator, file.Name);
            }
            catch (System.IO.IOException ex)
            {
                Console.WriteLine("The file is being used by some other process.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occured : {0}", ex.Message);
            }
        }

You should call this in STA mode just like you have used:

static void Main(string[] args)
{ 
      Thread printingThread = new Thread(() => PrintXPSToDefaultPrinter(@"D:\Assist\RemotePrint\Spool\Donalduck.xps"));
      printingThread.SetApartmentState(ApartmentState.STA);
      printingThread.Start();
}

You can also mention any printqueue u want in the pDialog.PrintQueue property.

Hope this helps. Enjoy man !!!

1
votes

I, too, got stuck in the AddJob() method. The problem seemed to be more prevalent as Windows 10 platforms became involved.

Breaking the execution in debug, the stack trace showed the STA thread was blocked on a call to

MS.Internal.PrintWin32Thunk.XpsCompatiblePrinter.JobIdentifier.get

and this was further blocking on a low-level WaitOne() on some unknown synchronization object.

Even though details on this problem are thin (this is the only post I have found on the topic), THANKFULLY the accepted solution works extremely well (and I have been trying all kinds of things to print in WPF for years).

AddJob() is fully replaced in all respects. One can even control the PrintTicket more easily through the PrintDialog (to set paper size, orientation, one/two sided printing, etc.)