Search code examples
c#winapilandscapeportraitprint-spooler-api

How to determine value of DM_OUT_BUFFER in C#


I am printing the output of a text box in C#. To do that, I use raw printing. However, before sending the text to print using WritePrinter

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

I would like to modify the device structure to landscape mode.

I can perform the first DocumentProperties call, because that returns the size of the device structure that will be pointed to by pDevMode.

IntPtr pDevMode;
.
.
.
int dwNeeded = DocumentProperties(GetForegroundWindow(),
    hPrinter,       /* Handle to our printer. */
    szPrinterName,        /* Name of the printer. */
    IntPtr.Zero,           /* Asking for size, so */
    IntPtr.Zero,           /* these are not used. */
    0);             /* Zero returns buffer size. */

pDevMode = new IntPtr(dwNeeded);

However, the second call to DocumentProperties requires a both a pointer to a device information block as well as the constant DM_OUT_BUFFER to tell the function to write the device information into the device information block, pointed to by pDevMode.

How can I access the value of DM_OUT_BUFFER from C#? I have read a lot of articles, but haven't come across anything that spells this out.

Most of the full raw printing function is listed below, without the DLL includes that are needed for the WinAPI functions not directly accessible from C#.

And this example works fine, except it sends the document portrait mode, even if the printer is defaulted to landscape mode.

   static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

    // 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();
        IntPtr pDevMode;
        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))
        {
            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;
    }

Solution

  • The constant is defined in the windows api, and you can use the following pinvoke definition from pinvoke.net: https://www.pinvoke.net/default.aspx/Enums.fModes

    [Flags]
    internal enum fModes
    {
       /// <summary>
       /// When used, the DocumentProperties function returns the number
       /// of bytes required by the printer driver's DEVMODE data structure.
       /// </summary>
       DM_SIZEOF = 0,
    
       /// <summary>
       /// <see cref="DM_OUT_DEFAULT"/>
       /// </summary>
       DM_UPDATE = 1,
    
       /// <summary>
       /// <see cref="DM_OUT_BUFFER"/>
       /// </summary>
       DM_COPY = 2,
    
       /// <summary>
       /// <see cref="DM_IN_PROMPT"/>
       /// </summary>
       DM_PROMPT = 4,
    
       /// <summary>
       /// <see cref="DM_IN_BUFFER"/>
       /// </summary>
       DM_MODIFY = 8,
    
       /// <summary>
       /// No description available.
       /// </summary>
       DM_OUT_DEFAULT = DM_UPDATE,
    
       /// <summary>
       /// Output value. The function writes the printer driver's current print settings,
       /// including private data, to the DEVMODE data structure specified by the 
       /// pDevModeOutput parameter. The caller must allocate a buffer sufficiently large
       /// to contain the information. 
       /// If the bit DM_OUT_BUFFER sets is clear, the pDevModeOutput parameter can be NULL.
       /// This value is also defined as <see cref="DM_COPY"/>.
       /// </summary>
       DM_OUT_BUFFER = DM_COPY,
    
       /// <summary>
       /// Input value. The function presents the printer driver's Print Setup property
       /// sheet and then changes the settings in the printer's DEVMODE data structure
       /// to those values specified by the user. 
       /// This value is also defined as <see cref="DM_PROMPT"/>.
       /// </summary>
       DM_IN_PROMPT = DM_PROMPT,
    
       /// <summary>
       /// Input value. Before prompting, copying, or updating, the function merges 
       /// the printer driver's current print settings with the settings in the DEVMODE
       /// structure specified by the pDevModeInput parameter. 
       /// The function updates the structure only for those members specified by the
       /// DEVMODE structure's dmFields member. 
       /// This value is also defined as <see cref="DM_MODIFY"/>. 
       /// In cases of conflict during the merge, the settings in the DEVMODE structure 
       /// specified by pDevModeInput override the printer driver's current print settings.
       /// </summary>
       DM_IN_BUFFER = DM_MODIFY,
    }