【问题标题】:Control or access custom printer settings (Rotate 180 degrees) for printers in C#在 C# 中控制或访问打印机的自定义打印机设置(旋转 180 度)
【发布时间】:2017-04-23 15:06:58
【问题描述】:

我有多个打印机,其中包含一个“旋转 180 度”复选框,如下所示:

这是另一个(“Nee”的意思是“不”):

有没有办法以编程方式设置此值并更改PrintTicket?如果没有,我怎样才能得到它的当前值?对于我的软件,我需要知道是否设置了它,如果可能的话,是否需要更改它。对于我的软件来说,最好不用打开打印对话框,它是一款用户无需点击任何按钮就可以直接打印的软件,所以我正在寻找以编程方式设置或获取它的软件。

我一直在使用ManagementObjectSearcher 和普通的PrintDocument.DefaultPageSettings 属性进行搜索,但找不到任何东西!

如果未设置此选项,我会自动为用户临时设置它(否则它会在我的特定打印机上颠倒打印)。我一直在尝试为用户设置它,但我不知道如何让它工作。我一直在查看 DEVMODE 结构并尝试实现它,但它也没有“旋转 180 度选项”或类似的东西。

注意:我不想设置横向模式。这很简单,而且有些不同。

我尝试执行以下操作: pdialog.PrintQueue.CurrentJobSettings.CurrentPrintTicket.PageOrientation = PageOrientation.ReversePortrait;

但是,这只会反转页面方向而不是内容(内容也需要旋转)。

如果我旋转正在打印的视觉对象,则边距不再正确,因此也不起作用!希望有人可以提供帮助。

提前致谢。

编辑

我使用 Hans 的方法来确定要更改的值。请注意,他的方法适用于任何类型的打印机! devmode 更改 PrintDialog 的值。这花了我几个小时来解决,所以对于我能帮助的人,这是我的代码,我很高兴分享或帮助!首先,我试图将 DefaultPrintTicketnew PrintServer() 更改为无效,但 UserPrintTicket 似乎是正确的,您可以实际看到 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 中的值变化的值已经不同。这更像是实践而不是现实生活中的使用。

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);
    }
}

printdialog 中的值发生了变化,但仍然打印错误。更改不适用。欢迎任何帮助!

【问题讨论】:

  • 不能自动画180度吗?
  • @DanielA.White 检查我的更新,希望这能让我更清楚为什么我需要访问它!
  • 听起来像是 x-y 问题。
  • 我的评论可能跑题了,但是当用户选择Landscape 或多个副本或选择反向顺序时,您是否显示MessageBox?可能您会找到解决方案,但试图满足这样的要求听起来很浪费时间。
  • 如果我说它是关于一个标签打印机,所有东西都需要在一个 1 英寸的小正方形上打印,也许更有意义?如果用户不小心选中了该复选框,它将不再在标签上打印,而是在包含贴纸的纸上打印,如果这有意义的话

标签: c# wpf winforms printing


【解决方案1】:

您是否尝试过 Print Spooler API?

https://msdn.microsoft.com/en-us/library/windows/desktop/dd162863(v=vs.85).aspx

Pinvoke.net 展示了从 C# 中的 dll 调用的 C# 语法。

在 C# 中打开对话框的示例:

http://hintdesk.com/c-invoke-printer-properties-dialog/

【讨论】:

    【解决方案2】:

    试试这个。 "LandscapeAngle" 将返回打印对话框控件中设置的方向。

    PrintDialog.PrinterSettings.LandscapeAngle
    

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

    【讨论】:

    • 这不起作用。横向角度与“旋转 180 度”复选框不同。
    【解决方案3】:

    了解这个属性有多么不寻常可能会有所帮助。有趣的真实故事介绍了美国宇航局为双子座太空计划招募的宇航员皮特康拉德。由于不知道太空飞行对人类的长期影响有多大,医生们基本上让他们接触了他们能想到的任何东西。通常是非常侵入性和不舒服的测试。康拉德叛逆并未能通过一项心理测试。医生递给他一张空白卡片,问道:“你看到了什么?”他立即将它推回去,并说“它是颠倒的”。

    这台打印机很不寻常,总是让人担心。也许设计用于在包括某种自动化纸张装订操作的生产线上运行。打印机驱动程序可以任意添加属性来自定义它们的行为,这就是这样的属性之一。您正在查看的属性表同样非标准,它来自打印机驱动程序。

    要了解如何更改此设置,您首先需要了解the DEVMODE structure。关于winapi中使用的最丑的结构。它是一个可变大小的结构,您在 MSDN 页面中看到的声明仅涵盖标准属性。打印机驱动程序可以任意扩展它,dmDriverExtra 字段跟踪添加了多少额外字节。 dmSize 字段报告结构的非可变部分的大小,在 Windows 版本 6(Vista 及更高版本)上为 220。您需要检查的东西。

    Winforms 直接使用 PrinterSettings.GetHdevmode() 方法公开 DEVMODE。 WPF 通过 PrintTicket 类将其抽象出来。您必须使用 PrintTicketConverter class 从 PrintTicket 转换为 DEVMODE 并返回。

    您需要对存储此设置的私有驱动程序数据中的确切字段进行逆向工程。只有打印机制造商知道这个细节,他们不会接听您的电话。一些示例代码可帮助您发现和验证该字段,只需在 MainWindow 构造函数中的无操作 WPF 应用程序中尝试一下即可:

    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();
        }
    }
    

    请注意,您可能会看到许多字节发生变化。最有可能的猜测是,您正在寻找一个从 0 变为 1 的变量。Debug > Windows > Memory > Memory1 窗口可用于过滤字符串产生的噪音,将“devmode2”放在地址字段中。

    预期结果是您现在知道如何自定义 PrintTicket。请注意,它高度特定于该打印机,并且可以随着驱动程序更新而改变。因此,您可能会考虑以最合乎逻辑的方式执行此操作,旋转要使用 LayoutTransform property 打印的视觉对象。

    【讨论】:

    • 您的 670k 声望肯定不是无缘无故的……很棒的一段代码!有一件事我还没有得到。如果我单击复选框(我认为一定是它!!),输出会显示Change at 361 from 0 to 1。但是,当我将行更改为 `var magic = dmSize + 361` 时,没有任何变化。但是当我将您的代码更改为devmode1 = devmode2 时,我得到了预期的结果。我应该用var magic = dmSize + 0 做什么?我想我理解那条线错了。
    • 抱歉,这可能更明显。使用var magic = 361;
    • 成功了,非常感谢您的帮助和解释。
    • 结果比我想象的要好。恭喜。
    猜你喜欢
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 2023-04-09
    • 2014-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-08
    相关资源
    最近更新 更多