DocumentProperties not updating the print job with new DEVMODE structure


I am trying to update a print job with a new property set to the printer's DEVMODE structure in my C# win forms application. Specifically, the tray to print to.

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public class DOCINFOA
                public string lpszDocName;

                public string lpszOutput;

                public string lpszDatatype;          

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
            private struct PRINTER_INFO_2
                public string pServerName;

                public string pPrinterName;

                public string pShareName;

                public string pPortName;

                public string pDriverName;

                public string pComment;

                public string pLocation;

                public IntPtr pDevMode;

                public string pSepFile;

                public string pPrintProcessor;

                public string pDatatype;

                public string pParameters;

                public IntPtr pSecurityDescriptor;

                public int Attributes;
                public int Priority;
                public int DefaultPriority;
                public int StartTime;
                public int UntilTime;
                public int Status;
                public int cJobs;
                public int AveragePPM;

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public class DEVMODE
                private const int CCHDEVICENAME = 32;
                private const int CCHFORMNAME = 32;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
                public string dmDeviceName;
                public ushort dmSpecVersion;
                public ushort dmDriverVersion;
                public ushort dmSize;
                public ushort dmDriverExtra;
                public uint dmFields;

                // values to set based on dmFields bits
                public short dmOrientation;
                public short dmPaperSize;
                public short dmPaperLength;
                public short dmPaperWidth;
                public short dmScale;
                public short dmCopies;
                public short dmDefaultSource;
                public short dmPrintQuality;

                public int dmPositionX;
                public int dmPositionY;
                public uint dmDisplayOrientation;
                public uint dmDisplayFixedOutput;

                public short dmColor;
                public short dmDuplex;
                public short dmYResolution;
                public short dmTTOption;
                public short dmCollate;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
                public string dmFormName;
                public ushort dmLogPixels;
                public uint dmBitsPerPel;
                public uint dmPelsWidth;
                public uint dmPelsHeight;
                public uint dmDisplayFlags;
                public uint dmDisplayFrequency;
                public uint dmICMMethod;
                public uint dmICMIntent;
                public uint dmMediaType;
                public uint dmDitherType;
                public uint dmReserved1;
                public uint dmReserved2;
                public uint dmPanningWidth;
                public uint dmPanningHeight;
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            public class PRINTER_DEFAULTS
                public IntPtr pDatatype;
                public IntPtr pDevMode;
                public int DesiredAccess;

            public enum FModes
                DM_SIZEOF = 0,
                DM_UPDATE = 1,
                DM_COPY = 2,
                DM_PROMPT = 4,
                DM_MODIFY = 8,
                DM_OUT_DEFAULT = DM_UPDATE,
                DM_OUT_BUFFER = DM_COPY,
                DM_IN_PROMPT = DM_PROMPT,
                DM_IN_BUFFER = DM_MODIFY,

            public enum DevModeFields : uint
                DM_ICMMETHOD = 0x10000,
                DM_FORMNAME = 0x00010000,
                DM_ICMINTENT = 0x04000000,
                DM_MEDIATYPE = 0x08000000,
                DM_DITHERTYPE = 0x10000000,
                DM_COPIES = 0x00000100,
                DM_DEFAULTSOURCE = 0x00000200,
                DM_PRINT_QUALITY = 0x00000400,
                DM_COLOR = 0x00000800,
                DM_DUPLEX = 0x00001000,
                DM_YRESOLUTION = 0x00002000,
                DM_TTOPTION = 0x00004000,
                DM_COLLATE = 0x00008000,
                DM_ORIENTATION = 0x00000001,
                DM_PAPERSIZE = 0x00000002,
                DM_PAPERLENGTH = 0x00000004,
                DM_PAPERWIDTH = 0x00000008,
                DM_SCALE = 0x00000010

            // DesiredAccess properties
            const int PRINTER_ACCESS_ADMINISTER = 0x4;
            const int PRINTER_ACCESS_USE = 0x8;
            const int STANDARD_RIGHTS_REQUIRED = 0xF0000;

            public enum PaperSource
                DMRES_HIGH = -4,
                DMRES_MEDIUM = -3,
                DMRES_LOW = -2,
                DMRES_DRAFT = -1,
                DMBIN_UPPER = 1,
                DMBIN_LOWER = 2,
                DMBIN_MIDDLE = 3,
                DMBIN_MANUAL = 4,
                DMBIN_ENVELOPE = 5,
                DMBIN_ENVMANUAL = 6,
                DMBIN_AUTO = 7,
                DMBIN_TRACTOR = 8,
                DMBIN_SMALLFMT = 9,
                DMBIN_LARGEFMT = 10,
                DMBIN_LARGECAPACITY = 11,
                DMBIN_CASSETTE = 14,
                DMBIN_FORMSOURCE = 15

            PRINTER_DEFAULTS settings = new PRINTER_DEFAULTS
                pDatatype = IntPtr.Zero,
                pDevMode = IntPtr.Zero,
                DesiredAccess = PRINTER_ACCESS_USE

            int bytesNeeded = 0;

            // get the printer handle
            if (OpenPrinter(szPrinterName.Normalize(), out IntPtr hPrinter, settings))
                // find out size needed for buffer first
                GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out bytesNeeded);
                if (bytesNeeded > 0)
                    // allocate memory for the printer info
                    IntPtr pPrinterInfo = Marshal.AllocHGlobal(bytesNeeded);

                    // fetch pointer to printer info at level 2 (gives us DEVMODE data)
                    if (GetPrinter(hPrinter, 2, pPrinterInfo, bytesNeeded, out _))
                        // convert the pointer to the readable data
                        PRINTER_INFO_2 printerInfo = (PRINTER_INFO_2)Marshal.PtrToStructure(pPrinterInfo, typeof(PRINTER_INFO_2));

                        // for some reason it didnt fetch the DEVMODE data we need, try getting it elsewhere
                        if (true)
                            // find out size needed for buffer first
                            bytesNeeded = DocumentProperties(IntPtr.Zero, hPrinter, printerInfo.pPrinterName, IntPtr.Zero, IntPtr.Zero, (int)FModes.DM_SIZEOF);
                            if (bytesNeeded > 0)
                                // allocate memory for the DEVMODE info
                                IntPtr pDevMode = Marshal.AllocHGlobal(bytesNeeded);

                                // fetch pointer to DEVMODE info
                                int result = DocumentProperties(IntPtr.Zero, hPrinter, szPrinterName.Normalize(), pDevMode, IntPtr.Zero, (int)FModes.DM_OUT_BUFFER);
                                if (result > 0)
                                    printerInfo.pDevMode = pDevMode;

                        // create the print job
                        DOCINFOA di = new DOCINFOA
                            lpszDocName = "My C#.NET RAW Document",
                            lpszDatatype = "RAW"

                        if (StartDocPrinter(hPrinter, 1, di))
                            if (StartPagePrinter(hPrinter))
                                // convert the pointer to readable data
                                DEVMODE dm = (DEVMODE)Marshal.PtrToStructure(printerInfo.pDevMode, typeof(DEVMODE));

                                // set new properties for printer
                                dm.dmFields |= (uint)DevModeFields.DM_DEFAULTSOURCE;
                                dm.dmDefaultSource = (short)PaperSource.DMBIN_UPPER;

                                Marshal.StructureToPtr(dm, printerInfo.pDevMode, false);

                                //overwrite the printers settings
                                int res = DocumentProperties(IntPtr.Zero, hPrinter, szPrinterName.Normalize(), printerInfo.pDevMode, printerInfo.pDevMode, (int)FModes.DM_IN_BUFFER | (int)FModes.DM_OUT_BUFFER);
                                if (res > 0)
                                    WritePrinter(hPrinter, pBytes, dwCount, out _);



The issue I am having is that the final call to DocumentProperties does not update the printer job settings. I've checked the structure is valid by inserting an IsDevmodeValid call each time I read or write to the structure and it returns ok. I've tried setting the DEVMODE structure both before and after the job is setup. It just does not update the settings when I view the print jobs properties.

Can anyone tell me what I'm missing here? FYI, it prints any documents fine, but to the printer's default tray.

asked on Stack Overflow Dec 21, 2020 by Maddy • edited Dec 21, 2020 by Useme Alehosaini

0 Answers

Nobody has answered this question yet.

User contributions licensed under CC BY-SA 3.0