Can't change DEVMODE of a printer

4.1k Views Asked by At

I need to change DEVMODE of printer for current printing task to pass standard and device-specific settings. I do the following:

PrintDocument d = new PrintDocument();
d.PrinterSettings.PrinterName = "Microsoft XPS Document Writer"; // example printer name           
byte[] devmode_data; // contains a valid value that is obtained from registry
IntPtr devmode = IntPtr.Zero;
GCHandle handle = GCHandle.Alloc(devmode_data, GCHandleType.Pinned);
try
{
    devmode = handle.AddrOfPinnedObject();
    if (devmode != IntPtr.Zero) d.PrinterSettings.SetHdevmode(devmode);
}
finally
{
    if (handle.IsAllocated) handle.Free();
}

It fails when I attempt to execute PrinterSettings.SetHdevmode with a NullReferenceException and without any meaningful error info. d.PrinterSettings is not null, the exception is thrown inside of PrinterSettings.SetHdevmode method.
So my question is: what is wrong? Is the byte[] to IntPtr cast wrong? Maybe SetHdevmode expects something other than a byte[] array?

I get the byte[] devmode_data array from the registry. It is a valid value and it is same value that is used in current printer settings.

2

There are 2 best solutions below

5
On

I modified your code in the following way, since I don't have any valid data for devmode_data:

devmode = d.PrinterSettings.GetHdevmode();
if (devmode != IntPtr.Zero) d.PrinterSettings.SetHdevmode(devmode);

and now there is no exception here.

Please, provide me with your data for devmode_data or check for your own, if it is valid or not!

0
On

SetHdevmode expects an HGLOBAL. You can get an HGLOBAL from .Net via Marshal.AllocHGlobal. Then, you can use Marshal.Copy(byte[], int, IntPtr, int) to copy from your managed byte array to the HGLOBAL. See below:

var pDevMode = Marshal.AllocHGlobal(devmode_data.Length);
Marshal.Copy(devmode_data, 0, pDevMode, devmode_data.Length);

d.PrinterSettings.SetHdevmode(pDevMode);
Marshal.FreeHGlobal(pDevMode);

The byte array can be partially handled as a structure, but that will require p/Invoke definitions. The PrinterSettings class, however, will not accept a structure, so doing so would be unneeded in this case. Additionally, the DEVMODE structure is variable length to allow for printer drivers to add additional opaque data, so it would not be possible to convert without data loss.

See How can I save and restore `PrinterSettings`? for more.