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;
}
}
I'm a little late to the game here but the responses stating that you can't send a PDF directly to a printer aren't quite right now that some time has passed since this question was first asked. You can use System.IO file copy to send a PDF file directly to a UNC printer queue in a lot of cases (as long as the printer supports it.)
Example: File.Copy(pathToAPDFFileYouWantPrinted, @"\\fserverprint\printerName");
I know this works because I use it all the time. it's very fast. The problem is you need to write the byte array to a file first THEN copy it to the printer.
While this doesn't satisfy the OP's byte array method, it's literally two lines of code to use the byte array/send to printer approach once you have the byte array as a PDF.
File.WriteAllBytes(pathToPDFFileYouWantPrinted, output);
File.Copy(pathToAPDFFileYouWantPrinted, @"\\fserverprint\printerName");
I tried the byte array approach recently and it doesn't work. You'll get this error: System.NotSupportedException: 'FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:', call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr.'
Yeah, this cannot work. You are bypassing the printer driver with this code, you would have to generate print commands using the printer control language of the specific printer you use. PCL and 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 soon" prompts. Shown on an invisible desktop, you have no way to find out why the document doesn't print.
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