Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control or access custom printer settings (Rotate 180 degrees) for printers in C#

I have multiple printers which contain a "Rotate 180 degree" checkbox like below:

enter image description here

Here's another one ("Nee" means "no"):

enter image description here

Is there a way to programmatically set this value, and change PrintTicket? If not, how can I get the current value of it? For my software I need to know if this is set or not and if it's set it needs to be changed, if possible. For my software it would be the best if I don't have to open the printdialog, it's a piece of software where users can print directly without clicking any buttons, so setting or getting it programmatically is what I'm looking for.

I've been searching around using ManagementObjectSearcher and in the normal PrintDocument.DefaultPageSettings property but couldn't find anything!

If this option isn't set, I automatically want to set it temporarily for the users (otherwise it prints upside down with my particular printers). I've been messing around trying to set it for the users but I can't figure out how to get it to work. I've been looking at the DEVMODE struct and tried to implement it but it also doesn't have a "rotate 180 degrees option" or anything similar.

NOTE: I'M NOT TRYING TO SET LANDSCAPE MODE. That's pretty easy and something different.

I tried doing the following: pdialog.PrintQueue.CurrentJobSettings.CurrentPrintTicket.PageOrientation = PageOrientation.ReversePortrait;

However, this only inverts the page orientation and not the content (the content needs to be rotated as well).

If I rotate the visual that I'm printing, the margins aren't right anymore so that doesn't work either! Hope someone can help.

Thanks in advance.

EDIT

I used Hans' method to figure out which value to change. Note that his method would apply for any type of printer! The devmode changes the value of the PrintDialog. This took me hours and hours to solve so for whoever I can help, here's my code and I'm glad to share or help! First I was trying to change DefaultPrintTicket from a new PrintServer() which didn't work, but UserPrintTicket seemed to be the right one where you can actually see the value change in WindowsControl Panel if you don't switch back to the original one. *However*, this only seemed to work on just my pc (which was necessary), on a virtual machine for example themagic` value was already different. This was rather practice than real-life usage.

var pdialog = new PrintDialog();

pdialog.PrintQueue = new PrintQueue(new LocalPrintServer(), _printername, PrintSystemDesiredAccess.AdministratePrinter); // this will be your printer. any of these: new PrintServer().GetPrintQueues()

pdialog.PrintTicket.PageMediaSize = size;
pdialog.PrintTicket.CopyCount = _amount;

if (CheckPrinterDriverName(_printername))
{
    int magic = 361;
    var defaulttckt = pdialog.PrintQueue.UserPrintTicket;
    pdialog.PrintQueue.UserPrintTicket = PrinterModifier.ChangeDevMode(pdialog, magic, vis);
    pdialog.PrintQueue.Commit();
    pdialog.PrintVisual(vis, "Label");
    //Set back old settings so it's not permanently changed
    pdialog.PrintQueue.UserPrintTicket = defaulttckt;
    pdialog.PrintQueue.Commit();
}

...

class PrinterModifier
{
    public static PrintTicket ChangeDevMode(PrintDialog pdialog, int prpty, DrawingVisual vis)
    {
        var queue = pdialog.PrintQueue;
        var cvt = new PrintTicketConverter(queue.Name, PrintTicketConverter.MaxPrintSchemaVersion);
        // Display dialog, don't make changes

        var devmode1 = cvt.ConvertPrintTicketToDevMode(pdialog.PrintTicket, BaseDevModeType.UserDefault);
        // Consistency check
        var dmSize = BitConverter.ToInt16(devmode1, 68);
        var dmDriverExtra = BitConverter.ToInt16(devmode1, 70);

        if (devmode1[361] == 0)
        {
            devmode1[361] = 1;
        }
        return cvt.ConvertDevModeToPrintTicket(devmode1, PrintTicketScope.PageScope);
    }
}

The value in the printdialog changes, but it still prints wrong. The changes don't apply. Any help is welcome!

like image 948
Markinson Avatar asked Dec 08 '16 11:12

Markinson


People also ask

How do I access my Brother printer settings?

(Start button) => Control Panel => Hardware and Sound => Devices and Printers. Right-click your Brother machine icon and select Printer properties. NOTE: If you see the Change Properties button at the bottom left of the dialog box, click the Change Properties button before changing the settings.


2 Answers

It probably helps to realize how unusual this property is. Funny true story as introduction about Pete Conrad, an astronaut recruited by NASA for the Gemini space program. Having no real idea how long term space flight affects humans, the doctors exposed them to basically anything they could think of. Often very intrusive and uncomfortable tests. Conrad rebelled and failed one psychological test. The doc handed him a blank card and asked "What do you see?" He instantly shoved it back and remarked "It is upside down".

This printer is unusual, always something to worry about. Perhaps designed to operate on a production line that includes some kind of automated paper binding operation. Printer drivers can arbitrarily add properties to customize their behavior, this is one such property. The property sheet you are looking at is equally non-standard, it comes from the printer driver.

To understand how you can change this setting, you first need to understand the DEVMODE structure. About the ugliest structure used in the winapi. It is a variable size struct, the declaration you see in the MSDN page only covers the standard properties. A printer driver can arbitrarily extend it, the dmDriverExtra field keeps track of how many extra bytes were added. The dmSize field reports the size of the non-variable part of the struct, it is 220 on Windows version 6 (Vista and up). Something you need to check.

Winforms exposes the DEVMODE directly with the PrinterSettings.GetHdevmode() method. WPF abstracts it away through the PrintTicket class. You have to use the PrintTicketConverter class to convert from a PrintTicket to a DEVMODE and back.

What you need to reverse-engineer is the exact field in the private driver data that stores this setting. Only the printer manufacturer knows this detail, they are not going to answer your phone call. Some sample code to help you discover and verify that field, just try it out in a do-nothing WPF app in the MainWindow constructor:

using System.Printing;          // Add reference to System.Printing
using System.Printing.Interop;  // Add reference to ReachFramework
using System.Diagnostics;
....

    public MainWindow() {
        InitializeComponent();
        // Assume default printer
        var queue = new LocalPrintServer().DefaultPrintQueue;
        var cvt = new PrintTicketConverter(queue.Name, PrintTicketConverter.MaxPrintSchemaVersion);
        // Display dialog, don't make changes
        var dlg = new PrintDialog();
        dlg.ShowDialog();
        var devmode1 = cvt.ConvertPrintTicketToDevMode(dlg.PrintTicket, BaseDevModeType.UserDefault);
        // Consistency check
        var dmSize = BitConverter.ToInt16(devmode1, 68);
        var dmDriverExtra = BitConverter.ToInt16(devmode1, 70);
        Debug.Assert(dmSize == 220);
        Debug.Assert(dmDriverExtra > 0);
        Debug.Assert(dmSize + dmDriverExtra == devmode1.Length);
        // Display dialog again, do make the change
        dlg.ShowDialog();
        var devmode2 = cvt.ConvertPrintTicketToDevMode(dlg.PrintTicket, BaseDevModeType.UserDefault);
        var len = Math.Min(devmode1.Length, devmode2.Length);
        for (int ix = 0; ix < len; ++ix) {
            if (devmode1[ix] != devmode2[ix]) {
                Debug.Print("Change at {0} from {1} to {2}", ix, devmode1[ix], devmode2[ix]);
            }
        }
        // Tinker with the DEVMODE...
        var magic = dmSize + 0;   // Change this
        Debug.Assert(magic < dmSize + dmDriverExtra);
        devmode1[magic] = devmode2[magic];
        dlg.PrintTicket = cvt.ConvertDevModeToPrintTicket(devmode1);
        // Verify that the setting changed!
        dlg.ShowDialog();
    }
}

Beware that you might see many bytes change. Most likely guess is that you'd be looking for one that changes from 0 to 1. The Debug > Windows > Memory > Memory1 window can be useful to filter out noise produced by strings, put "devmode2" in the Address field.

The expected outcome is that you now know how to customize the PrintTicket. Beware that it is highly specific to that printer and can change with a driver update. So you may well consider to do this the most logical way, rotating the visual to be printed with the LayoutTransform property.

like image 116
Hans Passant Avatar answered Oct 26 '22 02:10

Hans Passant


try This. "LandscapeAngle" will return the orientation set in the print dialog control.

PrintDialog.PrinterSettings.LandscapeAngle

Ref: https://msdn.microsoft.com/en-us/library/system.drawing.printing.printersettings(v=vs.110).aspx

like image 34
Vidura Silva Avatar answered Oct 26 '22 04:10

Vidura Silva