C# winspool.drv call to WritePrinter not printing
Asked Answered
A

6

5

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);
        }
    }
Ahimsa answered 3/4, 2015 at 2:14 Comment(4)
MSDN says these functions are in spoolss.dllGord
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 itselfAhimsa
In case anyone is wondering, code is taken from the link below I believe: vishalsbsinha.wordpress.com/2014/05/06/…Palocz
@Ahimsa I am suffering from the same problem. Did you have any solution of it?Overgrowth
S
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;
}
Salmonberry answered 21/2, 2019 at 14:5 Comment(2)
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?Bilow
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.Salmonberry
S
1

Change your code like this:

 var rawPrinter = new DOC_INFO_1() { pDocName = "My Document", pDataType = "Text" };
Saprolite answered 13/4, 2015 at 10:1 Comment(0)
U
0

You need to call StartPagePrinter after StartDocPrinter and before WritePrinter

Uitlander answered 10/3, 2017 at 17:8 Comment(0)
C
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);
Caribbean answered 28/6, 2017 at 12:50 Comment(1)
You may need to explain your answer and use some text formatting.Cletis
K
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

Kumasi answered 23/7, 2020 at 14:55 Comment(0)
C
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.

Cartouche answered 1/11, 2022 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.