spoof3r
spoof3r

Reputation: 627

C# winspool.drv call to WritePrinter not printing

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);
        }
    }

Upvotes: 5

Views: 10910

Answers (6)

Cole W
Cole W

Reputation: 15293

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.

Upvotes: 0

Developer Why
Developer Why

Reputation: 21

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

Upvotes: 0

Josue Barrios
Josue Barrios

Reputation: 502

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;
}

Upvotes: 6

valeb
valeb

Reputation: 1

[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);

Upvotes: 0

csensoft
csensoft

Reputation: 743

You need to call StartPagePrinter after StartDocPrinter and before WritePrinter

Upvotes: 0

Mohd Ahmed
Mohd Ahmed

Reputation: 1482

Change your code like this:

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

Upvotes: 1

Related Questions