none
プリンタドライバの詳細設定を直接行い一部変更して印刷したい RRS feed

  • 質問

  • VS2017のMicrosoft Visual C#(WPF)を使用しています
    計測用プログラムを作成していますが、印刷時にプリンタドライバの設定を反映したいと思っています
    印刷書式はユーザコントロールで作成しています
    今のところ複数ページの対応はできていません
    プリンタドライバの設定は以下の文献を参照して表示することはできましたが、OKを押しても記憶されません(使用中プログラム内での限定的な変更を想定しています)

    また、印刷はPrintDialog関数を使って、紙サイズのA4指定と縦横切替、ユーザコントロールを印刷サイズのフィッティングを行っているためどのように融合したら良いのかわかっていません
    イメージは、設定ボタンを押してプリンタドライバの設定を行ってその内容を記憶し、その内容にプログラム側で紙サイズの取得と縦横を指定して印刷したいと考えています
    例えばグレースケールモードに変更するとかプリンタ固有機能を活用したいです
    同じプリンタで縦と横で自動的に印刷することも想定しているため、PrintDialogのウィンドウは表示しません
    解決方法ありそうでしたら是非教えて頂きたくよろしくお願いします

    参照にした文献
    https://stackoverflow.com/questions/9704671/modifying-printersettings-after-printdialog-was-shown
    https://social.msdn.microsoft.com/Forums/vstudio/en-US/0dc695c1-578d-4da5-8f68-b2a257846c02/advanced-printer-properties-dialog-in-wpf?forum=wpf

    気になる文献
    https://ykobosknow.info/csharp/save-printer-setting.html
    https://dobon.net/vb/bbs/log3-47/28144.html


    ==== プログラムの印刷部分(縦印刷を抜粋) ====
    ※PRINTVIEWは縦用印刷イメージとなるユーザコントロール

    using System.Printing;        //印刷(System.Printing,ReachFramework参照追加)

    PrintDialog pd = new PrintDialog();
    LocalPrintServer lps = new LocalPrintServer();

    pd.PrintQueue = new PrintQueue(lps, GraphSelectedPrinter1);        //出力先選択
    pd.PrintTicket.PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4);        //用紙選択(A4)
    PageImageableArea area = pd.PrintQueue.GetPrintCapabilities().PageImageableArea;        //デフォルトページサイズ取得
    pd.PrintTicket.PageOrientation = System.Printing.PageOrientation.Portrait;        //ページ方向指定(縦長/ポートレイト)
    PRINTVIEW.Measure(new Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight));        //ページピクセル数設定
    PRINTVIEW.Arrange(new Rect(new Point(0, 0), new Point(area.OriginWidth + area.ExtentWidth, area.OriginHeight + area.ExtentHeight)));        //ページサイズ設定
    ~~~ ユーザコントロールに印刷イメージを描画(グラフ、表など) ~~~
    PRINTVIEW.UpdateLayout();
    pd.PrintVisual(PRINTVIEW, "計測結果の印刷");        //印刷指示


    ==== プリンタドライバ設定画面を開くプログラム部分 ====
    using System.Drawing.Printing;        //PrinterSettings
    using System.Runtime.InteropServices;        //DLL

    [DllImport("winspool.drv", SetLastError = true)]
    static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault);

    [DllImport("winspool.drv", SetLastError = true)]
    static extern int ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", EntryPoint = "DocumentPropertiesW", SetLastError = true,
        ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        static extern int DocumentProperties(
        IntPtr hwnd, IntPtr hPrinter,
        [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName,
        IntPtr pDevModeOutput,
        IntPtr pDevModeInput,
        int fMode);

    //ボタンを押す(PrinterProperty_Click)とプリンタドライバ設定画面が開く
    private void PrinterProperty_Click(object sender, RoutedEventArgs e)
    {
        string printername = (インストールされているプリンタの名前)
        PrinterSettings ps = new PrinterSettings();
        ps.PrinterName = printername;
        OpenPrinterPropertiesDialog((new System.Windows.Interop.WindowInteropHelper(this)).Handle,ps);
    }

    public static bool OpenPrinterPropertiesDialog(IntPtr hwnd, PrinterSettings ps)
    {
        //変数
        IntPtr hPrt = IntPtr.Zero;
        IntPtr hDevMode = ps.GetHdevmode(ps.DefaultPageSettings);
        IntPtr pDevModeInput = Marshal.AllocHGlobal(hDevMode);

        if (OpenPrinter(ps.PrinterName, out hPrt, IntPtr.Zero) == false)
        {
            return (false);
        }
        if (hPrt == IntPtr.Zero)
        {
            return (false);
        }

        int size = DocumentProperties(hwnd, hPrt, ps.PrinterName, IntPtr.Zero, IntPtr.Zero, 0);

        IntPtr pDevModeOutput = Marshal.AllocHGlobal(size);

        int ret = DocumentProperties(hwnd, hPrt, ps.PrinterName, pDevModeOutput, pDevModeInput, 14);
        if (ret == 1)
        {
            ps.SetHdevmode(pDevModeOutput);
        }

        Marshal.FreeHGlobal(pDevModeOutput);
        Marshal.FreeHGlobal(pDevModeInput);
        ClosePrinter(hPrt);

        return (true);
    }

    2020年5月17日 14:15

すべての返信

  • こんな

    namespace WpfApp1
    {
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Documents;
        using System.Windows.Input;
        using System.Windows.Media;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                Button button1 = new Button() { Content = "設定" };
                Button button2 = new Button() { Content = "印刷" };
    
                button1.Click += Button1_Click;
                button2.Click += Button2_Click;
    
                StackPanel stack = new StackPanel();
                stack.Children.Add(button1);
                stack.Children.Add(button2);
                this.Content = stack;
    
                this.SizeToContent = SizeToContent.WidthAndHeight;
            }
    
            private PrinterDEV dev;
    
            private void Button1_Click(object sender, RoutedEventArgs e)
            {
                dev = PrinterDEV.ShowPrintDialog(this.dev);
            }
    
            private void Button2_Click(object sender, RoutedEventArgs e)
            {
                if (dev == null)
                {
                    MessageBox.Show("設定されてません");
                    return;
                }
    
                var queue = dev.CreateQueue();
    
                if (System.Windows.Input.Keyboard.GetKeyStates(Key.LeftShift) == KeyStates.Down)
                {//左シフトキーが押されてたら横向きにしてみる
                    queue.UserPrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
                }
                if (System.Windows.Input.Keyboard.GetKeyStates(Key.RightShift) == KeyStates.Down)
                {//右シフトキーが押されてたらA5にしてみる
                    foreach (System.Printing.PageMediaSize ps in queue.GetPrintCapabilities(queue.UserPrintTicket).PageMediaSizeCapability)
                    {
                        if (ps.PageMediaSizeName == System.Printing.PageMediaSizeName.ISOA5)
                        {
                            queue.UserPrintTicket.PageMediaSize = ps;
                            break;
                        }
                    }
    
                }
    
                var caps = queue.GetPrintCapabilities(queue.UserPrintTicket);
                var area = caps.PageImageableArea;
    
                FixedPage page = new FixedPage();
                {
                    TextBlock block = new TextBlock()
                    {
                        Text = queue.UserPrintTicket.PageOrientation.ToString(),
                        FontSize = 20,
                        Foreground = Brushes.Red
                    };
                    Canvas.SetLeft(block, 10);
                    Canvas.SetTop(block, 10);
    
                    Border border = new Border
                    {
                        Width = caps.OrientedPageMediaWidth.Value,
                        Height = caps.OrientedPageMediaHeight.Value,
                        BorderBrush = Brushes.Blue,
                        BorderThickness = new Thickness(2, 2, 2, 2),
                        Background = Brushes.LightPink,
                        CornerRadius = new CornerRadius(10),
                    };
                    Canvas.SetLeft(border, 0);
                    Canvas.SetTop(border, 0);
    
                    Canvas canvas = new Canvas();
                    canvas.Children.Add(border);
                    canvas.Children.Add(block);
    
    
                    page.Width = caps.OrientedPageMediaWidth.Value;
                    page.Height = caps.OrientedPageMediaHeight.Value;
                    page.Children.Add(canvas);
                    page.Measure(new Size(page.Width, page.Height));
                    page.Arrange(new Rect(0, 0, page.Width, page.Height));
                }
    
                var writer = System.Printing.PrintQueue.CreateXpsDocumentWriter(queue);
                writer.Write(page);
            }
        }
    
        class PrinterDEV
        {
            public byte[] DEVMODE { get; set; }
            public string PrinterFullName { get; set; }
            public int ClientPrintSchemaVersion { get; set; }
    
            /// <summary>印刷キューを作る</summary>
            /// <returns></returns>
            public System.Printing.PrintQueue CreateQueue()
            {
                var svr = new System.Printing.LocalPrintServer();
                var queue = svr.GetPrintQueue(PrinterFullName);
    
                var conv = new System.Printing.Interop.PrintTicketConverter(PrinterFullName, ClientPrintSchemaVersion);
                queue.UserPrintTicket = conv.ConvertDevModeToPrintTicket(DEVMODE);
    
                return queue;
            }
    
            /// <summary>ダイアログを表示して印刷設定を取得</summary>
            /// <param name="devorg">ダイアログであらかじめ設定しておく設定値</param>
            /// <returns></returns>
            public static PrinterDEV ShowPrintDialog(PrinterDEV devorg)
            {
                PrintDialog dlg = new System.Windows.Controls.PrintDialog();
                if (devorg != null)
                {
                    var svr = new System.Printing.LocalPrintServer();
                    var queue = svr.GetPrintQueue(devorg.PrinterFullName);
                    if (queue != null)
                    {
                        dlg.PrintQueue = queue;
    
                        var conv = new System.Printing.Interop.PrintTicketConverter(devorg.PrinterFullName, devorg.ClientPrintSchemaVersion);
                        dlg.PrintTicket = conv.ConvertDevModeToPrintTicket(devorg.DEVMODE);
                    }
                }
    
                if (dlg.ShowDialog() == true)
                {
                    PrinterDEV ret = new PrinterDEV();
                    ret.PrinterFullName = dlg.PrintQueue.FullName;
                    ret.ClientPrintSchemaVersion = dlg.PrintQueue.ClientPrintSchemaVersion;
    
                    var conv = new System.Printing.Interop.PrintTicketConverter(dlg.PrintQueue.FullName, dlg.PrintQueue.ClientPrintSchemaVersion);
                    ret.DEVMODE = conv.ConvertPrintTicketToDevMode(dlg.PrintTicket, System.Printing.Interop.BaseDevModeType.PrinterDefault);
                    return ret;
                }
                else
                {
                    return devorg;
                }
            }
        }
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2020年5月17日 23:16
  • newon1さん、こんにちは。フォーラムオペレーターのHarukaです。
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    ご質問いただいた件ですが、その後いかがでしょうか。
    gekkaさんがご提示したサンプルを試しましたか。gekkaさんが与えたすべては適用がクリックされた後設定を保存することでした。 
    こちらでコードをテストしたところ、[適用]ボタンをクリックした後に設定を保存できました。

    どうぞよろしくお願いいたします。

    MSDN/ TechNet Community Support Haruka
    ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2020年5月22日 6:19
    モデレータ
  • 貴重な情報ありがとうございます
    まだ動作確認していないのですが、プリンタドライバの設定はPrintDialogのウィンドウを開いて行っているように思います
    今回のプログラムではプリンタ選択は別の場所で実施するため、winspool.drvから直接呼び出す設定ウィンドウを使用したいと考えています
    この場合、ウィンドウを開く際に設定を渡し、閉じたときに受け取る必要があると思います
    どのような方法でこれが実現できるか教えて頂けないでしょうか
    よろしくお願いします
    2020年5月24日 14:50
  • newon1 さま よろしく。

    gekka さまのコードを見ると、
    private PrinterDEV dev; とフィールド変数で定義していますね。
    ですから、
    PrintDialog pd = new PrintDialog(); そのもの、
    或いは、pd.PrintTicket の値を格納する printTicket を、
    フィールド変数 に定義(代入も忘れずに)して見て下さい。

    恐らく、Subroutine の中で定義している(ローカル/局所)ので、
    抜けると使えなくなっているだけでは無いですか?。

    • 編集済み ShiroYuki_Mot 2020年5月25日 8:28 語句のニュアンスを変更
    2020年5月25日 6:15
  • namespace WpfApp1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Documents;
        using System.Windows.Input;
        using System.Windows.Media;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                Button btnSelect = new Button() { Content = "選択" };
                Button btnDetail = new Button() { Content = "詳細", IsEnabled = false };
                Button btnPrint = new Button() { Content = "印刷", IsEnabled = false };
    
                btnSelect.Click += (s, e) =>
                {
                    dev = PrinterDEV.ShowPrintDialog(this.dev);
                    btnPrint.IsEnabled = btnDetail.IsEnabled = dev != null;
                };
    
                btnDetail.Click += (s, e) =>
                {
                    dev.ShowDetailDialog();
                };
    
                btnPrint.Click += Print_Click;
    
                StackPanel stack = new StackPanel();
                stack.Children.Add(btnSelect);
                stack.Children.Add(btnDetail);
                stack.Children.Add(btnPrint);
                this.Content = stack;
    
                this.SizeToContent = SizeToContent.WidthAndHeight;
            }
    
            private PrinterDEV dev;
    
            private void Print_Click(object sender, RoutedEventArgs e)
            {
                if (dev == null)
                {
                    MessageBox.Show("設定されてません");
                    return;
                }
    
                var queue = dev.CreateQueue();
    
                if (System.Windows.Input.Keyboard.GetKeyStates(Key.LeftShift) == KeyStates.Down)
                {//左シフトキーが押されてたら横向きにしてみる
                    queue.UserPrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
                }
                if (System.Windows.Input.Keyboard.GetKeyStates(Key.RightShift) == KeyStates.Down)
                {//右シフトキーが押されてたらA5にしてみる
                    foreach (System.Printing.PageMediaSize ps in queue.GetPrintCapabilities(queue.UserPrintTicket).PageMediaSizeCapability)
                    {
                        if (ps.PageMediaSizeName == System.Printing.PageMediaSizeName.ISOA5)
                        {
                            queue.UserPrintTicket.PageMediaSize = ps;
                            break;
                        }
                    }
                }
    
                var caps = queue.GetPrintCapabilities(queue.UserPrintTicket);
                var area = caps.PageImageableArea;
    
                FixedPage page = new FixedPage();
                {
                    TextBlock block = new TextBlock()
                    {
                        Text = queue.UserPrintTicket.PageOrientation.ToString(),
                        FontSize = 20,
                        Foreground = Brushes.Red
                    };
                    Canvas.SetLeft(block, 10);
                    Canvas.SetTop(block, 10);
    
                    Border border = new Border
                    {
                        Width = caps.OrientedPageMediaWidth.Value,
                        Height = caps.OrientedPageMediaHeight.Value,
                        BorderBrush = Brushes.Blue,
                        BorderThickness = new Thickness(2, 2, 2, 2),
                        Background = Brushes.LightPink,
                        CornerRadius = new CornerRadius(10),
                    };
                    Canvas.SetLeft(border, 0);
                    Canvas.SetTop(border, 0);
    
                    Canvas canvas = new Canvas();
                    canvas.Children.Add(border);
                    canvas.Children.Add(block);
    
    
                    page.Width = caps.OrientedPageMediaWidth.Value;
                    page.Height = caps.OrientedPageMediaHeight.Value;
                    page.Children.Add(canvas);
                    page.Measure(new Size(page.Width, page.Height));
                    page.Arrange(new Rect(0, 0, page.Width, page.Height));
                }
    
                var writer = System.Printing.PrintQueue.CreateXpsDocumentWriter(queue);
                writer.Write(page);
            }
    
        }
    
        class PrinterDEV
        {
            public byte[] DEVMODE { get; set; }
            public string PrinterFullName { get; set; }
            public int ClientPrintSchemaVersion { get; set; }
    
            /// <summary>印刷キューを作る</summary>
            /// <returns></returns>
            public System.Printing.PrintQueue CreateQueue()
            {
                var svr = new System.Printing.LocalPrintServer();
                var queue = svr.GetPrintQueue(PrinterFullName);
    
                var conv = new System.Printing.Interop.PrintTicketConverter(PrinterFullName, ClientPrintSchemaVersion);
                queue.UserPrintTicket = conv.ConvertDevModeToPrintTicket(DEVMODE);
    
                return queue;
            }
    
            /// <summary>ダイアログを表示して印刷設定を取得</summary>
            /// <param name="devorg">ダイアログであらかじめ設定しておく設定値</param>
            /// <returns></returns>
            public static PrinterDEV ShowPrintDialog(PrinterDEV devorg)
            {
                PrintDialog dlg = new System.Windows.Controls.PrintDialog();
                if (devorg != null)
                {
                    var svr = new System.Printing.LocalPrintServer();
                    var queue = svr.GetPrintQueue(devorg.PrinterFullName);
                    if (queue != null)
                    {
                        dlg.PrintQueue = queue;
    
                        var conv = new System.Printing.Interop.PrintTicketConverter(devorg.PrinterFullName, devorg.ClientPrintSchemaVersion);
                        dlg.PrintTicket = conv.ConvertDevModeToPrintTicket(devorg.DEVMODE);
                    }
                }
    
                if (dlg.ShowDialog() == true)
                {
                    PrinterDEV ret = new PrinterDEV();
                    ret.PrinterFullName = dlg.PrintQueue.FullName;
                    ret.ClientPrintSchemaVersion = dlg.PrintQueue.ClientPrintSchemaVersion;
    
                    var conv = new System.Printing.Interop.PrintTicketConverter(dlg.PrintQueue.FullName, dlg.PrintQueue.ClientPrintSchemaVersion);
                    ret.DEVMODE = conv.ConvertPrintTicketToDevMode(dlg.PrintTicket, System.Printing.Interop.BaseDevModeType.PrinterDefault);
                    return ret;
                }
                else
                {
                    return devorg;
                }
            }
    
    
    
            [System.Runtime.InteropServices.DllImport("winspool.drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
            static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, DM fMode);
    
            [Flags]
            private enum DM : int
            {
                DM_UPDATE = 1,
                DM_COPY = 2,
                DM_PROMPT = 4,
                DM_MODIFY = 8,
            }
            private const int ID_OK = 1;
    
            public void ShowDetailDialog()
            {
                //string printerfullname = PrinterFullName;
                IntPtr hprinter = IntPtr.Zero;
    
                var size = DocumentProperties(IntPtr.Zero, hprinter, this.PrinterFullName, IntPtr.Zero, IntPtr.Zero, 0);
                if (this.DEVMODE == null || size != this.DEVMODE.Length)
                {
                    throw new InvalidOperationException();
                }
    
                byte[] buff = new byte[size];
                bool ok = false;
                var pinOutput = System.Runtime.InteropServices.GCHandle.Alloc(buff, System.Runtime.InteropServices.GCHandleType.Pinned);
                try
                {
                    var pinInput = System.Runtime.InteropServices.GCHandle.Alloc(this.DEVMODE, System.Runtime.InteropServices.GCHandleType.Pinned);
                    try
                    {
                        var pOut = pinOutput.AddrOfPinnedObject();
                        var pIn = pinInput.AddrOfPinnedObject();
    
                        var ret = DocumentProperties(IntPtr.Zero, hprinter, this.PrinterFullName, pOut, pIn, DM.DM_COPY | DM.DM_MODIFY | DM.DM_PROMPT);
                        ok = (ret == (int)ID_OK);
                    }
                    finally
                    {
                        pinInput.Free();
                    }
                }
                finally
                {
                    pinOutput.Free();
                }
    
                if (ok)
                {
                    this.DEVMODE = buff;
                }
            }
        }
    }
    #動作確認せずにダメだし出来るほど優秀な人が判らない理由が判らないのです。

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2020年5月25日 15:06