Delphi Programming / Object Pascal
[掲載 2010年05月02日] [更新 2014年08月28日] Delphi サンプルプログラム集
722_PDF 文書の印刷
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro + Adobe Reader 9.3.0
開発環境にも,実行環境にも,Adobe Acrobat または Adobe Readerが必要
722_PDF_Print.zip [2,217 KB] 2014年08月28日版 (EXE 同梱)
2014年08月28日
終了時に,最初に検出した Adobe Reader のウィンドウを閉じるようにした
ShellExecuteEx または CreateProcess 関数を使用して Adobe Acrobat の PDF 文書を印刷するサンプルです.開発時にも実行時にも Adobe Acrobat または Adobe Reader が必要です.
このページのサンプルでは,印刷後に PDF 文書を閉じて Acrobat を終了するようにしています.
ShellExecuteEx 関数,CreateProcess 関数による PDF 文書印刷では,印刷ページを指定して印刷することはできません.プリンタは Adobe Reader のコマンドラインオプションで指定できます.
ShellExecuteEx では,TShellExecuteInfo の lpVerb メンバーの値,ShellExcete 関数では,第 2 引数で操作内容を指定できます.
それらの値は下表のようになっています.ただし,起動するアプリケーションがこの操作内容をサポートしていなければ指定の操作は実行しません.
edit
エディタを起動する
explore
エクスプローラを起動する
open
ファイルを開く.指定なしの場合のデフォルト値
print
印刷
properties
プロパティダイアログを表示
例えば,ワードの文書やエクセルのブックは次のようなコードで印刷できます.この時,ワードやエクセルが起動して,「通常使用するプリンタ」で印刷します.しかし,PDF 文書はこの方法では印刷はできません.PDF 文書を印刷するには,Adobe Acrobat または Adobe Reader の起動オプションに /p を指定して起動します.本ページのサンプルは,このことを利用しています.
ワードやエクセルの印刷は,それらのオブジェクトのインスタンスを生成する方法が確実です.そうすれば,各種の印刷設定が可能です.ワードやエクセルを非表示とすることもできます.
一口に印刷と言っても,アプリケーションの種類は様々です.印刷対象も様々です.どう考えても汎用的な方法があるとは考えられません.
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
FilePath : String ;
begin
// 印刷するファイルのフルパス
FilePath := ExtractFilePath(Application.ExeName) + 'TestWord.doc' ;
ShellExecute(Handle, 'print' , PChar(FilePath), nil , nil , 0 );
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
FilePath : String ;
begin
// 印刷するファイルのフルパス
FilePath := ExtractFilePath(Application.ExeName) + 'TestExcel.xls' ;
ShellExecute(Handle, 'print' , PChar(FilePath), nil , nil , 0 );
end ;
[備考]
ShellExecuteEx 関数,CreateProcess 関数による PDF 文書印刷では,印刷ページを指定して印刷することはできません.Adobe Acrobat オブジェクトを使用すれば印刷ページの指定ができますが,開発環境にも,実行環境にも,Adobe Acrobat が必要です.
01_ 印刷後に Acrobat を Terminate で終了 - ShellExecuteEx 使用
ShellExecuteEx 関数は,ファイル名に関連付けのあるアプリが起動できます.ここではコマンドラインオプションを使用するために,EXE 名も指定して実行します.
Adobe Acrobat または Adobe Reader は最小化で起動します.Acrobat は,印刷終了後に Terminate Process 関数で起動したそのプロセスを終了するようにしています.印刷終了は,印刷に使用したプリンタのジョブ数で判定しています.
図1
設計時画面
リスト1
PDF文書を ShellExecuteEx 関数で印刷 (Acrobat の終了は TerminateProcess 関数)
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ShellAPI, Printers, WinSpool;
{$R *.dfm}
// -----------------------------------------------------------------------------
// ShellExecuteExでEXE実行
// ShellExecuteExはファイル(拡張子)の関連付けでアプリを起動する
//
// ShellExcuteExの使用にはusesにShellAPIが必要
// -----------------------------------------------------------------------------
function RunExeFile(hWnd: HWND; AFile: String ; AParameters: String ;
Verb: String ): Cardinal;
var
AInfo : TShellExecuteInfo;
ret : DWORD;
begin
FillChar(AInfo, SizeOf(AInfo), 0 );
AInfo.cbSize := SizeOf(AInfo);
AInfo.Wnd := hWnd;
AInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
AInfo.lpVerb := PChar(Verb);
AInfo.lpFile := PChar(AFile);
AInfo.lpParameters := PChar(AParameters);
AInfo.nShow := SW_HIDE;
ShellExecuteEx(@AInfo);
// ShellExecuteExは成功するとhInstAppに33以上の値を返す
if AInfo.hInstApp >= 33 then begin
// 起動終了まで待つための処理
repeat
ret := WaitForInputIdle(AInfo.hProcess, 50 );
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
end ;
// 戻値は起動プロセスのハンドルとする
Result := AInfo.hProcess;
end ;
// =============================================================================
// AcrobatまたはAcrobat Readerを非表示で起動してPDFを印刷
//
// 例 EPSON PM-3300Cで印刷する場合
// AppCmd := '/n /s /h /t ' + '"'+FileName+'" ' + '"EPSON PM-3300C" ';
// Acrobatで印刷する場合は,AppStr := 'Acrobat.exe';とする
//
// Windows XP(SP3) + Acrobat Reader 8.2.0
// Acrobatウィンドウの枠線が表示されてしまう
// Windows 7 U64 + Acrobat Reader 9.3.0
// Acrobatのウィドウは最小化されて起動される
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
// ---------------------------------------------------------------------------
// hPrinterのプリンタの現在のジョブ数を取得
// プリンタの状態を取得したい場合は,EnumJobsコールバック関数を使用して,
// JOB_INFO_2構造体のStatusから取得可能
// ---------------------------------------------------------------------------
function GetJobCount(hPrinter: Cardinal): Cardinal;
var
pPinfo2 : PPrinterInfo2;
dwNeeded : DWORD;
begin
WinSpool.GetPrinter(hPrinter, 2 , nil , 0 , @dwNeeded);
// 構造体の配列に必要なバイト数を確保
GetMem(pPinfo2, dwNeeded);
try
WinSpool.GetPrinter(hPrinter, 2 , pPInfo2, dwNeeded, @dwNeeded);
Result := pPInfo2^.cJobs;
finally
FreeMem(pPinfo2);
end ;
end ;
// ---------------------------------------------------------------------------
var
PrinterName : String ;
hPrinter : Cardinal;
hProcess : Cardinal;
AppStr : String ;
AppCmd : String ;
JobCnt : Cardinal;
begin
// 対象のプリンタ名.ここでは通常使用するプリンタとする
PrinterName := Printer.Printers [Printer.PrinterIndex];
// 対象のプリンタのハンドルを取得
WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil );
if hPrinter <> 0 then begin
OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0 ));
OpenDialog1.DefaultExt := 'PDF' ;
OpenDialog1.Filter := 'PDF|*.PDF' ;
OpenDialog1.Options := [ofReadOnly];
if OpenDialog1.Execute then begin
Update;
AppStr := 'ACRORD32.EXE' ;
AppCmd := '/n /s /h /p ' + AnsiQuotedStr(OpenDialog1.FileName, '"' ) + ' ' ;
// 現在のこのプリンタのジョブ数を取得しておく
JobCnt := GetJobCount(hPrinter);
// PDF文書の印刷開始.hProcessはプロセスのハンドル
hProcess := RunExeFile(0 , AppStr, AppCmd, '' );
// データの印刷開始待ち
// 印刷ジョブ数が1つ増えれば印刷開始とみなす
if GetJobCount(hPrinter) = JobCnt then begin
while True do begin
if GetJobCount(hPrinter) > JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
end ;
// 印刷終了待ち
// このPDF印刷前のジョブ数以下であれば印刷終了とみなす
while True do begin
if GetJobCount(hPrinter) <= JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
// 印刷が終わったらAcrobatも終了する
// PostMessage(hProcess, WM_CLOSE, 0, 0);ではAcrobatのプロセス全てが閉じ
// ることにならないので注意.これは他のEXE起動の場合も同じ
TerminateProcess(hProcess, 0 );
end ;
WinSpool.ClosePrinter(hPrinter);
end ;
end ;
end .
02_ 印刷後に Acrobat を WM_CLOSE で終了 - ShellExecuteEx 使用
TerminateProcess 関数でプロセスを終了すると問題が発生することがあります.起動した EXE が使用している DLL には終了が通知されず,メモリ上に残る場合があります.以下のサンプルではこれを避けるために,Acrobat のトツプウィンドウを検索して,そのウィンドウに WM_CLOSE を送って終了するようにしています.
図2
設計時画面
リスト2
PDF文書を ShellExecuteEx 関数で印刷 (終了は WM_CLOSE メッセージ)
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
function RunExeFile(hWnd: HWND; AFile: String ; AParameters: String ;
Verb: String ): Cardinal;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ShellAPI, Printers, WinSpool, tlhelp32;
{$R *.dfm}
// -----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのクラス名を取得する関数
// -----------------------------------------------------------------------------
function GetWindowClassNameStr(hWindow: HWND): String ;
var
Buffer : array [0 ..MAX_PATH-1 ] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0 );
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then begin
Result := Buffer;
end else begin
Result := '' ;
end ;
end ;
// -----------------------------------------------------------------------------
// 対象アプリのウィンドウハンドルを取得して閉じるのためのコールバック関数
// Acrobatのトップウィンドのクラス名はAcrobatSDIWindow
// -----------------------------------------------------------------------------
function EnumTopWindowAndClose(hTopWnd: HWND; lp: LPARAM): BOOL; stdcall ;
var
ClassName : String ;
begin
Result := True;
// クラス名がAcrobatのクラス名であれば,そのウィンドウを閉じる
ClassName := GetWindowClassNameStr(hTopWnd);
if UpperCase(ClassName) = 'ACROBATSDIWINDOW' then begin
PostMessage(hTopWnd, WM_CLOSE, 0 , 0 );
// 処理したらこれ以上の列挙はしない
Result := False;
end ;
end ;
// -----------------------------------------------------------------------------
// ShellExecuteExでEXE実行
// ShellExecuteExはファイル(拡張子)の関連付けでアプリを起動する
//
// ShellExcuteExの使用にはusesにShellAPIが必要
// -----------------------------------------------------------------------------
function TForm1.RunExeFile(hWnd: HWND; AFile, AParameters, Verb: String ): Cardinal;
var
AInfo : TShellExecuteInfo;
ret : DWORD;
begin
FillChar(AInfo, SizeOf(AInfo), 0 );
AInfo.cbSize := SizeOf(AInfo);
AInfo.Wnd := hWnd;
AInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
AInfo.lpVerb := PChar(Verb);
AInfo.lpFile := PChar(AFile);
AInfo.lpParameters := PChar(AParameters);
AInfo.nShow := SW_HIDE;
ShellExecuteEx(@AInfo);
// ShellExecuteExは成功するとhInstAppに33以上の値を返す
if AInfo.hInstApp >= 33 then begin
// 起動終了まで待つための処理
repeat
ret := WaitForInputIdle(AInfo.hProcess, 50 );
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
end ;
// 戻値は起動プロセスのハンドルとする
Result := AInfo.hProcess;
end ;
// =============================================================================
// AcrobatまたはAcrobat Readerを非表示で起動してPDFを印刷
//
// 例 EPSON PM-3300Cで印刷する場合
// AppCmd := '/n /s /h /t ' + '"' + FileName+'" ' + '"EPSON PM-3300C" ';
// Acrobatで印刷する場合は,AppStr := 'Acrobat.exe';とする
//
// Windows XP(SP3) + Acrobat Reader 8.2.0
// Acrobatウィンドウの枠線が表示されてしまう
// Windows 7 U64 + Acrobat Reader 9.3.0
// Acrobatのウィドウは最小化されて起動される
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
// ---------------------------------------------------------------------------
// hPrinterのプリンタの現在のジョブ数を取得
// プリンタの状態を取得したい場合は,EnumJobsコールバック関数を使用して,
// JOB_INFO_2構造体のStatusから取得可能
// ---------------------------------------------------------------------------
function GetJobCount(PrinterName: String ): Cardinal;
var
hPrinter : Cardinal;
pPinfo2 : PPrinterInfo2;
dwNeeded : DWORD;
begin
Result := 0 ;
WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil );
if hPrinter > 0 then begin
WinSpool.GetPrinter(hPrinter, 2 , nil , 0 , @dwNeeded);
// 構造体の配列に必要なバイト数を確保
GetMem(pPinfo2, dwNeeded);
try
WinSpool.GetPrinter(hPrinter, 2 , pPInfo2, dwNeeded, @dwNeeded);
Result := pPInfo2^.cJobs;
finally
FreeMem(pPinfo2);
end ;
WinSpool.ClosePrinter(hPrinter);
end ;
end ;
// ---------------------------------------------------------------------------
var
PrinterName : String ;
AppStr : String ;
AppCmd : String ;
JobCnt : Cardinal;
begin
// 対象のプリンタ名
PrinterName := Printer.Printers [Printer.PrinterIndex];
OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0 ));
OpenDialog1.DefaultExt := 'PDF' ;
OpenDialog1.Filter := 'PDF|*.PDF' ;
OpenDialog1.Options := [ofReadOnly];
if OpenDialog1.Execute then begin
Update;
AppStr := 'ACRORD32.EXE' ;
AppCmd := '/n /s /h /p ' + AnsiQuotedStr(OpenDialog1.FileName, '"' ) + ' ' ;
// 印刷開始前のこのプリンタのジョブ数を取得しておく
JobCnt := GetJobCount(PrinterName);
// PDF文書の印刷開始
RunExeFile(0 , AppStr, AppCmd, '' );
// データの印刷開始待ち
// 印刷ジョブ数が1つ増えれば印刷開始とみなす
if GetJobCount(PrinterName) = JobCnt then begin
while True do begin
if GetJobCount(Printername) > JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
end ;
// 印刷終了待ち
// このPDF印刷前のジョブ数以下であれば印刷終了とみなす
while True do begin
if GetJobCount(PrinterName) <= JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
// 起動中のアプリを検出して終了処理を行う
EnumChildWindows(0 , @EnumTopWindowAndClose, 0 );
end ;
end ;
end .
03_ 印刷設定ダイアログを表示して印刷 - ShellExecuteEx 使用
上のサンプルと同じく ShellExecuteEx 関数で印刷しますが,コマンドラインオプションで,印刷設定ダイアログを表示して印刷の設定を行う例です.
ShellExcuteEx や CreateProcess 関数による PDF 文書の印刷では,プリンタの設定をコマンドラインで指定できません.したがって,印刷結果は,その PDF 文書の設定によって異なります.もし期待通りの印刷ができない場合は,その PDF 文書の設定を見直す必要があります.あるいは,このように印刷設定ダイアログを表示して印刷することになるでしょう.
図3
設計時画面
図4
実行中の画面
PDF 文書が表示され,印刷設定ダイアログも表示される
リスト3
PDF文書を ShellExecuteEx 関数で印刷 - 印刷設定ダイアログを表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
hProcess : Cardinal;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ShellAPI, Printers;
{$R *.dfm}
// -----------------------------------------------------------------------------
// ShellExecuteExでEXE実行
// ShellExecuteExはファイル(拡張子)の関連付けでアプリを起動する
//
// ShellExcuteExの使用にはusesにShellAPIが必要
// -----------------------------------------------------------------------------
function RunExeFile(hWnd: HWND; AFile: String ; AParameters: String ;
Verb: String ): Cardinal;
var
AInfo : TShellExecuteInfo;
ret : DWORD;
begin
FillChar(AInfo, SizeOf(AInfo), 0 );
AInfo.cbSize := SizeOf(AInfo);
AInfo.Wnd := hWnd;
AInfo.fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_NO_UI;
AInfo.lpVerb := PChar(Verb);
AInfo.lpFile := PChar(AFile);
AInfo.lpParameters := PChar(AParameters);
AInfo.nShow := SW_HIDE;
ShellExecuteEx(@AInfo);
// ShellExecuteExは成功するとhInstAppに33以上の値を返す
if AInfo.hInstApp >= 33 then begin
// 起動終了まで待つための処理
repeat
ret := WaitForInputIdle(AInfo.hProcess, 50 );
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
end ;
// 戻値は起動プロセスのハンドルとする
Result := AInfo.hProcess;
end ;
// =============================================================================
// AcrobatまたはAcrobat Readerを起動して印刷設定ダイアログを表示
//
// 例 EPSON PM-3300Cで印刷する場合
// AppCmd := '/n /s /h /t ' + '"'+FileName+'" ' + '"EPSON PM-3300C" ';
// Acrobatで印刷する場合は,AppStr := 'Acrobat.exe';とする
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
AppStr : String ;
AppCmd : String ;
begin
OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0 ));
OpenDialog1.DefaultExt := 'PDF' ;
OpenDialog1.Filter := 'PDF|*.PDF' ;
OpenDialog1.Options := [ofReadOnly];
if OpenDialog1.Execute then begin
Update;
AppStr := 'ACRORD32.EXE' ;
AppCmd := '/n /s /p ' + AnsiQuotedStr(OpenDialog1.FileName, '"' );
// PDF文書の印刷開始
hProcess := RunExeFile(0 , AppStr, AppCmd, '' );
end ;
end ;
// =============================================================================
// AcrobatまたはAcrobat Readerを閉じてこのプログラムも終了
// TerminateProcessを使用しないコードについては他のサンプルコードを参照
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if hProcess <> 0 then begin
TerminateProcess(hProcess, 0 );
end ;
Close;
end ;
end .
04_ Acrobat を CreateProcess 関数で起動して印刷
次は CreateProcess 関数を使用する例です.CreateProcess 関数では,実行する EXE 名は原則としてフルパスが必要です.
この EXE のフルパスはインストール先のフルパスなわけですが,例えば Delphiや Adobe Acrobat では,インストールするバージョンごとにレジストリの場所が違います.当然インストール先も違ってきます.そのような場合は,拡張子に関連付けされたアプリのフルパスを取得する AssocQueryString 関数を使用します.
AssocQueryString 関数だけのサンプルが [480_ 拡張子関連付けの実行ファイル名] にあります.
図5
設計時画面
PDF 文書を選択した時に下図のように,[アプリケーションが関連付けられていません] のエラーが発生することがあります.この現象は,Adobe Acrobat と Adobe Reader の両方がインストールされていて,各々の拡張子の関連付けが異なっている場合に発生するようです.
[ファイル名を指定して実行] のダイアログに,以下のコマンドを入力して拡張子の設定を確認してください.
%SystemRoot%\System32\control.exe /name Microsoft.DefaultPrograms /page pageFileAssoc
図6
拡張子関連付けのエラー
リスト4
PDF文書を CreateProcess 関数で印刷
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
function ExecuteApp(ExeAppStr: String ): DWORD;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ShellAPI, Printers, ComObj, WinSpool, ShLwApi;
{$R *.dfm}
// -----------------------------------------------------------------------------
// ウィンドウハンドルからウィンドウのクラス名を取得する関数
// -----------------------------------------------------------------------------
function GetWindowClassNameStr(hWindow: HWND): String ;
var
Buffer : array [0 ..MAX_PATH-1 ] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0 );
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then begin
Result := Buffer;
end else begin
Result := '' ;
end ;
end ;
// -----------------------------------------------------------------------------
// 対象アプリのウィンドウハンドルを取得して閉じるのためのコールバック関数
// Acrobatのトップウィンドのクラス名はAcrobatSDIWindow
// -----------------------------------------------------------------------------
function EnumTopWindowsAndClose(hwindow: HWnd; lparam: lparam): BOOL; stdcall ;
var
ClassName : String ;
begin
Result := True;
if (GetWindow(hwindow, GW_OWNER) = 0 ) then begin
// クラス名がAcrobatのクラス名であれば,そのウィンドウを閉じる
ClassName := GetWindowClassNameStr(hwindow);
if UpperCase(ClassName) = 'ACROBATSDIWINDOW' then begin
PostMessage(hwindow, WM_CLOSE, 0 , 0 );
// 処理したらこれ以上の列挙はしない
Result := False;
end ;
end ;
end ;
// -----------------------------------------------------------------------------
// CreaateProcess関数の実行
// -----------------------------------------------------------------------------
function TForm1.ExecuteApp(ExeAppStr: String ): DWORD;
var
PI : TProcessInformation;
SI : TStartupInfo;
ret : DWORD;
begin
Result := 0 ;
FillChar(SI, SizeOf(SI), #0 );
SI.cb := SizeOf(TStartupInfo);
SI.dwFlags := STARTF_USESHOWWINDOW;
SI.wShowWindow := SW_HIDE;
SetLength(ExeAppStr, Length(ExeAppStr));
// 参照カウンタ対策
UniqueString(ExeAppStr);
if not CreateProcess(nil ,
PChar(ExeAppStr),
nil ,
nil ,
False,
0 ,
nil ,
nil ,
SI,
PI) then begin
Result := WAIT_FAILED;
end else begin
// プロセスが起動するまでまつ(印刷開始とは限らない)
repeat
ret := WaitForInputIdle(PI.hProcess, 50 );
Application.ProcessMessages;
until ret <> WAIT_TIMEOUT;
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end ;
end ;
// -----------------------------------------------------------------------------
// 指定した拡張子に関連づけた実行ファイルのフルパス名を取得する関数
// 取得するのは拡張子の関連付けがレジストリに登録されているものの最初のもの
//
// AssocQueryStringの使用にはusesにShLwApiが必要
// OleCheckやWin32Checkの使用にはuuseにComObjが必要
// -----------------------------------------------------------------------------
function GetRelatedFullExePath(ExtName: String ):string ;
var
Buf : array [0 ..MAX_PAtH-1 ] of Char;
dwOut : DWORD;
begin
FillChar(Buf, SizeOf(Buf), #0 );
dwOut := MAX_PATH;
OleCheck(AssocQueryString(ASSOCF_NOTRUNCATE,
ASSOCSTR_COMMAND,
PChar(ExtName),
nil ,
Buf,
@dwOut));
Result := Buf;
if Length(Result) = 0 then begin
Result := '' ;
end else begin
// "(ダブルコーテーション)で囲まれている場合
if AnsiPos('" ' , Result) > 0 then begin
Result := Copy(Result, 1 , Pos('" ' , Result) + 1 );
end ;
end ;
end ;
// =============================================================================
// AcrobatまたはAcrobat Readerを非表示で起動してPDFを印刷
//
// Windows XP(SP3) + Acrobat Reader 8.2.0
// Acrobatウィンドウの枠線が表示されてしまう
// Windows 7 U64 + Acrobat Reader 9.3.0
// Acrobatのウィドウは最小化されて起動される
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
// ---------------------------------------------------------------------------
// hPrinterのプリンタの現在のジョブ数を取得
// プリンタの状態を取得したい場合は,EnumJobsコールバック関数を使用して,
// JOB_INFO_2構造体のStatusから取得可能
// ---------------------------------------------------------------------------
function GetJobCount(PrinterName: String ): Cardinal;
var
hPrinter : Cardinal;
pPinfo2 : PPrinterInfo2;
dwNeeded : DWORD;
begin
Result := 0 ;
WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil );
if hPrinter > 0 then begin
WinSpool.GetPrinter(hPrinter, 2 , nil , 0 , @dwNeeded);
// 構造体の配列に必要なバイト数を確保
GetMem(pPinfo2, dwNeeded);
try
WinSpool.GetPrinter(hPrinter, 2 , pPInfo2, dwNeeded, @dwNeeded);
Result := pPInfo2^.cJobs;
finally
FreeMem(pPinfo2);
end ;
WinSpool.ClosePrinter(hPrinter);
end ;
end ;
// ---------------------------------------------------------------------------
var
ExeFullPath : String ;
AppCmdStr : String ;
PrinterName : String ;
JobCnt : Cardinal;
begin
// 対象のプリンタ名
// ここでは通常使用するプリンタ
PrinterName := Printer.Printers [Printer.PrinterIndex];
OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0 ));
OpenDialog1.DefaultExt := 'PDF' ;
OpenDialog1.Filter := 'PDF|*.PDF' ;
OpenDialog1.Options := [ofReadOnly];
if OpenDialog1.Execute then begin
Update;
ExeFullPath := GetRelatedFullExePath('.PDF' );
// 印刷開始前のこのプリンタのジョブ数を取得しておく
JobCnt := GetJobCount(PrinterName);
// 印刷実行
AppCmdStr := ExeFullPath + ' /n /s /h /p ' ;
AppCmdStr := AppCmdStr + AnsiQuotedStr(OpenDialog1.FileName, '"' );
ExecuteApp(AppCmdStr);
// データの印刷開始待ち
// 印刷ジョブ数が1つ増えれば印刷開始とみなす
if GetJobCount(PrinterName) = JobCnt then begin
while True do begin
if GetJobCount(Printername) > JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
end ;
// 印刷終了待ち
// このPDF印刷前のジョブ数以下であれば印刷終了とみなす
while True do begin
if GetJobCount(PrinterName) <= JobCnt then break;
Application.ProcessMessages;
Sleep(1 );
end ;
// 起動中のアプリを検出して終了処理を行う
EnumWindows(@EnumTopWindowsAndClose, 0 );
end ;
end ;
end .
05_ Adobe Acrobat または Adobe Reader がない環境での印刷