トップ回答者
.NET Framework 2.0 にて、PageSetupDialog未使用時の印刷速度(プレビュー)が遅い

質問
-
PrintDocument の印刷にて、独自の印刷設定ダイアログを使用したいと考えております。
当初100ページ程度の印刷を行うプログラムを作成し、独自ダイアログに移行したところ
明らかに印刷速度が落ちました。
以下のサンプルプログラムにて現象が発生しております。
何か必要な手順が存在するのでしょうか?よろしくお願いします。
Code SnippetImports 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 ButtonPrivate Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
PD.PrinterSettings = New PrinterSettings
PD.DefaultPageSettings = New PageSettingsPS.PrinterSettings = PD.PrinterSettings
PS.PageSettings = PD.DefaultPageSettingsIf (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 PrinterSettingsPV.Document = PD
PV.ShowDialog()End Sub
Private Sub PD_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
pagecount = 1
End SubPrivate 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 Usingpagecount += 1
If (pagecount < 500) Then e.HasMorePages = True
End SubPrivate 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.HeightAddHandler Button1.Click, AddressOf Me.Button1_Click
AddHandler Button2.Click, AddressOf Me.Button2_ClickAddHandler PD.BeginPrint, AddressOf Me.PD_BeginPrint
AddHandler PD.PrintPage, AddressOf Me.PD_PrintPage
End SubEnd Class
回答
-
次のコードを実行することで、設定ダイアログを使用する場合と同じ速度が得られました。
PageSetupDialog でOKを押した際にも、これが実行されます。
# このコードを実行すると、用紙サイズ等はプリンタの印刷設定の値になります。
Code SnippetPD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())
PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())
PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())
PaperSize プロパティなどは、値を設定しない限り、参照するたびに内部的にプリンタから値が取得され、その取得値がプロパティ値として返されます。プロパティに値を設定すると、プロパティの参照時にはプリンタからの再取得は行われずに、設定した値が返されるようになります。
上記コードでは、これらのプロパティを設定済み状態にできます。
これを印刷処理の前に実行しておけば、印刷処理中にプリンタ情報の取得が何度も行われることを抑制できるため、処理が速くなります。
# 気付かずにいくつか納品してました…
-
TH01 さんからの引用 Code SnippetPD.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
このメソッドで作成されたハンドルを使用した処理が完了したら、ネイティブ Win32 GlobalFree メソッドを明示的に呼び出し、ハンドルを解放する必要があります。
すべての返信
-
次のコードを実行することで、設定ダイアログを使用する場合と同じ速度が得られました。
PageSetupDialog でOKを押した際にも、これが実行されます。
# このコードを実行すると、用紙サイズ等はプリンタの印刷設定の値になります。
Code SnippetPD.DefaultPageSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())
PD.PrinterSettings.SetHdevmode(PD.PrinterSettings.GetHdevmode())
PD.PrinterSettings.SetHdevnames(PD.PrinterSettings.GetHdevnames())
PaperSize プロパティなどは、値を設定しない限り、参照するたびに内部的にプリンタから値が取得され、その取得値がプロパティ値として返されます。プロパティに値を設定すると、プロパティの参照時にはプリンタからの再取得は行われずに、設定した値が返されるようになります。
上記コードでは、これらのプロパティを設定済み状態にできます。
これを印刷処理の前に実行しておけば、印刷処理中にプリンタ情報の取得が何度も行われることを抑制できるため、処理が速くなります。
# 気付かずにいくつか納品してました…
-
TH01 さんからの引用 Code SnippetPD.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
このメソッドで作成されたハンドルを使用した処理が完了したら、ネイティブ Win32 GlobalFree メソッドを明示的に呼び出し、ハンドルを解放する必要があります。
-
Azulean さんのご指摘の点、仰るとおりでした。失礼しました。ご指摘ありがとうございます。
# 昨日投稿前に SetHdevmode の中で Free されているように見えたので問題ないと思ったのですが、GlobalUnlock を見間違えていました(^^;
解決されたとのことですが、念のため以下のように訂正させていただきます。
Code SnippetPrivate 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 -
TH01 様、訂正までしていただき、ありがとう御座います。
以前のコードにて大幅に速度が上がったため解決したと思っていたのですが、どうやらまだ微妙に速度に差が有るようでした。
いろいろ試したところ、以下のコードで一応同一の速度が得られているようです。
Code SnippetDim hDevMode As IntPtr = IntPtr.Zero
Dim hDevNames As IntPtr = IntPtr.ZeroTry
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 SnippetImports System.Drawing.Printing
Imports System.Runtime.InteropServicesPublic 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 StructureDim 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 ButtonPrivate Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
PD.PrinterSettings = New PrinterSettings
PD.DefaultPageSettings = New PageSettingsPS.PrinterSettings = PD.PrinterSettings
PS.PageSettings = PD.DefaultPageSettingsIf (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 PrinterSettingsDim hDevMode As IntPtr = IntPtr.Zero
Dim hDevNames As IntPtr = IntPtr.Zero
Dim hPtrDevMode As IntPtr = IntPtr.ZeroTry
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 TryPV.Document = PD
PV.ShowDialog()
End Sub
Private Sub PD_BeginPrint(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintEventArgs)
pagecount = 1
End SubPrivate 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 Usingpagecount += 1
If (pagecount < 500) Then e.HasMorePages = True
End SubPrivate 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.HeightAddHandler Button1.Click, AddressOf Me.Button1_Click
AddHandler Button2.Click, AddressOf Me.Button2_ClickAddHandler PD.BeginPrint, AddressOf Me.PD_BeginPrint
AddHandler PD.PrintPage, AddressOf Me.PD_PrintPage
End SubEnd Class
ところで、実はこの質問は別の掲示板でも先月中ごろに投稿していたのですが、マルチポストの指摘を受けてしまいました。
#VB4時代から、回答は何度も有りますが質問は初めてでした・・・
そちらの方にもフィードバックさせていただいてもよろしいでしょうか?
-
> どうやらまだ微妙に速度に差が有るようでした
そうでしたか…。
それと私の API の定義書式は、.NET 風じゃなかったようですね…。> ほかの方の役に立つかと思い、DEVMODE構造体の入手も行ってみました。
変なこと確認しますが、書かれたコードは、あくまで構造体の取得方法のサンプルということですね?
書かれたコードだけを見た場合、処理上、構造体を取得することには意味がなさそうに思いましたので。> そちらの方にもフィードバックさせていただいてもよろしいでしょうか?
そちらのほうに、ここのURLを書かれると良いかと思います。
ただ、そちらにフィードバックされることで、もしかしたら新たに有用なレスがそちら側に付く可能性もあると思うので、念のためにそちらのURLをここにも書かれてはと思います。
# 細かなことばかり書いてすいません。 -
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
どうもありがとう御座いました。私も回答できるレベルになれるよう頑張りますので、よろしくお願いします。