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 のウィンドウを閉じるようにした




PDF 文書の印刷


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 を指定して起動します.本ページのサンプルは,このことを利用しています.

ワードやエクセルの印刷は,それらのオブジェクトのインスタンスを生成する方法が確実です.そうすれば,各種の印刷設定が可能です.ワードやエクセルを非表示とすることもできます.
一口に印刷と言っても,アプリケーションの種類は様々です.印刷対象も様々です.どう考えても汎用的な方法があるとは考えられません.



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 関数)
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 メッセージ)
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 関数で印刷 - 印刷設定ダイアログを表示
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 関数で印刷
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 がない環境での印刷


以下の記事を参考にしてください.