views:

280

answers:

1

I would like to be able to change the printer properties without bringing up the printer properties window...

Using the DocumentProperties (imported from winspool.drv) function has so far failed, because while it is easy to suppress the dialog from showing up, it seems that the value returned by PrinterSettings.GetHdevmode() is not reflecting the PrinterSettings that is calling it, but instead the value from the previous printer properties returning OK. For example, this gives me the previous (wrong) values from the last call to the properties, instead of the values it should have from the PrinterSettings object:

IntPtr hdevmode = PrinterSettings.GetHdevmode(PrinterSettings.DefaultPageSettings);
PrinterSettings.SetHdevmode(hdevmode);
PrinterSettings.DefaultPageSettings.SetHdevmode(hdevmode);

So does GetHdevmode have a bug or is this what its supposed to do? Is there a C# work around for this or does anyone even have any information about it? I have been hard pressed even to find info on the topic.

Thanks in advance for any insight.

EDIT: I didn't want to make this too personal of a problem, but hopefully having all the info in this case can provide an answer that is a useful solution for others too.

Here is a C++ DLL I have written in order to have a workaround for this issue. Its not currently working - it changes other memory such as copies, and doesn't succeed in changing the "underlying" papersize. I thought all I needed to do was specify the out buffer flag in order to make the changes?

extern "C" __declspec(dllexport) DEVMODE* __stdcall GetRealHDevMode(int width, int height, char *printerName, DEVMODE* inDevMode)
    {
//declare handles and variables
HANDLE printerHandle;
LPHANDLE printerHandlePointer(&printerHandle);

//get printer handle pointer
OpenPrinter((LPWSTR)printerName, printerHandlePointer, NULL);

//Get size needed for public and private devmode data and declare devmode structure
size_t devmodeSize = DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, NULL, NULL, 0);
DEVMODE* devmode = reinterpret_cast<DEVMODE*>(new char[devmodeSize + sizeof(DEVMODE) + sizeof(inDevMode->dmDriverExtra)]);

//lock memory
GlobalLock(devmode);

//fill the out buffer
DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, devmode, NULL, DM_OUT_BUFFER);

//change the values as required
devmode->dmPaperWidth = width;
devmode->dmPaperLength = height;
devmode->dmPaperSize = DMPAPER_USER;

devmode->dmFields &= ~DM_PAPERSIZE;
devmode->dmFields &= ~DM_PAPERLENGTH;
devmode->dmFields &= ~DM_PAPERWIDTH;
devmode->dmFields |= (DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH);

//input flag on now to put the changes back in
DocumentProperties(NULL, printerHandle, (LPWSTR)printerName, devmode, devmode, DM_IN_BUFFER | DM_OUT_BUFFER);

//unlock memory
GlobalUnlock(devmode);

//return the devmode that was used to alter the settings
return devmode;
    }

I figured the C++ code was enough to change the settings, so all I do in C# is this:

public PrinterSettings ChangePrinterProperties(PrinterSettings inPrinterSettings)
    {
        IntPtr TemphDevMode = inPrinterSettings.GetHdevmode(inPrinterSettings.DefaultPageSettings);
        IntPtr hDevMode = GetRealHDevMode((int)(inPrinterSettings.DefaultPageSettings.PaperSize.Width * 2.54F),
            (int)(inPrinterSettings.DefaultPageSettings.PaperSize.Height * 2.54F),
            inPrinterSettings.PrinterName, TemphDevMode);
        GlobalFree(hDevMode);
        return inPrinterSettings;
    }

UPDATE: Changed up the order a bit with dmPaperSize and dmFields. Improved results; not quite there yet.

UPDATE 2: Okay, I found a microsoft page that says the documentation is wrong. MSDN says to set dmPaperSize to 0 when you want to specify width and height whereas the Microsoft Support correction says to set it to DMPAPER_USER. http://support.microsoft.com/kb/108924

+1  A: 

There are 2 problems with the way you are specifying the paper size in the DEVMODE:

(1) If you specify DM_PAPERWIDTH or DM_PAPERLENGTH or both, you MUST NOT also set the DM_PAPERSIZE bit. It depends on the printer driver, but many drivers will ignore DM_PAPERLENGTH/WIDTH in the above code.

(2) Many drivers don't support DM_PAPERLENGTH/WIDTH at all. With such drivers, you simply cannot set the paper size like you are trying to do above. You can only select one of the predefined dmPaperSizes.

You can use DeviceCapabilities(DC_FIELDS) to determine if your driver supports DM_PAPERLENGTH/WIDTH.

You can use DeviceCapabilities(DC_PAPERS) to enumerate the allowable dmPaperSizes.

richb
I think this has set me on the right track... though MSDN does say to set dmPaperSize to zero: http://msdn.microsoft.com/en-us/library/ms896928.aspx
Brandi
Update: I found this: http://support.microsoft.com/kb/108924 which says 0 is not the correct value.
Brandi