Delphi Programming / Object Pascal
[掲載 2005年01月01日] [更新 2012年04月04日] Delphi サンプルプログラム集
015_プリンタ設定関係ダイアログ API の使用方法
動作確認等
Windows 7 U64(SP1) + Delphi XE Pro
015_PrintDlgAPI.zip [2,185 KB] 2012年04月04日版 (EXE 同梱)
2012年04月03日
PrintDlgEx と PageSetupDlg 関数のサンプルを追加
構造体 (レコード型) の表を追加して,説明記事を大幅に追加
印刷ダイアログ,プリンタの設定ダイアログ,ページ設定ダイアログの Windows API 関数のサンプルコードです.
Delphi の TPrintDialog,TPrinterSetupDialog,TPageSetupDialog のコンポーネントは,これらのAPI をカプセル化したものです.この API を使用する場面はあまりないと思いますが,掲示板などでたまに質問があったのでテストしてみました.
その他のダイアログの構造体の説明も MSDN の [Common Dialog Box Library] にあります.Delphi のダイアログは,これらのコモンダイアログのライブラリをカプセル化したものです.該当する構造体を操作すれば,ダイアログのカスタマイズが可能です.
Delphi の印刷とプリンタ設定ダイアログを使用する場合,あらかじめ,プリンタの設定をしておけば,その設定でダイアログを表示します.そして,ダイアログを終了した後,Printer の Canvas に印刷コードを書けば,その設定で印刷されます.
一方,Windows API の PrintDlg 関数,PrintDlgEx 関数で直接設定する場合は,例えプリンタの設定をしていても,その内容はダイアログに反映されません.各 API 関数の構造体のメンバーに値をセットをしないと,常に「通常使用するプリンタ」のデフォルトの値で表示されます.
また,ダイアログを終了しても,プリンタオブジェクトに対して,構造体のメンバーから設定値を取出して設定しないと,設定内容は反映されません.
一方,ワードやエクセルのように,現在のシステムの現在のプリンタの設定をデフォルトの設定値として使用するアプリケーションがあります.この場合,プリンタの設定関係の API を使用して,あらかじめデフォルトの値を設定しておくことができます.
ただし,元の設定に戻しておかないと,他のアプリケーションが影響を受けることになります.
01_ [ 印刷 ] [ プリンタ設定 ] ダイアログ
- PrintDlg 関数
PrintDlg 関数のテストプログラムです.
引数は 1 つで,TPrintDlg 構造体です.この構造体の Flags に PD_PRINTSETUP を含めると {プリンタの設定} ダイアログとなります.
下の表はこの構造体のメンバのリストです.
表1
TPrintDlg 構造体(レコード型)のメンバー
変数名と型は Delphi XE の CommDlg.pas のもの
メンバー
型
説明
lStructSize
DWORD
この構造体のサイズを必ず指定する
hwndOwner
HWND
ダイアログの親ウィンドウを指定する
0 でもよいがモーダル表示のためには指定した方がよい
hDevMode
HGLOBAL
DEVMODE 構造体のハンドル
実行後はダイアログで設定した値を返す
この情報でダイアログを表示する
hDevNames
HGLOBAL
プリンタドライバ名,デバイス名,出力ポート名等を格納した DEVNAMES 構造体のポインタを指定する
実行後はダイアログで設定した値を返す
hDevMode の指定よりも優先される
hDC
HDC
Flags の値が PD_RETURNDC または PD_RETURNIC の時に有効
システムにデバイスコンテキストまたインフォーメーションの作成を任せた場合に,ここにそのハンドルを返す
Flags
DWORD
ダイアログの初期化や各コントロールの表示等の制御のフラグを指定する.値は下表の組み合わせ
終了時には,ダイアログで設定した値が返ってくる
nFromPage
Word
印刷開始ページ番号を指定する
最初のページは1.nFromPage < = nToPage にすること
終了時は,ダイアログの設定値を返す
nToPage
Word
印刷終了ページ番号を指定する
その他は nFromPage と同じ
nMinPage
Word
印刷可能な最初のページを指定する.最小値は 1
nMaxPage
Word
印刷可能な最後のページ番号を指定する
nCopies
Word
印刷部数を指定する
終了時は,ダイアログで設定した値を返す
Flags に PD_USEDEVMODECOPIESANDCOLLATE を指定している場合は hDevModeのdmCopies の値が使われ,戻り値も dmCopies の値に格納され,nCopies の値は常に 1 が返る
hInstance
HINST
Flags に以下のいずれかの値を指定した場合に指定する
PD_ENABLEPRINTTEMPLATE,PD_ENABLESETUPTEMPLATE
この場合,この hInstanceは
lpPrintTemplateName または lpSetupTemplateName で指定したダイアログボックステンプレート (EXE とか DLL) のインスタンスのハンドルとする
lCustData
LPARAM
lpfnPrintHook または lpfnSetupHook を使用する場合に使用
アプリ独自のカスタムデータを設定する
フックのコールバック関数では,WM_INITDIALOG メッセージの時にこの値を受け取ることができる
lpfnPrintHook
-
[印刷] ダイアログの動作仕様を変更するためのコールバック関数のアドレスを指定する
(参考リンク参照)
Flags に PD_ENABLEPRINTHOOK を指定した場合に有効
lpfnSetupHook
-
[プリンタの設定] ダイアログの動作仕様を変更するためのコールバック関数のアドレスを指定する (参考リンク参照)
Flags に PD_ENABLESETUPHOOK を指定した場合に有効
lpPrintTemplateName
PWideChar
[印刷] ダイアログテンプレートのリソース名を指定する
これで標準の [印刷] ダイアログを置き換える
Flags に PD_ENABLEPRINTTEMPLATE を指定した場合に有効
lpSetupTemplateName
PWideChar
[プリンタの設定] ダイアログテンプレートのリソース名を指定
これで標準の [プリンタの設定] ダイアログを置き換える
Flags に PD_ENABLESETUPTEMPLATE を指定した場合に有効
hPrintTemplate
HGLOBAL
[印刷] ダイアログテンプレートのリソースハンドルを指定
これで標準の [印刷] ダイアログを置き換える
Flags に PD_ENABLEPRINTTEMPLATEHANDLE を指定した場合に有効
hSetupTemplate
HGLOBAL
[プリンタの設定] ダイアログテンプレートのリソースハンドルを指定する
これで標準の [プリンタの設定] ダイアログを置き換える
Flags に PD_ENABLESETUPTEMPLATEHANDLE を指定した場合に有効
次の表は,PrintDlg 関数の引数である構造体の,Flags メンバーで設定可能な値のリストです.
また,図は Delphi の印刷ダイアログとプリンタ設定のダイアログのプロパティです.これらを較べてみると,Flags メンバーで指定可能なフラグが,Delphi の,これらのコンポーネントでも設定可能なことが分かります.
図1
TPrintDialog コンポのプロパティ
Delphi の TPrintDialog には,PrintDlg 関数の Flags で設定可能なプロパティがほとんど用意されている
各プロパティの意味については,Delphi のオンラン委ヘルプの TPrinter, TPrintDialog の記事で確認できる
図2
TPrinterSetupDialog のプロパティ
表2
Flags の値 (Delphi XE の CommDlg.pas のもの)
PD_ALLPAGES
ページ範囲の [全て] を選択状態にする
これがデフォルトの設定
PD_SELECTION
ページ範囲の [選択した部分] を選択状態にする
PD_PAGENUMS
ページ範囲の [ページ指定] を選択状態にする
nMinPage = nMaxPage の時は無効
PD_NOSELECTION
ページ範囲の [選択した部分] を選択不可にする
PD_NOPAGENUMS
ページ範囲の [ページ指定] を選択不可にする
PD_COLLATE
[部単位で印刷] をチェックする
PD_PRINTTOFILE
[ファイルへ出力] をチェックする
PD_PRINTSETUP
[印刷] ダイアログではなく,[プリンタの設定] ダイアログを表示する
この指定がない場合は [印刷] ダイアログ
PD_NOWARNING
デフォルトプリンタが無い場合に表示されるメッセージを表示しないようする
PD_RETURNDC
[印刷]ダイアログから戻った時にプリンタのデバイスコンテキストhDCメンバに格納する
PD_RETURNIC
[印刷] ダイアログから戻った時にプリンタインフォメーションコンテキストをhDCメンバに返す
PD_RETURNDEFAULT
[印刷]ダイアログを表示しない
デフォルトプリンタの情報を hDevMode と hDevNames に格納して返す.hDevNames と hDevMode を 0 にしておかないと失敗する
PD_SHOWHELP
ダイアログボックスにヘルプボタンを表示する
hwndOwner が 0 だと,表示しても反応しない
PD_ENABLEPRINTHOOK
[印刷] ダイアログのフックのコールバック処理を有効にする (lpfnPrintHook の指定を有効にする)
PD_ENABLESETUPHOOK
[プリンタ設定] ダイアログのコールバック処理を有効にする (lpfnSetupHook の指定を有効にする)
PD_ENABLEPRINTTEMPLATE
[印刷] ダイアログのテンプレート(ダイアログのリソース,lpPrintTemplateName) の使用を有効にする
PD_ENABLESETUPTEMPLATE
[プリンタの設定] ダイアログのテンプレート (ダイアログのリソース,lpSetupTemplateName) の使用を有効にする
PD_ENABLEPRINTTEMPLATEHANDLE
[印刷] ダイアログのテンプレート(ダイアログのリソース,hPrintTemplate) の使用を有効にする
PD_ENABLESETUPTEMPLATEHANDLE
[プリンタの設定]ダイアログのテンプレート(ダイアログのリソース,hSetupTemplate) の使用を有効にする
PD_USEDEVMODECOPIES
hDevMode メンバーの dmCopies の値を使用するを指定するフラグだが,MSDN の記事によると,nCopies メンバと Flags メンバの PD_COLLATE フラグで操作可能なので特に意識する必要なしの記述がある
PD_USEDEVMODECOPIESANDCOLLATE
PD_USEDEVMODECOPIES と同じ
PD_DISABLEPRINTTOFILE
[ファイルへ出力] を選択不可にする
PD_HIDEPRINTTOFILE
[ファイルへ出力] のチェックを非表示にする
PD_NONETWORKBUTTON
[ネットワーク] ボタンを非表示にする
次の表はコールバック関数の定義と引数のリストです.
ダイアログは,コールバック関数を呼び出す前に,WM_INITDIALOG メッセージを処理します.他の全てのメッセージは,コールバック関数が受け取ります.操作の対象となるメッセージとして,例えば以下のようなものがあります.
他のメッセージは主に,WM_COMMAND で検出できます.また,コールバック関数内で,メッセージの送信は,PostMessage を使用します.
copy code
function (Wnd: HWND; Message: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall ;
引数
型
説明
Wnd
HWND
印刷ダイアログのハンドル
Message
UINT
メッセージの種類
wParam
WPARAM
メッセージに関する情報.Message の値によって異なる
lParam
LPARAM
メッセージに関する情報.Message の値によって異なる
Message の値が WM_INITDIALOG の場合は PRINTDLG 構造体のポインタ
戻り値
UINT
0 にするとコールバック内の処理が実行される
0 以外にするとダイアログの既定の動作となる
テストプログラムです.
最初の 2 つは印刷ダイアログで,次の 2 つがプリンタの設定ダイアログです.コールバック関数を利用する際の参考コードと,あらかじめ,用紙サイズと印刷の向きを設定してからダイアログを表示するサンプルがあります.
図3
設計時画面
図4
[印刷] ダイアログ 1
Button1 クリック
PrintDlgAPI をデフォルトで使用
図5
[印刷]ダイアログ 2
Button2 クリック
コールバック関数内で以下の処理
[ファイルへ出力] を非表示
[部単位で印刷] を非表示
図6
[プリンタの設定] ダイアログ 1
図7
[プリンタの設定] ダイアログ 2
Button4 クリック
用紙サイズと印刷の向きを指定してからダイアログを表示
リスト2
PrintDlg 関数を使用した印刷ダイアログとプリンタの設定ダイアログのテストコード
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CommDlg;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses Printers;
{$R *.dfm}
// =============================================================================
// PringDlg APIで[印刷]ダイアログの表示 1
// 引数の構造体の設定を,ほとんどデフォルトのままで実行
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
PrintDlgRec : TPrintDlg;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
// 構造体(TPrintDlg)の各メンバの設定
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
Flags := 0 ;
end ;
// ダイアログ表示
if PrintDlg(PrintDlgRec) then begin
// ここに印刷のコードを書く
// ダイアログの設定内容が,構造体の各メンバーから取得できる
end ;
end ;
// -----------------------------------------------------------------------------
// PringDlg APIのコールバック関数([印刷]ダイアログ用)
// このコードに実用性はない.このように使用できるというサンプル
// -----------------------------------------------------------------------------
function PrintHookProc(Wnd: HWND; Message: UINT; wParam: WPARAM;
lParam: LPARAM): UINT stdcall ;
var
hWindow: HWND;
begin
Result := 0 ;
// 初期化の時に処理(当然?)
if Message = WM_INITDIALOG then begin
// [ファイルへ出力]部分のハンドルを取得
hWindow := GetDlgItem(Wnd, 1040 );
// そのハンドルのウィンドウを非表示
ShowWindow(hWindow, SW_HIDE);
// [部単位で印刷]部分のハンドルを取得
hWindow := GetDlgItem(Wnd, 1041 );
// そのハンドルのウィンドウを非表示
ShowWindow(hWindow, SW_HIDE);
end ;
end ;
// =============================================================================
// PringDlg APIを使用した[印刷]ダイアログの表示 2
// コールバック関数の使用例
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
PrintDlgRec : TPrintDlg;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
// 構造体(TPrintDlg)の各メンバの設定
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
// テストのため頁と部数を設定
nFromPage := 1 ;
nToPage := 6 ;
// nMinPaeとnMaxPageがなければ[部単位で印刷]は非表示
nMinPage := 0 ;
nMaxPage := 4 ;
// nCopiesがなければ当然[部単位で印刷]は無効表示となる
nCopies := 3 ;
// コールバック関数を使用するフラグ
Flags := PD_ENABLEPRINTHOOK;
// 使用するコールバック関数のアドレスをセット
lpfnPrintHook := @PrintHookProc;
end ;
// ダイアログ表示
if PrintDlg(PrintDlgRec) then begin
// ここに印刷のコードを書く
// ダイアログの設定内容が,構造体の各メンバーから取得できる
end ;
end ;
// =============================================================================
// PringDlg APIを使用した[プリンタの設定]ダイアログの表示 1
// メンバーのFlagsにPD_PRINTSETUPを含めると,[プリンタ設定]ダイアログとなる
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
PrintDlgRec: TPrintDlg;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
// 各パラメータ(TPringDlg構造体)の設定
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
Flags := PD_PRINTSETUP;
end ;
// ダイアログ表示
if PrintDlg(PrintDlgRec) then begin
// ここに印刷のコードを書く
// ダイアログの設定内容が,構造体の各メンバーから取得できる
end ;
end ;
// =============================================================================
// PringDlg APIを使用した[プリンタの設定]ダイアログの表示 2
// メンバーのFlagsにPD_PRINTSETUPを含めると,[プリンタ設定]ダイアログとなる
//
// 用紙サイズと印刷の向きを指定してダイアログを表示する例
// usesにPrintersが必要
// =============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
PrintDlgRec : TPrintDlg;
ADevice : array [0 ..MAX_PATH - 1 ] of Char;
ADriver : array [0 ..MAX_PATH - 1 ] of Char;
APort : array [0 ..MAX_PATH - 1 ] of Char;
ADeviceMode : THandle;
ADevMode : PDeviceMode;
begin
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
// ADeviceModeのメモリをロックしDEVMODE構造体のポインタを取得
ADevMode := GlobalLock(ADeviceMode);
try
// 実際には該当メンバーをサポートしているかチェックしてからセットするのがよい
ADevMode^.dmPaperSize := DMPAPER_A3;
ADevMode^.dmOrientation := DMORIENT_LANDSCAPE;
finally
GlobalUnlock(ADeviceMode);
end ;
// 構造体を0で埋める(つまり初期化)
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
// 各パラメータ(TPageSetupDlg構造体)の設定
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
hDevMode := ADeviceMode;
Flags := PD_PRINTSETUP;
end ;
// ダイアログ表示
if PrintDlg(PrintDlgRec) then begin
// ここに印刷のコードを書く
// ダイアログの設定内容が,構造体の各メンバーから取得できる
end ;
end ;
end .
指定したプリンタ名でダイアログを表示するには,hDevNames メンバーに値を設定することになっていますが,次のコードのように,現在のプリンタを変更して,hDevMode メンバーに,DEVMODE 構造体のハンドルをセットする方法のほうが簡単です.
この方法であれば,プリンタ名だけでなく,用紙サイズ,印刷の方向など,他のプロパティも同時に設定してダイアログを表示できます.
リスト3
プリンタの番号 (インデックス) で,プリンタ名を指定してダイアログ表示
copy code
procedure TForm1.Button4Click(Sender: TObject);
var
AIndex : Integer;
ADevice : array [0 ..MAX_PATH - 1 ] of Char;
ADriver : array [0 ..MAX_PATH - 1 ] of Char;
APort : array [0 ..MAX_PATH - 1 ] of Char;
ADeviceMode : THandle;
ADevMode : PDeviceMode;
PrintDlgRec : TPrintDlg;
begin
// プリンタのインデックスを求める(簡略法)
AIndex := Printer.Printers.IndexOf('プリンタ名' );
Printer.PrinterIndex := AIndex;
// Printer.PrinteIndexのプリンタの初期化
// この3行で実行しないと,ADeviceModeの内容が更新されない
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
Printer.SetPrinter(ADevice, ADriver, APort, 0 );
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
// プリンタのプロパティの設定をしなければこのコードは不要
ADevMode := GlobalLock(ADeviceMode);
try
// 必要であればここでプリンタのプロパティの設定を行う
finally
GlobalUnlock(ADeviceMode);
end ;
// 構造体を0で埋める(つまり初期化)
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
// 各パラメータ(TPageSetupDlg構造体)の設定
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
Flags := PD_PRINTSETUP;
hDevMode := ADeviceMode;
end ;
// ダイアログ表示
if PrintDlg(PrintDlgRec) then begin
// ここに印刷のコードを書く
// ダイアログの設定内容が,構造体の各メンバーから取得できる
end ;
end ;
02_ [ 印刷 ] ダイアログ
- PrintDlgEx
関数
PrintDlgEx 関数のテストプログラムです.
引数は 1 つで,TPrintDlgEx 構造体です.構造体のメンバー等の内容は,[参考リンク] の記事を参考にしてください.PrintDlg 関数との違いは,
[印刷] ダイアログ専用
[適用] ボタンがある
[ページ指定] の項目で範囲も指定可能
[プロパティシート] の追加が可能
となっています.
[適用] ボタンをクリックすすると,その後 [キャンセル] しても,関数実行後に,構造体に[適用] した時の値が格納されます.[プロパティシート] は,ダイアログにタブを追加し,そのタブに独自のダイアログを表示するものです.このダイアログは,DLL から,あるいはリソースから読み込みます.
以下のサンプルプログラムでは,リソースエディタで作成したダイアログを表示しています.
コールバック関数は,[730] [05_ UrlDownloadToFile における進捗ダイアログの表示] のコールバック関数と同じく,インターフェイス型となっています.IPrintDialogCallback というインタフェースを実装します.Delphi XE には実装されていません.
ただし,プロパティシート用のコールバック関数は,PrintDlg 関数で説明したコールバック関数と同じ定義となっています.
PrintDlgEx 関数もその構造体の定義も,Delphi XE にはありません.そこで,以下のサンプルではコード内で定義しています.
図8
設計時画面
図9
[印刷] ダイアログ 1
図10
[印刷] ダイアログ 2
Button2 クリック
[ページ指定] を設定してから表示
図11
[印刷]ダイアログ 3
Button3 クリック
独自のプロパティシートを追加表示
リスト4
PrintDlgEx 関数を使用した印刷ダイアログのテストコード
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CommDlg, CommCtrl;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
// -----------------------------------------------------------------------------
// PrintDlgEx用の定数と構造体(レコード型)と関数の定義
// Delphi XEにないものだけを追加
// -----------------------------------------------------------------------------
const
START_PAGE_GENERAL = $FFFFFFFF ;
PD_RESULT_APPLY = 2 ;
PD_RESULT_CANCEL = 0 ;
PD_RESULT_PRINT = 1 ;
PD_EXCLUSIONFLAGS = $01000000 ;
PD_CURRENTPAGE = $00400000 ;
PD_NOCURRENTPAGE = $00800000 ;
PD_USELARGETEMPLATE = $10000000 ;
type
PPRINTPAGERANGE = ^TPRINTPAGERANGE;
tagPRINTPAGERANGE = packed record
nFromPage : DWORD;
nToPage : DWORD;
end ;
TPrintPageRange = tagPRINTPAGERANGE;
PPRINTDLGEX = ^TPRINTDLGEX;
tagPDEX = packed record
lStructSize : DWORD;
hwndOwner : HWND;
hDevMode : HGLOBAL;
hDevNames : HGLOBAL;
hDC : HDC;
Flags : DWORD;
Flags2 : DWORD;
ExclusionFlags : DWORD;
nPageRanges : DWORD;
nMaxPageRanges : DWORD;
lpPageRanges : PPRINTPAGERANGE;
nMinPage : DWORD;
nMaxPage : DWORD;
nCopies : DWORD;
hInstance : HINST;
lpPrintTemplateName : PAnsiChar;
lpCallback : PUNKNOWN;
nPropertyPages : DWORD;
lphPropertyPages : HPropSheetPage; // CommCtrl.pas
nStartPage : DWORD;
dwResultAction : DWORD;
end ;
TPrintDlgEx = tagPDEX;
TPageRange = packed record
nFromPage, nToPage : DWord;
end ;
function PrintDlgEx(var PrintDlgEx: TPrintDlgEx): HRESULT; stdcall ;
// -----------------------------------------------------------------------------
var
Form1: TForm1;
implementation
{$R *.dfm}
// リソースをインクルード
{$R TEST.RES}
function PrintDlgEx; external 'comdlg32.dll' name 'PrintDlgExW' ;
// =============================================================================
// PringDlgEx APIで[印刷]ダイアログの表示 1
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
PDEXRec : TPrintDlgEx;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PDEXRec, SizeOf(PDEXRec), #0 );
// 構造体(レコード型)の各メンバーに値をセット
with PDEXRec do begin
lStructSize := SizeOf(TPrintDlgEx);
hWndOwner := Handle;
Flags := PD_ALLPAGES or PD_NOPAGENUMS;
nMaxPageRanges := 3 ;
nMinPage := 1 ;
nMaxPage := 10 ;
nCopies := 1 ;
nStartPage := START_PAGE_GENERAL;
end ;
if PrintDlgEx(PDEXRec) = PD_RESULT_PRINT then begin
// ここに印刷コードを書く
// ダイアログで設定した内容は,構造体の各メンバーから取得する
end ;
end ;
// =============================================================================
// PringDlgEx APIで[印刷]ダイアログの表示 2
// ページ指定を設定してから表示
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
PDEXRec : TPrintDlgEx;
PageRangeArray : array [0 ..1 ] of TPrintPageRange;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PDEXRec, SizeOf(PDEXRec), #0 );
// ページ指定の場合の,ページの範囲を設定
PageRangeArray[0 ].nFromPage := 1 ;
PageRangeArray[0 ].nToPage := 3 ;
PageRangeArray[1 ].nFromPage := 5 ;
PageRangeArray[1 ].nToPage := 8 ;
// 構造体(レコード型)の各メンバーに値をセット
with PDEXRec do begin
lStructSize := SizeOf(TPrintDlgEx);
hWndOwner := Handle;
Flags := PD_PAGENUMS;
nMaxPageRanges := 3 ;
nPageRanges := Length(PageRangeArray);
lpPageRanges := @PageRangeArray[0 ];
nMinPage := 1 ;
nMaxPage := 10 ;
nCopies := 1 ;
nStartPage := START_PAGE_GENERAL;
end ;
if PrintDlgEx(PDEXRec) = PD_RESULT_PRINT then begin
// ここに印刷コードを書く
// ダイアログで設定した内容は,構造体の各メンバーから取得する
end ;
end ;
// -----------------------------------------------------------------------------
// TPropSheetPage(プロパティシート用構造体)のコールバック関数
// pfnDlgProcメンバの値に,この関数名のポインタを指定する
//
// この関数内では,EndDialogを呼び出してはいけない.戻り値は通常0とする
// -----------------------------------------------------------------------------
function PropSheetHookProc(Wnd: HWND; Message: UINT; wParam: WPARAM;
lParam: LPARAM): UINT stdcall ;
var
TextEdit : String ;
begin
Result := 0 ;
if Message = WM_COMMAND then begin
// [OK]ボタンクリック
// if wParam = IDOK でも可
if wParam = 1 then begin
MessageDlg('[ OK ] ボタンを押しましたね ' ,mtInformation, [mbOK], 0 );
end else
// [参照]ボタンクリック
if wParam = 102 then begin
TextEdit := 'プロパティシート追加のテスト' ;
SetDlgItemText(Wnd, 101 , PChar(TextEdit));
end ;
end ;
end ;
// =============================================================================
// PringDlgEx APIで[印刷]ダイアログの表示 3
// ページ指定を設定してから表示
// ダイアログにユーザ設定のタブシート(プロパティシート)を追加する例
// ここでは,リソースファイル(.RES)に格納してあるダイアログを表示
//
// プロパティシート関係のコードには,usesにCommCtrlが必要
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
hpsp : array [0 ..0 ] of PPropSheetPage;
psp : TPropSheetPage;
PDEXRec : TPrintDlgEx;
begin
// プロパティシート用構造体を初期化
FillChar(psp, SizeOf(TPropSheetPage), #0 );
with psp do begin
dwSize := SizeOf(TPropSheetPage);
dwFlags := PSP_USETITLE;
pszTemplate := MakeIntResource('DIALOG_1' );
pfnDlgProc := @PropSheetHookProc;
hInstance := hInstance;
// deFlagsにPSP_USETITLEがあると,元のダイアログのCatioonの変更が可能
pszTitle := '印刷ファイル' ;
end ;
// 上の設定で0番目のプロパティシートを作成
hpsp[0 ] := CreatePropertySheetPage(psp);
// PrintDlgEXで使用する構造体を0で埋める(つまり初期化)
FillChar(PDEXRec, SizeOf(PDEXRec), #0 );
with PDEXRec do begin
lStructSize := SizeOf(TPrintDlgEx);
hWndOwner := Handle;
Flags := PD_ALLPAGES or PD_NOPAGENUMS;
nMaxPageRanges := 3 ;
nMinPage := 1 ;
nMaxPage := 10 ;
nCopies := 1 ;
// プロパティシートの最初のタブを起動時にアクティブにする
// 通常は,START_PAGE_GENERALにしておく
nStartPage := 0 ;
nPropertyPages := Length(hpsp);
lphPropertyPages := @hpsp[0 ];
end ;
if PrintDlgEx(PDEXRec) = PD_RESULT_PRINT then begin
// ここに印刷コードを書く
// ダイアログで設定した内容は,構造体の各メンバーから取得する
end ;
end ;
end .
上のサンプルのプロパティシートに表示しているダイアログは,下図のように,リソースエディタで作成しました.リソースエディタとして XN Resource Editor の日本語版を使用し,リソースファイル名を TEST.RES として保存しています.
このリソースエディタ,XN Resource Editor の元の英語版で日本語を使用すると,リソースが壊れてしまうことがあります.
図12
リソースエディタで,プロパティシートに表示するダイアログを作成
上のサンプルコードでは,リソースの有無の確認をしていません.実際には,以下のようなコードで確認した方がいいでしょう.
copy code
var
hRes : HRSRC;
begin
// 該当リソースがない場合は,戻りが0となる
hRes := FindResource(hInstance, 'DIALOG_1' , RT_DIALOG);
04_ [ ページ設定 ] ダイアログ
- PageSetupDlg
関数
PapgeSetupDlg 関数のテストプログラムです.
引数は 1 つで,TPageSetupDlg 構造体です.下の表はこの構造体のメンバのリストです.
表4
TPageSetupDlg 構造体(レコード型)のメンバー
変数名と型は Delphi XE の CommDlg.pas のもの
メンバー
型
説明
lStructSize
DWORD
この構造体のサイズを必ず指定する
hwndOwner
HWND
ダイアログの親ウィンドウを指定する
0 でもよいがモーダル表示のためには指定した方がよい
hDevMode
HGLOBAL
DEVMODE 構造体のハンドル
実行後はダイアログで設定した値を返す
この情報でダイアログを表示する
hDevNames
HGLOBAL
プリンタドライバ名,デバイス名,出力ポート名等を格納した DEVNAMES 構造体のポインタを指定する
実行後はダイアログで設定した値を返す
hDevMode の指定よりも優先される
Flags
DWORD
ダイアログの初期化や各コントロールの表示等の制御のフラグを指定する.値は下表の組み合わせ
終了時には,ダイアログで設定した値が返ってくる
ptPaperSize
TPoint
ダイアログで選択した用紙のサイズが格納される
出力専用
Flags メンバの値によって寸法の単位が異なる
PSD_INHUNDREDTHSOFMILLIMETERS (1/1000 inch 単位)
PSD_INTHOUSANDTHSOFINCHES (1/100 mm 単位)
rtMinMargin
TRect
ページの余白の最低値を指定する TRect 構造体
ptPaperSize と同じく Flags メンバによって単位が異なる
rtMargin
TRect
ページ余白を指定する TRect 構造体
実行後にダイアログの設定値を返す
ptPaperSize と同じく Flags メンバによって単位が異なる
hInstance
HINST
Flags に以下のいずれかの値を指定した場合に指定する PSD_ENABLEPAGESETUPTEMPLATE
PSD_ENABLEPAGESETUPTEMPLATEHANDLE
この場合,この hInstance は lpPapgeSetupTemplateName か hPageSetupTemplateName で指定したダイアログボックステンプレート (EXE とか DLL )のインスタンスのハンドルとする
lCustData
LPARAM
lpPapgeSetupTemplateName か hPageSetupTemplateName を使用する場合に使用
アプリ独自のカスタムデータを設定する
フックのコールバック関数では,WM_INITDIALOG メッセージの時にこの値を受け取ることができる
lpfnPageSetupHook
Integer
[ページ設定] ダイアログの動作仕様を変更するためのコールバック関数のアドレスを指定する
Flags に PSD_ENABLEPAGESETUPHOOK を指定した場合に有効
lpfnPagePaintHook
Integer
[ページ設定] ダイアログの動作仕様を変更するためのコールバック関数のアドレスを指定する
Flags に PSD_ENABLEPAGEPAINTHOOK を指定した場合に有効
lpPageSetupTemplateName
PAnsiChar
[ページ設定] ダイアログテンプレートのリソース名を指定する
これで標準のダイアログを置き換える
Flags に PSD_ENABLEPAGESETUPTEMPLATE を指定した場合のみ有効
hPageSetupTemplate
HGLOBAL
[ページ設定] ダイアログテンプレートのリソースハンドルを指定する
これで標準のダイアログを置き換える
Flags に PSD_ENABLEPAGESETUPTEMPLATEHANDLE を指定した場合に有効
次の表は,PageSetupDlg 関数の引数である構造体の,Flags メンバーで設定可能な値のリストです.
また,図は Delphi のページ設定ダイアログのプロパティです.これらを較べてみると,Flags メンバーで指定可能なフラグが,Delphi の,これらのコンポーネントでも設定可能なことが分かります.
図13
TPageSetupDialog のプロパティ
PageSetupDlg 関数の Flags で設定可能なプロパティがほとんど用意されている
表5
Flags の値 (Delphi XE の CommDlg.pas のもの)
PSD_DEFAULTMINMARGINS
使用するプリンタデフォルトの余白最小値を用いる.デフォルトの設定
PSD_MARGINS,PSD_MINMARGINS がある場合は,それらが優先となる
PSD_INWININIINTLMEASURE
予約済み
PSD_MINMARGINS
rtMinMargin メンバの設定を有効にする
PSD_MARGINS
rtMarginメンバの設定を有効にする
PSD_INTHOUSANDTHSOFINCHES
ダイアログでの
余白の入力単位を 1/1000 インチとする
PSD_INHUNDREDTHSOFMILLIMETERS
ダイアログでの余白の入力単位を 1/100 mm とする
PSD_DISABLEMARGINS
[余白] グループのエディットボックスを無効にし,余白の設定をできないようにする
PSD_DISABLEPRINTER
[プリンタの設定] ボタンを無効にする
PSD_NOWARNING
デフォルトプリンタが無い場合の警告メッセージを表示しないようにする
PSD_DISABLEORIENTATION
印刷方向の設定を無効にする
PSD_RETURNDEFAULT
hDevNames と hDevMode メンバにデフォルトプリンタで初期化された DEVMODE と DEVNAMES 構造体のハンドルを返すようにする
PSD_DISABLEPAPER
用紙サイズと給紙方法の設定を無効にする
PSD_SHOWHELP
ダイアログボックスにヘルプボタンを表示する hwndOwner が 0 だと,表示しても反応しない
PSD_ENABLEPAGESETUPHOOK
lpfnPageSetupHook の設定を有効にする
PSD_ENABLEPAGESETUPTEMPLATE
[ページ設定」ダイアログのテンプレート(リソース)であるlpPageSetupTemplateNameの設定を有効にする
PSD_ENABLEPAGESETUPTEMPLATEHANDLE
「ページ設定」ダイアログのテンプレート(リソース) である hPageSetupTemplate の設定を有効にする
PSD_ENABLEPAGEPAINTHOOK
lpfnPagePaintHook の設定を有効にする
PSD_DISABLEPAGEPAINTING
サンプルページ(プレビューの様な画像)の描画をしない
ただし,描画のフックが有効な場合は,このフラグは無効となる
PSD_NONETWORKBUTTON
[プリンタの設定] ボタンをクリックして表示されるダイアログの [ネットワーク] ボタンを無効にする
lpfnSetupHoo と lpfnPrintHook メンバーで指定するコールバック関数は,PrintDlg 関数の定義と同じです.lpfnPrintHook メンバに指定するフック関数は,下図のような [サンプルページ] の描画をカスタマイズするために使用するものです.
これも Delphi では TPageSetupDialog コンポーネントのイベントとして用意されています.
Flags に PSD_DISABLEPAGEPAINTING を含めると,このサンプルページが白紙で表示されます.
lpfnPrintHook のフック関数内で処理可能なメッセージ類は,PagePaintHook callback function の記事を参考にしてください.
図14
TPageSetupDialog のイベント
図15
[サンプルページ] の意味
テストプログラムです.
コールバック関数の使用例と,あらかじめ,用紙サイズと印刷の向きを設定してからダイアログを表示するサンプルがあります.
図16
設計時画面
図17
[ページ設定] ダイアログ 1
Button1 クリック
Flags に何も設定していない場合
図18
[ページ設定] ダイアログ 2
Button2 クリック
コールバック関数内でダイアログの上に文字列を描画した例
図19
[ページ設定] ダイアログ 3
Button3 クリック
用紙サイズ,印刷の向き,余白を設定してからダイアログを表示
リスト6
PageSetupDlg 関数を使用したページ設定ダイアログ表示のテストコード
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CommDlg;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses Printers;
{$R *.dfm}
// =============================================================================
// PageSetupDlg APIで[ページ設定]ダイアログの表示 1
// 引数の構造体の設定を,ほとんどデフォルトのままで実行
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
PageSetupDlgRec : TPageSetupDlg;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PageSetupDlgRec, SizeOf(TPageSetupDlg), #0 );
// 各パラメータ(TPageSetupDlg構造体)の設定
with PageSetupDlgRec do begin
lStructSize := SizeOf(TPageSetupDlg);
hWndOwner := Handle;
Flags := 0 ;
end ;
// ダイアログ表示
if PageSetupDlg(PageSetupDlgRec) then begin
// 構造体の各メンバーから値を取出せる
end ;
end ;
// -----------------------------------------------------------------------------
// PageSetupDlg APIのコールバック関数
// ダイアログの操作用のlpfnPageSetupHookメンバのコールバック関数
// -----------------------------------------------------------------------------
function PageSetupHookProc(Wnd: HWND; Message: UINT; wParam: WPARAM;
lParam: LPARAM): UINT stdcall ;
var
TargetRect : TRect;
hWindowHDC : HDC;
ACanvas : TCanvas;
begin
Result := 0 ;
if Message = WM_INITDIALOG then begin
// ダイアログの表示位置を変更
SetWindowPos(Wnd, 0 , 200 , 30 , 0 , 0 , SWP_NOSIZE or SWP_NOZORDER);
end else
if Message = WM_CTLCOLORDLG then begin
hWindowHDC := GetWindowDC(Wnd);
// ウィンドウのデバイスハンドルを直接操作するコードでもよいが,
// TCanvasを使用した方が簡単なので,TCanvasを生成して実行
ACanvas := TCanvas.Create;
try
ACanvas.Handle := hWindowHDC;
GetClientRect(Wnd, TargetRect);
ACanvas.Brush.Style := bsClear;
ACanvas.Font.Color := clRed;
ACanvas.Font.Size := 20 ;
TargetRect.Bottom := 200 ;
DrawText(ACanvas.Handle, 'ページ設定ダイアログテスト' , -1 , TargetRect,
DT_CENTER or DT_VCENTER or DT_SINGLELINE);
finally
FreeAndNil(ACanvas);
end ;
end ;
end ;
// =============================================================================
// PageSetupDlg APIで[ページ設定]ダイアログの表示 2
// コールバック関数の使用例
// 構造体のlpfnPageSetupHookを使用した例
// この他に,サンプルページ描画用のフック関数が使用可能
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
PageSetupDlgRec : TPageSetupDlg;
begin
// 構造体を0で埋める(つまり初期化)
FillChar(PageSetupDlgRec, SizeOf(TPageSetupDlg), #0 );
// 各パラメータ(TPageSetupDlg構造体)の設定
with PageSetupDlgRec do begin
lStructSize := SizeOf(TPageSetupDlg);
hWndOwner := Handle;
Flags := PSD_ENABLEPAGESETUPHOOK;
lpfnPageSetupHook := @PageSetupHookProc;
end ;
// ダイアログ表示
if PageSetupDlg(PageSetupDlgRec) then begin
// 構造体の各メンバーから値を取出せる
end ;
end ;
// =============================================================================
// PageSetupDlg APIで[ページ設定]ダイアログの表示 3
// 用紙サイズと印刷の方向と余白を設定してからダイアログを表示する例
// usesにPrintersが必要
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
PageSetupDlgRec : TPageSetupDlg;
ADevice : array [0 ..MAX_PATH - 1 ] of Char;
ADriver : array [0 ..MAX_PATH - 1 ] of Char;
APort : array [0 ..MAX_PATH - 1 ] of Char;
ADeviceMode : THandle;
ADevMode : PDeviceMode;
begin
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
// ADeviceModeのメモリをロックしDEVMODE構造体のポインタを取得
ADevMode := GlobalLock(ADeviceMode);
try
// 実際には該当メンバーをサポートしているかチェックしてからセットするのがよい
ADevMode^.dmPaperSize := DMPAPER_A3;
ADevMode^.dmOrientation := DMORIENT_LANDSCAPE;
finally
GlobalUnlock(ADeviceMode);
end ;
// 構造体を0で埋める(つまり初期化)
FillChar(PageSetupDlgRec, SizeOf(TPageSetupDlg), #0 );
// 各パラメータ(TPageSetupDlg構造体)の設定
with PageSetupDlgRec do begin
lStructSize := SizeOf(TPageSetupDlg);
hWndOwner := Handle;
hDevMode := ADeviceMode;
rtMargin.Top := 3000 ;
rtMargin.Left := 2500 ;
rtMargin.Right := 2000 ;
rtMargin.Bottom := 3200 ;
Flags := PSD_MARGINS or PSD_INHUNDREDTHSOFMILLIMETERS;
end ;
// ダイアログ表示
if PageSetupDlg(PageSetupDlgRec) then begin
// 構造体の各メンバーから値を取出せる
end ;
end ;
end .
05_ TPrinter を使用した印刷コードの実装
PrintDlg 関数,PrintDlgEx 関数を使用してプリンタ関係の設定をした場合,基本的には,StartDoc 等の Windows API を使用して印刷コードを書くことになります.
PrintDlg 関数や PrintDlgEx 関数で設定した内容は,TPrinter オブジェクトには反映されません.
Windows API のダイアログ関数で設定を行った場合でも,TPrinter を使用して印刷コードを実装するには,該当プリンタの DEVMODE 構造体の値を使用して,プリンタの設定を行う必要があります.その参考コードです.
(1) hDevNames メンバーからプリンタ名を取出す
(2) そのプリンタ名のプリンタを現在のプリンタとする
(3) hDevMode メンバーの値で,そのプリンタの DEVMODE 構造体を設定
(4) TPrinter を使用した印刷コードを書く
という手順となります.コードについては以下のリンクの記事も参考にしてください.
リスト7
TPrinter を使用して印刷する場合の参考コード
uses に Printers が必要
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CommDlg;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses Printers;
{$R *.dfm}
// =============================================================================
// TPrinterを使用して印刷する場合の参考コード
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AIndex : Integer;
ADevice : array [0 ..MAX_PATH - 1 ] of Char;
ADriver : array [0 ..MAX_PATH - 1 ] of Char;
APort : array [0 ..MAX_PATH - 1 ] of Char;
ADeviceMode : THandle;
ADevMode : PDeviceMode;
PrintDlgRec : TPrintDlg;
Offset : PChar;
PrinterName : String ;
hPrinter : THandle;
Mode : Cardinal;
Names : PDevNames;
begin
FillChar(PrintDlgRec, SizeOf(TPrintDlg), #0 );
with PrintDlgRec do begin
lStructSize := SizeOf(TPrintDlg);
hWndOwner := Handle;
Flags := PD_PRINTSETUP;
end ;
if PrintDlg(PrintDlgRec) then begin
Update;
Names := GlobalLock(PrintDlgRec.hDevNames );
try
Offset := PChar(Names);
PrinterName := String (Offset + Names.wDeviceOffset);
finally
GlobalUnlock(PrintDlgRec.hDevNames);
end ;
AIndex := Printer.Printers.IndexOf(PrinterName);
Printer.PrinterIndex := AIndex;
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
Printer.SetPrinter(ADevice, ADriver, APort, 0 );
Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
ADevMode := GlobalLock(ADeviceMode);
try
ADeviceMode := PrintDlgRec.hDevMode;
finally
GlobalUnlock(ADeviceMode);
end ;
Printer.SetPrinter(ADevice, ADriver, APort, ADeviceMode);
Printer.Title := '印刷テスト' ;
Printer.BeginDoc;
try
Printer.Canvas.Font.Size := 25 ;
Printer.Canvas.TextOut(0 , 0 , 'プリンタ関係ダイアログAPIのテスト' );
finally
Printer.EndDoc;
end ;
end ;
end ;
end .