4

I'm using some code I found online which was a solution to someone else's similar printing problem. The code appears to run fine, and even errors out when I would expect it to (for example, when I purposefully enter in a bad printer name). The problem I'm having is that the interop call to winspool.drv's WritePrinter method does not seem to cause the printer to print anything, even though this method returns "true". Any ideas why the printer isn't actually printing???

public class PrintRaw
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public class DOC_INFO_1
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDocName;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pOutputFile;
            [MarshalAs(UnmanagedType.LPStr)]
            public string pDataType;
        }

        [DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
          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 = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi,
          ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level,
                                                  [In, MarshalAs(UnmanagedType.LPStruct)] DOC_INFO_1 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);

        public void Print(String printerName, String filename)
        {
            IntPtr lhPrinter;
            OpenPrinter(printerName, out lhPrinter, new IntPtr(0));

            if (lhPrinter.ToInt32() == 0)
                return; //Printer not found!!

            var rawPrinter = new DOC_INFO_1() { pDocName = "My Document", pDataType = "RAW" };

            StartDocPrinter(lhPrinter, 1, rawPrinter);

            using (var b = new BinaryReader(File.Open(filename, FileMode.Open)))
            {
                var length = (int)b.BaseStream.Length;
                const int bufferSize = 8192;

                var numLoops = length / bufferSize;
                var leftOver = length % bufferSize;


                for (int i = 0; i < numLoops; i++)
                {
                    var buffer = new byte[bufferSize];
                    int dwWritten;

                    b.Read(buffer, 0, bufferSize);
                    IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
                    Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
                    WritePrinter(lhPrinter, unmanagedPointer, bufferSize, out dwWritten);
                    Marshal.FreeHGlobal(unmanagedPointer);
                }

                if (leftOver > 0)
                {
                    var buffer = new byte[leftOver];
                    int dwWritten;

                    b.Read(buffer, 0, leftOver);
                    IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
                    Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
                    var result = WritePrinter(lhPrinter, unmanagedPointer, leftOver, out dwWritten);
                    Marshal.FreeHGlobal(unmanagedPointer);
                }
            }

            EndDocPrinter(lhPrinter);
            ClosePrinter(lhPrinter);
        }
    }
spoof3r
  • 607
  • 8
  • 23
  • MSDN says these functions are in spoolss.dll – Ben Voigt Apr 03 '15 at 02:31
  • The calls seem to be invoked, and it even returns a handle to the printer when I give it a real printer name, so I don't think that is the problem. As I mentioned in the question, it even fails when I give it a bad printer name. Plus the majority of that code is from MSDN itself – spoof3r Apr 03 '15 at 13:54
  • 1
    In case anyone is wondering, code is taken from the link below I believe: https://vishalsbsinha.wordpress.com/2014/05/06/how-to-programmatically-c-net-print-a-pdf-file-directly-to-the-printer – slayernoah Apr 28 '16 at 15:37
  • @spoof3r I am suffering from the same problem. Did you have any solution of it? – 720p Download Watch Dabangg 3 Feb 22 '17 at 11:23

6 Answers6

6

In my case I found the solution changing the pDataType

For Win7 use RAW

For Win8+ use XPS_PASS

Example:

// 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.
private static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
    bool bSuccess = false; // Assume failure unless you specifically succeed.
    try
    {
        Int32 dwError = 0, dwWritten = 0;
        IntPtr hPrinter = new IntPtr(0);
        DOCINFOA di = new DOCINFOA();

        di.pDocName = "RAW Document";
        // Win7
        //di.pDataType = "RAW";

        // Win8+
        di.pDataType = "XPS_PASS";

        // 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)
        {
            dwError = Marshal.GetLastWin32Error();
        }
    }
    catch { }
    return bSuccess;
}
Josue Barrios
  • 440
  • 5
  • 10
  • This worked for me in Windows 10! Thanks. But could you explain why we need to use XPS_PASS instead of RAW or how did you bumped on it? – Noble Apr 21 '21 at 21:38
  • I don't really know much about it but this is what I found. Use "XPS_PASS" when the data buffer should bypass the print filter pipeline of the XPSDrv printer driver. This datatype would be used to send the buffer directly to the printer, such as when sending print head alignment commands. Normally, a data buffer would be sent as the "RAW" datatype. – Josue Barrios May 27 '21 at 14:56
0

Change your code like this:

 var rawPrinter = new DOC_INFO_1() { pDocName = "My Document", pDataType = "Text" };
Mohd Ahmed
  • 1,422
  • 13
  • 27
0

You need to call StartPagePrinter after StartDocPrinter and before WritePrinter

csensoft
  • 493
  • 1
  • 6
  • 15
0
[DllImport("winspool.drv", EntryPoint = "FlushPrinter", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool FlushPrinter(IntPtr hPrinter, IntPtr pBuf, Int32 cbBuf, out Int32 pcWritten, Int32 cSleep);

WritePrinter(lhPrinter, unmanagedPointer, bufferSize, out dwWritten);

after add:

FlushPrinter(hPrinter, pBytes, dwCount, out dwWritten,2);
Mohammad abumazen
  • 1,286
  • 1
  • 11
  • 24
valeb
  • 1
0

Zebra ZPL printer

I had the same issue with Window 10 and updated the following setting to make it work:

Changed highlighted State (Please see below images) from “Not configured” to “Disabled”

Image Local Group Policy Editor

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
0

We ran into an issue with the WritePrinter pinvoke call succeeding but no physical print happening and also no print job making it into the windows print queue. This call was returning true for success and the number of bytes written was matching the number of bytes that needed to be written.

Solution:

Our problem turned out to be permissions related even though none of the pinvoke calls were failing. We had to grant read/write access for the user issuing the request to the following folder before this would work:
  • C:\Windows\System32\spool\PRINTERS

This request was being made by a windows service also for us.

Cole W
  • 15,123
  • 6
  • 51
  • 85