At Skiviez, we print out a full-color eight page customized flyer for every one of our orders. As soon as the order is done being processed, our internal processing application generates the flyer and shoots it off to one of the copier machines. The copier machine prints out the flyer (duplex), folds it, and staples it. This all happens silently and automatically; the employee does not have to twiddle with printer configuration or driver settings.
Getting this to happen automatically, however, was not easy. If you’re used to using the PrinterSettings and PrintDocument classes in Windows Forms, you’ll know that you have some options like a Duplex option available. But in our case, using that Duplex property seemed to make no difference in the printed output; the ol’ copier stubbornly went simplex every single time. It Simply Did Not Work. Not to mention that the .NET-native API doesn’t even begin to provide support for specifying paper folding, saddle stitching, and stapling. In a quest to find out how I could automate the selection of all these settings, I learned a bit more about printer drivers on Windows than I cared to know.
To get down and dirty with printer settings, I had to get familiar with DEVMODE. Being a .NET weenie who really doesn’t know his way around the Windows API very well, discovering this was progress; this structure exposes some of the options that I needed (such as collation), but it still didn’t provide any programmatic way to access folding and stapling settings.
Let’s go into the Printers section of the Control Panel and look at the Printing Preferences for one of these copiers. It turns out that this is a good way to be able to tell where a configuration setting lives. Some of the universal, standard settings are displayed on the “Layout” and “Paper/Quality” tabs. But if I want to get to any of the fancy features such as folding or stapling, well, they’re specific to our unique printer driver (and indeed, they’re exposed on a custom tab called “Fiery Printing” that the printer driver provides). There’s approximately zero documentation out there for configuring Fiery printer drivers programmatically, so how the hell was I going to communicate with this black box?
I’m not sure if my solution is genius, expected, or insane, but it’s been working for over a year now. The secret is a curious little member of the DEVMODE structure called dmDriverExtra. MSDN has the following to say about this little member:
Contains the number of bytes of private driver-data that follow this structure. If a device driver does not use device-specific information, set this member to zero.
Private driver data, I thought. How interesting. It’s perfectly logical to assume that since the stapling and folding features are unique to this printer, then the configuration for those features is stored in some unknown format. That data lives in a block of memory at the end of the DEVMODE structure. The size of that block of memory is the value given in the dmDriverExtra field.
Here’s a crazy idea, I continued. What if I configured the default printer settings via the Printer Preferences pane in the Control Panel to the exact settings that I want the fliers to print with. Then, I’ll write a program that dumps the default DEVMODE structure and its private data for the printer to a file. Then, I can revert the default printer settings in the Control Panel back to normal.
When I want to print the flyer, I’ll just obtain the default DEVMODE, overwrite its spot in memory with the version that I had saved to disk, and then send the document to the printer. That way, I can specify those proprietary folding and stapling settings without actually knowing exactly how they’re defined in memory!
It turns out that this ended up working quite well. Obviously, things will blow up if we ever change the version of the printer driver that’s installed on the server (since they’ll probably have changed the layout of their private data section). Luckily, though, we don’t really plan on changing things once they’re working.
Dumping the DEVMODE
So, first I went into the Control Panel and selected the duplexing, folding, and stapling options that I wanted in the print driver-supplied tab. Then I ran a quick and dirty console application like the following (I swiped the P/Invoke version of DEVMODE from pinvoke.net:
class Program2 { #region P/Invoke [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalFree(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalLock(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalUnlock(IntPtr handle); #endregion #region Constants private const string DUMP_PATH = "DevModeDump.bin"; #endregion static void Main(string[] args) { IntPtr hDevMode; // handle to the DEVMODE IntPtr pDevMode; // pointer to the DEVMODE DEVMODE devMode; // the actual DEVMODE structure PrinterSettings printerSettings; // our flyer's printer settings Fall2008LargeFlyerPrintDocument flyer; // our custom subclass of PrintDocument flyer = new Fall2008LargeFlyerPrintDocument(); flyer.PrintController = new StandardPrintController(); printerSettings = flyer.PrinterSettings; printerSettings.PrinterName = "Fiery X3E"; // Get a handle to a DEVMODE for the default printer settings hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); // Obtain a lock on the handle and get an actual pointer so Windows won't // move it around while we're futzing with it pDevMode = GlobalLock(hDevMode); // Marshal the memory at that pointer into our P/Invoke version of DEVMODE devMode = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE)); // Read the bytes starting at that pointers position in memory into our // file stream using (FileStream fs = new FileStream(DUMP_PATH, FileMode.Create)) { for (int i = 0; i < devMode.dmSize + devMode.dmDriverExtra; ++i) { fs.WriteByte(Marshal.ReadByte(pDevMode, i)); } } // Unlock the handle, we're done futzing around with memory GlobalUnlock(hDevMode); // And to boot, we don't need that DEVMODE anymore, either GlobalFree(hDevMode); } } |
Using the saved DEVMODE for printing
Now what do I do when I want to crank these suckers out? I just take my saved DEVMODE and overwrite the one that I get in memory when I’m about to print. It’s something like this:
class Program3 { #region P/Invoke [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalFree(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalLock(IntPtr handle); [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalUnlock(IntPtr handle); #endregion #region Constants private const string DUMP_PATH = "DevModeDump.bin"; #endregion static void Main(string[] args) { IntPtr hDevMode; // a handle to our current DEVMODE IntPtr pDevMode; // a pointer to our current DEVMODE PrinterSettings printerSettings; // our flyer's printer settings Fall2008LargeFlyerPrintDocument flyer; // our custom subclass of PrintDocument byte[] savedDevMode; // will hold the DEVMODE we saved earlier Stream savedDevStream; // used to read the DEVMODE we saved earlier flyer = new Fall2008LargeFlyerPrintDocument(); renderedFlyer.PrintController = new StandardPrintController(); printerSettings = renderedFlyer.PrinterSettings; printerSettings.PrinterName = "Fiery X3E"; // Obtain the current DEVMODE position in memory hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings); // Obtain a lock on the handle and get an actual pointer so Windows won't move // it around while we're futzing with it pDevMode = GlobalLock(hDevMode); // Load the saved DEVMODE that we dumped earlier savedDevStream = new FileStream(DUMP_PATH, FileMode.Open, FileAccess.Read); savedDevMode = new byte[savedDevStream.Length]; savedDevStream.Read(savedDevMode, 0, savedDevMode.Length); savedDevStream.Close(); savedDevStream.Dispose(); // Overwrite our current DEVMODE in memory with the one we saved. // They should be the same size since we haven't like upgraded the OS // or anything. for (int i = 0; i < savedDevMode.Length; ++i) { Marshal.WriteByte(pDevMode, i, savedDevMode[i]); } // We're done futzing GlobalUnlock(hDevMode); // Tell our printer settings to use the one we just overwrote printerSettings.SetHdevmode(hDevMode); // It's copied to our printer settings, so we can free the OS-level one GlobalFree(hDevMode); // Print and we're done! renderedFlyer.Print(); renderedFlyer.Dispose(); } } |
Is it insane? Probably. But watching it duplex, fold, and staple automatically is great. I love it when something Simply Works!


That was beautiful. I ain’t going there, but talk about an elegant way around a clunky, futzy PoS handling of printer settings.
Hi Nicholas,
Thanks that I found your blog, quite relieve after a while searching for answer to print using finisher.
Actually I’m not yet try, but this solution is quite obvious work.
Regards, Hendri
Great! Let me know if it works out for you. For me it was a good way to get around a poorly documented, probably poorly written driver. If the driver has documentation, calling ExtEscape() via P/Invoke might have better results. But hey, as I always say, once it works–stop coding! Good luck!
Hi Nicholas,
Seems I found a problem regarding to the devMode.dmSize and devMode.dmDriverExtra cause after I get the DEVMODE from the printer the devMode.dmSize give me “0″.
Seems that my printer driver didn’t using devMode.dmDriverExtra.
Still looking for another method to get the devMode.dmSize?
I think it’s just close but seems still have a things to do.
Just for your info actually printer driver can save DEVMODE in windows registry, it’s possible to use DEVMODE in windows registry.
Regards, Hendri
.Hendri,
I found for me that the DevMode structure definition linked to was incorrect in that it did not marshal using unicode. Initially I got zero for the DriverExtra also…after I altered the StructLayout to be what I show below, it seemed to work fine…
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
I’ve seen several structs at PInvoke.Net show this issue – in general, check the strings and see if they contain only one character…if so there is a good chance this is the issue…
Michael,
You’re right. In my code I had set the Charset to CharSet.Auto, which leaves it up to magic marshalling code in the framework to make the decision for me. Leaving it out–as is done in the pinvoke.net wiki page–will use Charset.Ansi as the default value, and that won’t work. I’ve run into a similar issue as you describe as well in other P/Invoke APIs, most notably the other printing APIs. Given that most of us aren’t in the business of supporting Windows 9x anymore, I usually default to specifying Unicode for everything (I think a default FxCop rule even suggests this).
Hi, I having an issue with HP printer, sometimes when i sent to print a document changes it original size of the document (the document is actually an image), i can’t reproduce every time. It’s random. I tried your solution but with no success. Any ideas?
Thank you, thank you, thank you! That’s so awsome!! You saved me a lot of work this week with your piece of code.
Greetings, arnd.
Nice approach. It really helped me figure out a problem I was having where I needed to figure out the Printing Preferences that were set on a print queue.
I couldn’t quite get the DEVMODE approach to work but the step of creating a StandardPrintController() allowed the PrinterSettings.DefaultPageSettings to get correctly populated.
Thanks again, Dan
I’m curious, have you ever tried to combine this w/ printing word or excel documents, and if so what kind of results did you get?
Yes, even 2 1/2 years later, this solution STILL works. I stumbled on this trying to find a way to automatically select the paper tray.
Thank you for your efforts!
Thank you for writing up your solution to this problem, I really like how you solved the problem. I had an issue somewhat similar, but I solved it a totally different way. I’ll share so you can decide if my approach is “genius, expected or insane”
What I did was create an entirely new printer with a uniquely indicative name on the print server that points to the same IP address. (I’m using a windows print server, so this might not work if you have a different setup) For this second printer, I clicked Properties>Advanced Tab>Printing Defaults Button, then I configured all of my printing options (default drawer, paper type, stapling options) and saved it. Theoretically anybody who connects to that printer now gets those options as the default. Then in my code, iterate through all connected printers looking for an indicative string in the name and print the job to that printer. I search for a string instead of hardcoding it because I have 2 departments doing the same task to two seperate printers, so all I need to worry about is getting each profile connected to the right printer and the app just works after that.
Why thank you Kirby, that’s an excellent idea! In my case, I’ll setup 3 different versions of the driver in additional to the regular driver, so that I can have double-sided/stapled, and two other variants for different versions of the pre-loaded letter head in different drawers. A very simple solution that I can extend to everyone on the network without hardly writing a single piece of code or modifying each workstation. Thanks!
Could the saved DEVMODE file be used across multiple machines, or would it be machine specific?
Hello, I did something similar some years ago with C++/MFC application. So I can confirm that the Idea is correct… and I’m currently trying to do the same with c#.
About the portability between different machines, I think that it should work.
In the past I had the need to save ALSO additional infos (I don’t remember why… but the structure was “devnames”; it should store the printer “properties”).
In fact, I had problems using the same “printer profile” (devname+devmode) between different machines, because what was changing was the “port name”.. information that is stored in “devnames” structure. So… if you use just “devmode”, you shouldn’t have problem.
Hi there This worked like a charm thank you very much but is there a way to print PDF documents this way. Thanks
Thx, For also saving and loading dmDriverExtra Bytes I needed to use GlobalSize as follows: … [DllImport("kernel32.dll", ExactSpelling = true)] public static extern IntPtr GlobalSize(IntPtr handle); … devMode = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE)); devMode.dmSize = (short)Marshal.SizeOf(devMode); int isize = GlobalSize(hDevMode).ToInt32() – (int)devMode.dmSize; devMode.dmDriverExtra = Convert.ToInt16(isize); …
To receive the value for dmDriverExtra I needed to use GlobalSize