none
.NET Framework 2.0 にて、PageSetupDialog未使用時の印刷速度(プレビュー)が遅い RRS feed

  • 質問

  • PrintDocument の印刷にて、独自の印刷設定ダイアログを使用したいと考えております。

    当初100ページ程度の印刷を行うプログラムを作成し、独自ダイアログに移行したところ

    明らかに印刷速度が落ちました。

    以下のサンプルプログラムにて現象が発生しております。

    何か必要な手順が存在するのでしょうか?よろしくお願いします。

     

    Code Snippet

    Imports System.Drawing.Printing

    Public Class Form1

        Dim PS As New PageSetupDialog
        Dim WithEvents PD As New PrintDocument
        Dim PV As New PrintPreviewDialog
        Dim pagecount As Integer
        Dim WithEvents Button1 As New Button
        Dim WithEvents Button2 As New Button

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

            PD.PrinterSettings = New PrinterSettings
            PD.DefaultPageSettings = New PageSettings

            PS.PrinterSettings = PD.PrinterSettings
            PS.PageSettings = PD.DefaultPageSettings

            If (PS.ShowDialog() <> Windows.Forms.DialogResult.OK) Then Return

            PV.Document = PD
            PV.ShowDialog()

        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

            PD.PrinterSettings = New PrinterSettings
            PD.DefaultPageSettings = New PageSettings
            PD.DefaultPageSettings.PrinterSettings = New PrinterSettings

            PV.Document = PD
            PV.ShowDialog()

        End Sub

        Private Sub PD_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
            pagecount = 1
        End Sub

        Private Sub PD_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PD.PrintPage
            Using font As New Font("MS ゴシック", 10)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 10)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 20)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 30)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 40)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 50)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 60)
            End Using

            pagecount += 1

            If (pagecount < 500) Then e.HasMorePages = True
        End Sub

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Button1.Text = "PageSetupDialog使用"
            Button1.Width = Me.Width
            Button2.Text = "PageSetupDialog未使用"
            Button2.Width = Me.Width
            Button1.Parent = Me
            Button2.Parent = Me
            Button2.Top = Button1.Height

            AddHandler Button1.Click, AddressOf Me.Button1_Click
            AddHandler Button2.Click, AddressOf Me.Button2_Click

            AddHandler PD.BeginPrint, AddressOf Me.PD_BeginPrint
            AddHandler PD.PrintPage, AddressOf Me.PD_PrintPage
        End Sub

    End Class

     

     

    2008年7月1日 3:12

回答

  • 次のコードを実行することで、設定ダイアログを使用する場合と同じ速度が得られました。

    PageSetupDialog でOKを押した際にも、これが実行されます。

    # このコードを実行すると、用紙サイズ等はプリンタの印刷設定の値になります。

     

    Code Snippet

    PD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())

     

    PaperSize プロパティなどは、値を設定しない限り、参照するたびに内部的にプリンタから値が取得され、その取得値がプロパティ値として返されます。プロパティに値を設定すると、プロパティの参照時にはプリンタからの再取得は行われずに、設定した値が返されるようになります。

     

    上記コードでは、これらのプロパティを設定済み状態にできます。

    これを印刷処理の前に実行しておけば、印刷処理中にプリンタ情報の取得が何度も行われることを抑制できるため、処理が速くなります。

    # 気付かずにいくつか納品してました…

    2008年7月1日 11:02
  •  TH01 さんからの引用
    Code Snippet

    PD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())

    GetHdevmode、GetHdevnamesの戻り値は解放(GlobalFree)が必要なものです。

    値を一時変数に取らずにそのままSetするのはよろしくないかと。

     

    http://msdn.microsoft.com/ja-jp/library/88dfsy75(VS.80).aspx

    http://msdn.microsoft.com/ja-jp/library/system.drawing.printing.printersettings.gethdevnames(VS.80).aspx

     

    このメソッドで作成されたハンドルを使用した処理が完了したら、ネイティブ Win32 GlobalFree メソッドを明示的に呼び出し、ハンドルを解放する必要があります。

     

    2008年7月1日 14:03
    モデレータ

すべての返信

  • 次のコードを実行することで、設定ダイアログを使用する場合と同じ速度が得られました。

    PageSetupDialog でOKを押した際にも、これが実行されます。

    # このコードを実行すると、用紙サイズ等はプリンタの印刷設定の値になります。

     

    Code Snippet

    PD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())

     

    PaperSize プロパティなどは、値を設定しない限り、参照するたびに内部的にプリンタから値が取得され、その取得値がプロパティ値として返されます。プロパティに値を設定すると、プロパティの参照時にはプリンタからの再取得は行われずに、設定した値が返されるようになります。

     

    上記コードでは、これらのプロパティを設定済み状態にできます。

    これを印刷処理の前に実行しておけば、印刷処理中にプリンタ情報の取得が何度も行われることを抑制できるため、処理が速くなります。

    # 気付かずにいくつか納品してました…

    2008年7月1日 11:02
  •  TH01 さんからの引用
    Code Snippet

    PD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())

    PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())

    GetHdevmode、GetHdevnamesの戻り値は解放(GlobalFree)が必要なものです。

    値を一時変数に取らずにそのままSetするのはよろしくないかと。

     

    http://msdn.microsoft.com/ja-jp/library/88dfsy75(VS.80).aspx

    http://msdn.microsoft.com/ja-jp/library/system.drawing.printing.printersettings.gethdevnames(VS.80).aspx

     

    このメソッドで作成されたハンドルを使用した処理が完了したら、ネイティブ Win32 GlobalFree メソッドを明示的に呼び出し、ハンドルを解放する必要があります。

     

    2008年7月1日 14:03
    モデレータ
  • TH01 さん、Azulean さんありがとう御座います。

    おかげさまで解決いたしました。

    2008年7月2日 10:28
  • Azulean さんのご指摘の点、仰るとおりでした。失礼しました。ご指摘ありがとうございます。

    # 昨日投稿前に SetHdevmode の中で Free されているように見えたので問題ないと思ったのですが、GlobalUnlock を見間違えていました(^^;


    解決されたとのことですが、念のため以下のように訂正させていただきます。


    Code Snippet

    Private Declare Function GlobalFree Lib "kernel32.dll" (ByVal hMem As IntPtr) As IntPtr

     

    Dim hDevMode As IntPtr = IntPtr.Zero
    Dim hDevNames As IntPtr = IntPtr.Zero
    Try
        hDevMode = PD.PrinterSettings.GetHdevmode()
        hDevNames = PD.PrinterSettings.GetHdevnames()

        PD.DefaultPageSettings.SetHdevmode(hDevMode)
        PD.PrinterSettings.SetHdevmode(hDevMode)
        PD.PrinterSettings.SetHdevnames(hDevNames)
    Finally
        '(GlobalFree の戻り値チェックは省略)
        If (hDevMode <> IntPtr.Zero) Then GlobalFree(hDevMode)
        If (hDevNames <> IntPtr.Zero) Then GlobalFree(hDevNames)
    End Try

     

    2008年7月2日 10:48
  • TH01 様、訂正までしていただき、ありがとう御座います。

    以前のコードにて大幅に速度が上がったため解決したと思っていたのですが、どうやらまだ微妙に速度に差が有るようでした。

    いろいろ試したところ、以下のコードで一応同一の速度が得られているようです。

     

    Code Snippet

    Dim hDevMode As IntPtr = IntPtr.Zero
    Dim hDevNames As IntPtr = IntPtr.Zero

    Try
        hDevMode = PD.PrinterSettings.GetHdevmode()
        hDevNames = PD.PrinterSettings.GetHdevnames()

        PD.DefaultPageSettings.SetHdevmode(hDevMode)
        PD.DefaultPageSettings.PrinterSettings.SetHdevnames(hDevNames)' 追加しました
        PD.PrinterSettings.SetHdevmode(hDevMode)
        PD.PrinterSettings.SetHdevnames(hDevNames)
    Finally
        '(GlobalFree の戻り値チェックは省略)
        If (hDevMode <> IntPtr.Zero) Then GlobalFree(hDevMode)
        If (hDevNames <> IntPtr.Zero) Then GlobalFree(hDevNames)
    End Try

     

     

    ほかの方の役に立つかと思い、DEVMODE構造体の入手も行ってみました。

     

    Code Snippet

    Imports System.Drawing.Printing
    Imports System.Runtime.InteropServices

    Public Class Form1

        <DllImport("kernel32.dll")> Private Shared Function GlobalLock(ByVal hMem As IntPtr) As IntPtr
        End Function

        <DllImport("kernel32.dll")> Private Shared Function GlobalUnlock(ByVal hMem As IntPtr) As IntPtr
        End Function

        <DllImport("kernel32.dll")> Private Shared Function GlobalFree(ByVal hMem As IntPtr) As IntPtr
        End Function

        <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
        Public Structure DEVMODE
            <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)> _
            Public dmDeviceName As String
            Public dmSpecVersion As Short
            Public dmDriverVersion As Short
            Public dmSize As Short
            Public dmDriverExtra As Short
            Public dmFields As Integer
            Public dmOrientation As Short
            Public dmPaperSize As Short
            Public dmPaperLength As Short
            Public dmPaperWidth As Short
            Public dmScale As Short
            Public dmCopies As Short
            Public dmDefaultSource As Short
            Public dmPrintQuality As Short
            Public dmColor As Short
            Public dmDuplex As Short
            Public dmYResolution As Short
            Public dmTTOption As Short
            Public dmCollate As Short
            <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=32)> _
            Public dmFormName As String
            Public dmLogPixels As Short
            Public dmBitsPerPel As Short
            Public dmPelsWidth As Integer
            Public dmPelsHeight As Integer
            Public dmDisplayFlags As Integer
            Public dmDisplayFrequency As Integer
            Public dmICMMethod As Integer
            Public dmICMIntent As Integer
            Public dmMediaType As Integer
            Public dmDitherType As Integer
            Public dmReserved1 As Integer
            Public dmReserved2 As Integer
            Public dmPanningWidth As Integer
            Public dmPanningHeight As Integer
        End Structure

        Dim PS As New PageSetupDialog
        Dim WithEvents PD As New PrintDocument
        Dim PV As New PrintPreviewDialog
        Dim pagecount As Integer
        Dim WithEvents Button1 As New Button
        Dim WithEvents Button2 As New Button

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

            PD.PrinterSettings = New PrinterSettings
            PD.DefaultPageSettings = New PageSettings

            PS.PrinterSettings = PD.PrinterSettings
            PS.PageSettings = PD.DefaultPageSettings

            If (PS.ShowDialog() <> Windows.Forms.DialogResult.OK) Then Return

            PV.Document = PD
            PV.ShowDialog()

        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

            PD.PrinterSettings = New PrinterSettings
            PD.DefaultPageSettings = New PageSettings
            PD.DefaultPageSettings.PrinterSettings = New PrinterSettings

            Dim hDevMode As IntPtr = IntPtr.Zero
            Dim hDevNames As IntPtr = IntPtr.Zero
            Dim hPtrDevMode As IntPtr = IntPtr.Zero

            Try
                hDevMode = PD.PrinterSettings.GetHdevmode()
                hDevNames = PD.PrinterSettings.GetHdevnames()

                ' DEVMODE 構造体の入手
                hPtrDevMode = GlobalLock(hDevMode)
                Dim dModDefPgStngs_Hdevmode As DEVMODE = CType(Marshal.PtrToStructure(hPtrDevMode, GetType(DEVMODE)), DEVMODE)
                Marshal.StructureToPtr(dModDefPgStngs_Hdevmode, hPtrDevMode, True)  ' 構造体からポインタにコピー

                GlobalUnlock(hDevMode)

                PD.DefaultPageSettings.SetHdevmode(hDevMode)
                PD.DefaultPageSettings.PrinterSettings.SetHdevnames(hDevNames)      ' 追加しました
                PD.PrinterSettings.SetHdevmode(hDevMode)
                PD.PrinterSettings.SetHdevnames(hDevNames)

            Finally
                '(GlobalFree の戻り値チェックは省略)
                If (hDevMode <> IntPtr.Zero) Then GlobalFree(hDevMode)
                If (hDevNames <> IntPtr.Zero) Then GlobalFree(hDevNames)
            End Try

            PV.Document = PD

            PV.ShowDialog()

        End Sub

        Private Sub PD_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
            pagecount = 1
        End Sub

        Private Sub PD_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PD.PrintPage
            Using font As New Font("MS ゴシック", 10)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 10)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 20)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 30)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 40)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 50)
                e.Graphics.DrawString("文字列", font, Brushes.Black, 10, 60)
            End Using

            pagecount += 1

            If (pagecount < 500) Then e.HasMorePages = True
        End Sub

        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Button1.Text = "PageSetupDialog使用"
            Button1.Width = Me.Width
            Button2.Text = "PageSetupDialog未使用"
            Button2.Width = Me.Width
            Button1.Parent = Me
            Button2.Parent = Me
            Button2.Top = Button1.Height

            AddHandler Button1.Click, AddressOf Me.Button1_Click
            AddHandler Button2.Click, AddressOf Me.Button2_Click

            AddHandler PD.BeginPrint, AddressOf Me.PD_BeginPrint
            AddHandler PD.PrintPage, AddressOf Me.PD_PrintPage
        End Sub

    End Class

     

     

    ところで、実はこの質問は別の掲示板でも先月中ごろに投稿していたのですが、マルチポストの指摘を受けてしまいました。

    #VB4時代から、回答は何度も有りますが質問は初めてでした・・・

    そちらの方にもフィードバックさせていただいてもよろしいでしょうか?

    2008年7月4日 1:38
  • > どうやらまだ微妙に速度に差が有るようでした

     

    そうでしたか…。
    それと私の API の定義書式は、.NET 風じゃなかったようですね…。

     

    > ほかの方の役に立つかと思い、DEVMODE構造体の入手も行ってみました。

     

    変なこと確認しますが、書かれたコードは、あくまで構造体の取得方法のサンプルということですね?
    書かれたコードだけを見た場合、処理上、構造体を取得することには意味がなさそうに思いましたので。

     

    > そちらの方にもフィードバックさせていただいてもよろしいでしょうか?

     

    そちらのほうに、ここのURLを書かれると良いかと思います。
    ただ、そちらにフィードバックされることで、もしかしたら新たに有用なレスがそちら側に付く可能性もあると思うので、念のためにそちらのURLをここにも書かれてはと思います。
    # 細かなことばかり書いてすいません。

    2008年7月4日 8:45
  •  TH01 さんからの引用

    > どうやらまだ微妙に速度に差が有るようでした

     

    そうでしたか…。
    それと私の API の定義書式は、.NET 風じゃなかったようですね…。

     

    いえ、大変有用な情報であったことは間違いありませんし、非常に感謝しております。

    元々印刷速度が遅くなる理由が分からず、DEVMODE構造体を何とかしなければならないのか見当違い

    なことを考えて作ったコードなので、TH01さんのコードの添削等を行ったわけでは有りません。

    #DLL呼び出し部は既に手持ちのコード中に有ったのでTH01さんの定義に気づかずコピーしておりませんでした。

     

     TH01 さんからの引用

    > ほかの方の役に立つかと思い、DEVMODE構造体の入手も行ってみました。

     

    変なこと確認しますが、書かれたコードは、あくまで構造体の取得方法のサンプルということですね?
    書かれたコードだけを見た場合、処理上、構造体を取得することには意味がなさそうに思いましたので。

     

    そうですね、確認用のコードをそのまま載せさせていただいただけです。取得のサンプルとしていただければと。

     

     TH01 さんからの引用

    > そちらの方にもフィードバックさせていただいてもよろしいでしょうか?

     

    そちらのほうに、ここのURLを書かれると良いかと思います。
    ただ、そちらにフィードバックされることで、もしかしたら新たに有用なレスがそちら側に付く可能性もあると思うので、念のためにそちらのURLをここにも書かれてはと思います。
    # 細かなことばかり書いてすいません。

     

    いえ、おっしゃることは良く分かります。以下のURLが私の投稿です。

    http://dobon.net/cgi-bin/vbbbs/cbbs.cgi?mode=one&namber=22241&type=0&space=0&no=0

     

    どうもありがとう御座いました。私も回答できるレベルになれるよう頑張りますので、よろしくお願いします。

     

    2008年7月4日 9:23