tags:

views:

134

answers:

1

Hi,

My service converts report into a byte array and passes to client(wpf app) using the following code:

byte[] bytes = renderer.ServerReport.Render("PDF", deviceInfo, out mimeType, out encoding, out extension, out streamids, out warnings);

where renderer is an instance of Microsoft.Reporting.Webforms.ReportViewer. There is a problem here: encoding output parameter is returning null, so couldn't findout information about encoding.

The UI has to print this byte array silently to printer. Can this byte array be sent directly to printer instead of converting it back to PDF file in UI and then printing?

I actually tried something like below after looking at a msdn link, but this is printing wierd symbols over so many pages when the actual report is just a one or two page one. There is less information about the winspool dll's functions online, so not sure where I am going wrong.

Any ideas highly appreciated.

public class RawPrintHelper
{
     //Structure and API declarions:
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class DOCINFOA
    {
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)]
        public string pDataType;
    }
    [DllImport("winspool.Drv", EntryPoint = "OpenPrinter", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);

    [DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartDocPrinter", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);

    [DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
    public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);

    // SendBytesToPrinter()
    // When the function is given a printer name and an unmanaged array
    // of bytes, the function sends those bytes to the print queue.
    // Returns true on success, false on failure.
    public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
    {
        Int32 dwError = 0, dwWritten = 0;
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();
        bool bSuccess = false; // Assume failure unless you specifically succeed.

        di.pDocName = "My C#.NET RAW Document";
        di.pDataType = "RAW";

        // Open the printer.
        if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
        {
            // Start a document.
            if (StartDocPrinter(hPrinter, 1, di))
            {
                // Start a page.
                if (StartPagePrinter(hPrinter))
                {
                    // Write your bytes.
                    bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
                    EndPagePrinter(hPrinter);
                }
                EndDocPrinter(hPrinter);
            }
            ClosePrinter(hPrinter);
        }
        // If you did not succeed, GetLastError may give more information
        // about why not.
        if (bSuccess == false)
        {
            dwError = Marshal.GetLastWin32Error();
        }
        return bSuccess;
    }

}

+1  A: 

Yeah, this cannot work. You are bypassing the printer driver with this code, you would have to generate print commands in the name language of the printer. PCL, Postscript are common printer control languages. But anything goes, manufacturers often create their own custom variety.

Looks like you are sending PDF, I don't know any printer that uses PDF as its native control language. Maybe such a printer exists, clearly the one you have doesn't. I can't really guess why you'd use a Webforms class in a service, hard to provide an alternative. Printing from a service is a bad idea in general, printer drivers are far too happy to put up "change the toner" prompts. Shown on an invisible desktop.

Hans Passant
Hans and Clint, Thanks for your replies. I will look into those printer control languages you mentioned. Hans, Printing is not done at service level. what exactly happens is the service generates a report and converts to byte array(this is where winforms.reportviewer is used) and sends this byte array in callback to client. Client is the one which has to print but without dialog box.
Well, no matter what shape you bang it in, it is still a .pdf. Components that can print a .pdf are plentiful, they surely can do so without having to go through a print dialog. Just saving it to a temporary file and using Acrobat with the "print" verb might even work.
Hans Passant
Oh, and btw: there's little point in using a service if you need to have a client program running.
Hans Passant
I already implemented the temporary file method. Just looking for a more efficient solution.
Files are *very* efficient, the file system cache makes them memory-mapped. You would do a lot of work and get *no* improvement.
Hans Passant