Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2009年03月18日] [更新 2013年10月23日] Delphi サンプルプログラム集
010_PRINTER_INFO 構造体とプリンタの列挙
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
010_Printer_Info_Structure.zip [3,947 KB] 2013年10月23日版 (EXE 同梱)








PRINTER_INFO 構造体とプリンタの列挙


プリンタ情報構造体,PRINTER_INFO_X (X は数字)を使用してプリンタを列挙したり,その情報を取得するサンプルプログラムです.この構造体は 9 までありますが,Delphi の WinSpool.pas で定義されているのは 6 までです.
サンプルでは,原則 Windows 9X 関係の説明と処理コードは省略しています.
PRINTER_INFO 構造体を含め,プリンタ,印刷関係の関数の詳しい説明は,MSDN ライブラリを参考にしてください.これらの関数を使用するには uses に WinSpool が必要です.

変数の型名は,MSDN ライブラリの表記を用いています.Delphi では別の型名を使用しなけれならない場合があります.テストプログラムのコード,あるいは,コード補完機能等で確認してください.
以下は,このサンプルプログラムで使用している関数の簡単な説明です.

プリンタの情報を取得する際,物理プリンタと直接通信することはありません.プリンタドライバとの通信となります.プリンタドライバが提供していない情報は取得できません.また,プリンタドライバに印刷ジョブを送信しないと取得できない情報もあります.プリンタドライバによっては,実際の物理プリンタの状態を取得可能にしているものもあります.

物理プリンタと直接通信する方法もあります.プリンタデバイスに直接コマンドを送信します.それには,プリンタのメーカーが提供している,それらのコマンド類の仕様を知る必要があります.
MS-DOS の時代は,この方法で印刷のプログラムを作成していました.したがって,プリンタメーカーあるいは,プリンタによって印刷のコードが違うこともありました.

本ページで扱う,プリンタ関係情報の列挙用 Windows API は 2 回の呼び出しが必要です.1 回目は情報の格納に必要なメモリサイズの取得,次に実際の情報取得です.この手順は,多くの Windows API で使われる手法です.





01_EnumPrinters 関数


利用可能なプリンタ、プリントサーバー、ドメイン、プリントプロバイダを列挙する関数です.関数が成功すると True を返します.引数は,左から順番に下表の内容となっています.


表1
EnumPrinters 関数の引数

Flags DWORD 列挙するプリンタオブジェクトの種類を,次の表の値の組合せで指定する
Level4 では ,PRINTER_ENUM_CONNECTIONS と PRINTER_ENUM_LOCAL のみ使用可能
pName LPTSTR nil またはプリントプロバイダ,サーバ名,ドメイン名
この値と Lebel の値と,Flags の組合せによって取得できる列挙リストが異なる
詳細は MSDN ライブラリの記事を参照
Level DWORD プリンタ情報構造体のタイプ番号
PRINTER_INFO_1 の内容を取得したい場合は 1
PRINTER_INFO_2 の内容を取得したい場合は 2
...
pPrinterEnum LPBYTE プリンタ情報構造体のバッファ
関数実行後,このバッファに複数の構造体の配列が格納される
各構造体がプリンタオブジェクトに関するデータを表わす
cbBuf DWORD バッファのサイズをバイト数で指定する
pcbNeeded LPDWORD 関数が成功すると,コピーされたバイト数が格納される
0 を与えると必要なバイト数を返す
pcReturned LPDWORD 関数実行後,構造体の数が格納される


表2
EnumPrinters 関数の Flags パラメータで指定するプリンタオブジェクトの種類

PRINTER_ENUM_LOCAL pName パラメータを無視し,ローカルにインストールされているプリンタを列挙する
PRINTER_ENUM_NAME pName で指定したプリンタを列挙する
pName にはサーバー,ドメイン,プリントプロバイダのいずれかを指定する
pName が nil の場合,使用可能なプリントプロバイダ列挙
PRINTER_ENUM_SHARED 共有属性のプリンタを列挙する
単独では使用できない.他の値と組み合わせて使用する
PRINTER_ENUM_DEFAULT Win 9X 用
PRINTER_ENUM_CONNECTIONS 接続続を確立しているプリンタのリストを列挙する
PRINTER_ENUM_NETWORK Lebel の 値が 1 の時のみ有効な値
コンピュータと同じドメイン内のネットワークプリンタを列挙する
PRINTER_ENUM_REMOTE Lebel の値が 1 の時のみ有効な値
コンピュータと同じドメイン内の,ネットワークプリンタとプリントサーバーを列挙する


EnumPrinters 関数の使用例です.
使用可能なプリンタ全ての数と,共有プリンタの数を取得して表示します.


図1
設計時画面
図2
実行画面
  • 現在使用可能なプリンタ数を表示

リスト1
EnumPrinters 関数を使用して,使用可能なプリンタ全ての数と,共有プリンタの数を取得
unit PrintersCountUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ExtCtrls;

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

{$R *.dfm}

//=============================================================================
//  使用可能なプリンタ数を求める
//
//  TPrinterのPrinters.Countを使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Cnt : Integer;
begin
  Cnt := Printer.Printers.Count;
  MessageBox(Handle, PChar(IntToStr(Cnt)), '情報', MB_ICONINFORMATION);
end;

//=============================================================================
//  使用可能なプリンタ数を求める
//
//  EnumPrinters APIを使用
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Flags    : Cardinal;
  dwNeeded : Cardinal;
  dwReturn : Cardinal;
  pPinfo4  : PPrinterInfo4;
begin
  dwNeeded := 0;
  dwReturn := 0;

  //プリンタ情報を得る準備
  Flags := PRINTER_ENUM_CONNECTIONS or PRINTER_ENUM_LOCAL;

  dwNeeded := 0;
  //バッファ長を得る
  EnumPrinters(Flags, nil, 4, nil, 0, dwNeeded, dwReturn);
  if dwNeeded <> 0 then begin
    //構造体の配列に必要なバイト数を確保
    GetMem(pPinfo4, dwNeeded);
    try
      //プリンタ情報(Level=4)を取得
      EnumPrinters(Flags,
                   nil,
                   4,
                   Pointer(pPinfo4),
                   dwNeeded, 
                   dwNeeded, 
                   dwReturn);
    finally
      FreeMem(pPinfo4);
    end;
  end;
  MessageBox(Handle, PChar(IntToStr(dwReturn)), '情報', MB_ICONINFORMATION);
end;

//=============================================================================
//  使用可能なプリンタ数の共有プリンタ数求める
//
//  EnumPrinters APIを使用
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  Flags    : Cardinal;
  dwNeeded : Cardinal;
  dwReturn : Cardinal;
  pPinfo4  : PPrinterInfo4;
begin
  dwNeeded := 0;
  dwReturn := 0;

  //プリンタ情報を得る準備
  Flags := PRINTER_ENUM_LOCAL or PRINTER_ENUM_SHARED;

  dwNeeded := 0;
  //バッファ長を得る
  EnumPrinters(Flags, nil, 4, nil, 0, dwNeeded, dwReturn);
  if dwNeeded <> 0 then begin
    //構造体の配列に必要なバイト数を確保
    GetMem(pPinfo4, dwNeeded);
    try
      //プリンタ情報(Level=4)を取得
      EnumPrinters(Flags,
                   nil,
                   4,
                   Pointer(pPinfo4),
                   dwNeeded, 
                   dwNeeded, 
                   dwReturn);
    finally
      FreeMem(pPinfo4);
    end;
  end;
  MessageBox(Handle, PChar(IntToStr(dwReturn)), '情報', MB_ICONINFORMATION);
end;

end.




02_WinSpool.OpenPriner 関数


指定されたプリンタまたはプリントサーバーを識別するハンドルを取得する関数です.WinSpool.pas で定義されています.ここで取得したプリンタのハンドルを SetPrinter や GetPrinter で使用します.関数が成功すると True を返します.このハンドルは使用が終了したら,ClosePrinter で閉じなけれなりません.


表3
OpenPrinter 関数の引数

pPrinterName LPTSTR プリンタ名またはプリントサーバー名
phPrinter LPHANDL 関数が成功すると,この変数にプリンタオブジェクトまたはプリントサーバーオブジェクトを表すハンドルを返す
pDefault LPPRINTER_
DEFAULTS
LPPRINTER_DEFAULTS 構造体
以下のメンバーがある
DesiredAccess, pDatatype, pDevMode
nil でもよい


表4
LPPRINTER_DEFAULTS 構造体の DesiredAccess メンバーで指定可能な値

PRINTER_ACCESS_ADMINISTER 管理作業等,アクセス権などの指定の場合に指定
PRINTER_ACCESS_USE 基本的な印刷作業を行う場合に指定
PRINTER_ALL_ACCESS SYNCHRONIZE を除き,すべての管理作業と基本的な印刷作業を行う場合に指定
WRITE_DAC 特定の制御アクセス権を認める場合に指定




03_WinSpool.GetPrinter 関数


指定したプリンタの情報を取得します.同名の関数が Printers.pas にもありますが,本項目で説明する GetPrinter は WinSpool.pas で定義されているものです.この関数を使用する前に OpenPrinter でプリンタのハンドルを取得し,関数実行後,ClosePrinter でのハンドルを閉じる操作が必要です.
関数は,成功すると True を返します.


表5
GetPrinter 関数の引数

hPrinter HANDLE 調査対象のプリンタを識別するハンドルを指定する.この値はOpenPrinter 関数を使って取得する
Level DWORD バッファに格納する構造体の番号を指定する
PRINTER_INFO_1 の内容を取得したい場合は 1
PRINTER_INFO_2 の内容を取得したい場合は 2
...
指定可能な値は 1, 2, 3, 4, 5, 7 のいずれか
pPrinter LPBYTE プリンタの情報を受取るプリンタ情報構造体のバッファ
格納内容は Level の値によって異なる
cbBuf DWORD バッファのサイズをバイト単位で指定する
pcbNeeded LPDWORD プリンタ情報のサイズ (バイト単位) を受け取る変数へのポインタを指定する.0 を渡すと GetPrinter は失敗するが,必要なバッファサイズをこの変数に返す.
GetPrinter が成功すると,バッファ内に格納されたバイト数をこの変数に返す.




04_WinSpool.SetPrinter 関数


指定されたプリンタのデータ設定または状態の変更(印刷の一時停止,印刷の再開,全ジョブの削除)を実行します.
同名の関数が Printers.pas にもありますが,本項目の SetPrinter は WinSpool.pas で定義されているものです.この関数を使用する前に OpenPrinter でプリンタのハンドルを取得し,関数実行後,ClosePrinter  でのハンドルを閉じる操作が必要です. 関数は,成功するとTrue を返します.


表6
SetPrinter 関数の引数

hPrinter HANDLE 操作対象のプリンタを識別するハンドルを指定する.この値はOpenPrinter 関数を使って取得する
Level DWORD pPrinter が受け取るプリンタ情報構造体の番号を指定する.
PRINTER_INFO_1 の内容を設定する場合は 1
PRINTER_INFO_2 の内容を設定する場合は 2
...
指定可能な値は 1, 2, 3, 4, 5, 7 のいずれか

Command に 0 以外の値を指定するときは 必ず 0 とする
pPrinter LPBYTE プリンタに設定するデータ,または Command に指定するコマンドの情報を格納するバッファ.
バッファ内のデータの種類は Level の値によって決まる.
Command DWORD 実行するアクションを表す値を指定する.
Level に 0 以外の値を指定したときは必ず 0 とする.
その場合プリンタの状態はそのままで,Level と pPrinter の指定に従ってプリンタデータが再設定される.ただし,ユーザの権限によっては設定できない内容もある.


表7
SetPrinter 関数の Command パラメータに指定可能な値

PRINTER_CONTROL_PAUSE プリンタを一時停止する
PRINTER_CONTROL_PURGE プリンタのすべての印刷ジョブを削除する
PRINTER_CONTROL_RESUME 一時停止しているプリンタを再開する
PRINTER_CONTROL_SET_STATUS pPrinter で指定した値でプリンタの状態を設定する


SetPrinter 関数の使用例です.
プリンタ名のリストから,選択したプリンタを一時停止,一時停止解除を行います.


図3
設計時画面
図4
一時停止を実行
図5
プリンタの状態が一時停止となる
図6
一時停止解除

リスト2
WinSpool.SetPrinter 関数を使用して,プリンタの一時停止,一時停止解除
unit StopRealPrintUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ValEdit, ExtCtrls, Grids;

type
  TForm1 = class(TForm)
    Panel2: TPanel;
    ListBox1: TListBox;
    Panel3: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタのリストを作成
//
//  ここでは簡単にしてTPrinterオブジェクト(クラス)を使用
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  ListBox1.Items.Clear;
  ListBox1.Items.Assign(Printer.Printers);

  ListBox1.ItemIndex := 0;
end;

//=============================================================================
//  Listboxで選択したプリンタを一時停止にする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  hPrinter    : Cardinal;
  Pdef        : PRINTER_DEFAULTS;
  PrinterName : String;
  ACommand    : DWORD;
begin
  if ListBox1.ItemIndex<0 then exit;

  //対象のプリンタ名
  PrinterName := ListBox1.Items[ListBox1.ItemIndex];

  //WindowsNT系の場合アクセス権が必要
  ZeroMemory(@Pdef, Sizeof(PRINTER_DEFAULTS));
  Pdef.DesiredAccess := PRINTER_ALL_ACCESS;

  //アクセス権を指定して対象のプリンタのハンドルを取得 
  WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, @Pdef);
  if hPrinter <> 0 then begin
    try
      ACommand := PRINTER_CONTROL_PAUSE;
      //Commandを使用するのでLevelは0とする
      if WinSpool.SetPrinter(hPrinter, 0, nil, ACommand) then begin
        MessageBox(Handle, '一時停止しました', '情報', MB_ICONINFORMATION);
      end;
    finally
      WinSpool.ClosePrinter(hPrinter);
    end;
  end;
end;

//=============================================================================
//  Listboxで選択したプリンタを一時停止を解除[印刷の再開]にする
//  プリンタの状態が[準備完了]となる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  hPrinter    : Cardinal;
  Pdef        : PRINTER_DEFAULTS;
  PrinterName : String;
  ACommand    : DWORD;
begin
  if ListBox1.ItemIndex<0 then exit;

  //対象のプリンタ名
  PrinterName := ListBox1.Items[ListBox1.ItemIndex];

  //WindowsNT系の場合アクセス権が必要
  ZeroMemory(@Pdef, Sizeof(PRINTER_DEFAULTS));
  Pdef.DesiredAccess := PRINTER_ALL_ACCESS;

  //アクセス権を指定して対象のプリンタのハンドルを取得 
  WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, @Pdef);
  if hPrinter <> 0 then begin
    try
      ACommand := PRINTER_CONTROL_RESUME;
      //Commandを使用するのでLevelは0とする
      if WinSpool.SetPrinter(hPrinter, 0, nil, ACommand) then begin
        MessageBox(Handle, '一時停止を解除しました', '情報', MB_ICONINFORMATION);
      end;
    finally
      WinSpool.ClosePrinter(hPrinter);
    end;
  end;
end;

//=============================================================================
//  Listboxで選択したプリンタをの状態を取得
//  ここでは一時停止だけを検査
//
//  GetPrinterの説明にはPRINTER_INFO_6は指定できないようになっているであるが,
//  では,PRINTER_INFO_6の情報取得はどうするのか,ということでテスト
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  hPrinter    : Cardinal;
  PrinterName : String;
  dwNeeded    : DWORD;
  InfoLevel   : Cardinal;
  pPinfo6     : PPrinterInfo6;
begin
  if ListBox1.ItemIndex<0 then exit;

  InfoLevel := 6;
  pPinfo6   := nil;
  //対象のプリンタ名
  PrinterName := ListBox1.Items[ListBox1.ItemIndex];
  //対象のプリンタのハンドルを取得 
  WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil);

  if hPrinter <> 0 then begin
    try
      //構造体に必要なバッファサイズを取得
      WinSpool.GetPrinter(hPrinter, InfoLevel, nil, 0, @dwNeeded);

      //構造体の配列に必要なバイト数を確保
      GetMem(pPinfo6, dwNeeded);

      //PRINTER_INFO_6構造体の情報を取得して表示
      //時刻など表示形式は指定していないので注意
      if WinSpool.GetPrinter(hPrinter,
                             InfoLevel,
                             pPinfo6,
                             dwNeeded,
                             @dwNeeded) then begin
        if (pPinfo6.dwStatus and
            PRINTER_STATUS_PAUSED) = PRINTER_STATUS_PAUSED then begin
            MessageBox(Handle, '一時停止中です', '情報', MB_ICONINFORMATION);
        end else begin
          MessageBox(Handle, '一時停止中ではありません', '情報', MB_ICONINFORMATION);
        end;
      end;
    finally
      FreeMem(pPinfo6);
      WinSpool.ClosePrinter(hPrinter);
    end;
  end;
end;

end.




05_PRINTER_INFO_1 構造体


プリンタの汎用的な情報を格納する構造体です.


表8
PRINTER_INFO_1 構造体のメンバー

Flags DWORD 取得データに関する情報の値
詳細は MSDN ライブラリの記事参照
pDescription LPTSTR 構造体の内容説明の内容を表す文字列
pName LPTSTR 構造体の名前.プリンタ名
pComment LPTSTR 追加情報


PRINTER_INFO_1 構造体のテストプログラムです.
指定したプロバイダのプリンタ名を列挙します.


図7
設計時画面
図8
実行画面
  • 上で取得したプリンタプロバイダを指定してプリンタ名を列挙したのが下

リスト3
PRINTER_INFO_1 構造体のメンバから指定プロバイダのプリンタを列挙
unit Printer_Info1_EnumUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタの情報取得 PRINTER_INFO_1構造体から
//
//  第2引数をnilにして使用可能なプリントプロバイダを取得する場合
//  ここでは構造体のpNameしか取得していないが,他の値も同様に取得できる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Flags    : Cardinal;
  dwNeeded : Cardinal;
  dwReturn : Cardinal;
  pPinfo1  : PPrinterInfo1;
  pWork    : PPrinterInfo1;
  i        : Integer;
  S        : String;
begin
  Memo1.Lines.Clear;

  pPInfo1  := nil;
  dwNeeded := 0;
  dwReturn := 0;
  Flags    := PRINTER_ENUM_NAME;

  //dwNeededを0にして必要なバッファサイズを取得
  //第2引数をnilにして使用可能なプリントプロバイダを取得する場合
  EnumPrinters(Flags,
               nil,
               1,
               pPInfo1,
               dwNeeded,
               dwNeeded,
               dwReturn);
  if dwNeeded=0 then exit;

  //構造体の配列に必要なバイト数を確保
  GetMem(pPinfo1, dwNeeded);
  try
    if EnumPrinters(Flags,
                    nil,
                    1,
                    pPinfo1,
                    dwNeeded,
                    dwNeeded,
                    dwReturn) then begin
      pWork := pPinfo1;
      for i := 0 to dwReturn - 1 do begin
        S := String(pWork.pName);
        Memo1.Lines.Add(S);
        //次の構造体の配列に移動
        Inc(pWork);
      end;
    end else begin
      MessageBox(Handle, PChar(SysErrorMessage(GetLastError)), '', 0);
    end;
  finally
    FreeMem(pPinfo1);
  end;
end;

//=============================================================================
//  プリンタの情報取得 PRINTER_INFO_1構造体から
//
//  プリントプロバイダを指定して,そのプリンタのリストを取得
//  ここでは構造体のpNameしか取得していないが,他の値も同様に取得できる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Flags     : Cardinal;
  pName     : PWideChar;
  dwNeeded  : Cardinal;
  dwReturn  : Cardinal;
  pPinfo1   : PPrinterInfo1;
  pWork     : PPrinterInfo1;
  i         : Integer;
  S         : String;
begin
  Memo2.Lines.Clear;

  pPInfo1  := nil;
  dwNeeded := 0;
  dwReturn := 0;
  pName    := 'Windows NT Local Print Providor';
  Flags    := PRINTER_ENUM_NAME;

  //dwNeededを0にして必要なバッファサイズを取得
  EnumPrinters(Flags,
               pName,
               1,
               pPInfo1,
               dwNeeded,
               dwNeeded,
               dwReturn);
  if dwNeeded=0 then exit;

  //構造体の配列に必要なバイト数を確保
  GetMem(pPinfo1, dwNeeded);
  try
    if EnumPrinters(Flags,
                    pName,
                    1,
                    pPinfo1,
                    dwNeeded,
                    dwNeeded,
                    dwReturn) then begin
      pWork := pPinfo1;
      for i := 0 to dwReturn - 1 do begin
        S := string(pWork.pName);
        Memo2.Lines.Add(S);
        //次の構造体の配列に移動
        Inc(pWork);
      end;
    end else begin
      MessageBox(Handle, PChar(SysErrorMessage(GetLastError)), '', 0);
    end;
  finally
    FreeMem(pPinfo1);
  end;
end;

end.


PRINTER_INFO_1 構造体のメンバーの値を表示するサンプルです.
プリンタのリストは,ListBox1.Items.Assign (Printer.Printers) でも取得可能です.


図9
設計時画面
図10
実行画面
  • 上のリストボックスで選択したプリンの情報を表示

リスト4
PRINTER_INFO_1 構造体のメンバーの値を取得する
unit Printer_Info1_GetPrinterUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ValEdit, ExtCtrls, Grids;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    ListBox1: TListBox;
    Panel3: TPanel;
    ValueListEditor1: TValueListEditor;
    procedure Button1Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタのリストを作成
//
//  ここでは簡単にしてTPrinterオブジェクト(クラス)を使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  PrinterName : String;
  i           : Integer;
begin
  ListBox1.Items.Clear;
  for i := 0 to Printer.Printers.Count - 1 do begin
    PrinterName := Printer.Printers[i];
    ListBox1.Items.Add(PrinterName)
  end;
end;

//=============================================================================
//  ListBox1をクリックしたら,そのプリンタの情報を表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  hPrinter    : Cardinal;
  PrinterName : String;
  dwNeeded    : DWORD;
  pPinfo1     : PPrinterInfo1;
begin
  pPinfo1 := nil;

  //対象のプリンタ名
  PrinterName := ListBox1.Items[ListBox1.ItemIndex];
  //対象のプリンタのハンドルを取得 
  WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil);
  if hPrinter <> 0 then begin
    try
      //構造体に必要なバッファサイズを取得
      WinSpool.GetPrinterW(hPrinter, 1, nil, 0, @dwNeeded);

      //構造体の配列に必要なバイト数を確保
      GetMem(pPinfo1, dwNeeded);

      //PRINTER_INFO_1構造体の各メンバーの情報を取得して表示
      if WinSpool.GetPrinter(hPrinter, 1, pPinfo1, dwNeeded, @dwNeeded) then begin
        ValueListEditor1.Cells[1, 1] := pPInfo1.pDescription;
        ValueListEditor1.Cells[1, 2] := pPInfo1.pName;
        ValueListEditor1.Cells[1, 3] := pPInfo1.pComment;
      end;
    finally
      FreeMem(pPinfo1);
      WinSpool.ClosePrinter(hPrinter);
    end;
  end;
end;

end.




06_PRINTER_INFO_2 構造体


プリンタの詳細情報を格納する構造体です.


表9
PRINTER_INFO_2 構造体のメンバー

pServerName LPTSTR サーバ名
nil の場合はローカルプリンタ
pPrinterName LPTSTR プリンタ名
pShareName LPTSTR 共有名
pPortName LPTSTR ポート名
複数ある場合はカンマ区切りの文字列
pDriverName LPTSTR プリンタドライバ名
pComment LPTSTR プリンタの簡単な説明
pLocation LPTSTR プリンタの物理的な場所
pDevMode LPDEVMODE 用紙の向きや解像度等,プリンタのデフォルトのデータを定義する DEVMODE 構造体
pSepFile LPTSTR 区切りページを作成する場合,そのファイルが使用される名前.このページは別々の印刷ジョブをプリンタに送信するために使用される
pPrintProcessor LPTSTR プリントプロセッサ名
pDatatype LPTSTR 印刷ジョブのデータを記録するために使用するタイプの指定
pParameters LPTSTR デフォルトのプリントプロセッサのパラメータ
pSecurityDescriptor - SECURITY_DESCRIPTOR 構造体
Attributes DWORD プリンタの属性を表す値の組合せ
詳細は MSDN ライブラリの記事参照
DefaultPriority DWORD 印刷ジョブのプライオリティ
StartTime DWORD プリンタ印刷ジョブ開始時刻(グリニッジ標準時間)
UntilTime DWORD プリンタ印刷ジョブ終了時刻(グリニッジ標準時間)
Status DWORD プリンタの状態を表す値の組合せ
PINTER_INFO_6 構造体では,単独でこの値の取得可能
cJobs DWORD 印刷ジョブのキュー番号
AveragePPM DWORD 1 分あたりの平均印刷ページ数


プリンタ名のリストから,選択したプリンタの PRINTER_INFO_2 構造体のメンバーの値を表示するサンプルです.


図11
設計時画面
図12
実行画面
  • 上のリストボックスで選択したプリンの情報を表示

リスト5
PRINTER_INFO_2 構造体のメンバの値を取得
unit Printer_Info2_EnumUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ValEdit, ExtCtrls, Grids;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    ListBox1: TListBox;
    Panel3: TPanel;
    ValueListEditor1: TValueListEditor;
    procedure Button1Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタの情報取得 PRINTER_INFO_2構造体から
//
//  第2引数をnilにして使用可能なプリントプロバイダを取得する場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Flags       : Cardinal;
  dwNeeded    : Cardinal;
  dwReturn    : Cardinal;
  pPinfo2     : PPrinterInfo2;
  pWork       : PPrinterInfo2;
  i           : Integer;
  S           : String;
begin
  ListBox1.Items.Clear;

  pPInfo2  := nil;
  dwNeeded := 0;
  dwReturn := 0;
  Flags := PRINTER_ENUM_NAME;

  //dwNeededを0にして必要なバッファサイズを取得
  EnumPrinters(Flags,
               nil,
               2,
               pPInfo2,
               dwNeeded,
               dwNeeded,
               dwReturn);
  if dwNeeded = 0 then exit;

  //構造体の配列に必要なバイト数を確保
  GetMem(pPinfo2, dwNeeded);
  try
    if EnumPrinters(Flags,
                    nil,
                    2,
                    pPinfo2,
                    dwNeeded,
                    dwNeeded,
                    dwReturn) then begin
      pWork := pPinfo2;
      for i := 0 to dwReturn - 1 do begin
        S := String(pWork.pPrinterName);
        ListBox1.Items.Add(S);
        //次の構造体の配列に移動
        Inc(pWork);
      end;
    end else begin
      MessageBox(Handle, PChar(SysErrorMessage(GetLastError)), '', 0);
    end;
  finally
    FreeMem(pPinfo2);
  end;
end;

//=============================================================================
//  ListBox1をクリックしたら,そのプリンタの情報を表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  hPrinter    : Cardinal;
  PrinterName : WideString;
  dwNeeded    : DWORD;
  pPinfo2     : PPrinterInfo2;
begin
  pPinfo2 := nil;

  //対象のプリンタ名
  PrinterName := ListBox1.Items[ListBox1.ItemIndex];
  //対象のプリンタのハンドルを取得 
  WinSpool.OpenPrinter(PChar(PrinterName), hPrinter, nil);
  if hPrinter <> 0 then begin
    try
      //構造体に必要なバッファサイズを取得
      WinSpool.GetPrinter(hPrinter, 2, nil, 0, @dwNeeded);

      //構造体の配列に必要なバイト数を確保
      GetMem(pPinfo2,dwNeeded);

      //PRINTER_INFO_2構造体の各メンバーの情報を取得して表示
      //時刻など表示形式は指定していないので注意
      if WinSpool.GetPrinterW(hPrinter, 2, pPinfo2, dwNeeded, @dwNeeded) then begin
        ValueListEditor1.Cells[1,1]  := pPInfo2.pServerName;
        ValueListEditor1.Cells[1,2]  := pPInfo2.pPrinterName;
        ValueListEditor1.Cells[1,3]  := pPInfo2.pShareName;
        ValueListEditor1.Cells[1,4]  := pPInfo2.pPortName;
        ValueListEditor1.Cells[1,5]  := pPInfo2.pDriverName;
        ValueListEditor1.Cells[1,6]  := pPInfo2.pComment;
        ValueListEditor1.Cells[1,7]  := pPInfo2.pLocation;
        ValueListEditor1.Cells[1,8]  := pPInfo2.pSepFile;
        ValueListEditor1.Cells[1,9]  := pPInfo2.pPrintProcessor;
        ValueListEditor1.Cells[1,10] := pPInfo2.pParameters;
        ValueListEditor1.Cells[1,11] := IntTostr(pPInfo2.DefaultPriority);
        ValueListEditor1.Cells[1,12] := IntToStr(pPInfo2.StartTime);
        ValueListEditor1.Cells[1,13] := IntToStr(pPInfo2.UntilTime);
        ValueListEditor1.Cells[1,14] := IntToStr(pPInfo2.cJobs);
        ValueListEditor1.Cells[1,15] := IntToStr(pPInfo2.AveragePPM);
      end;
    finally
      FreeMem(pPinfo2);
      WinSpool.ClosePrinter(hPrinter);
    end;
  end;
end;

end.




07_PRINTER_INFO_3 構造体


プリンタのセキュリティ情報を格納する構造です.Windows NT 系のみで使用可能です.


表10
PRINTER_INFO_3 構造体のメンバー

pSecurityDescriptor PSECURITY_DESCRIPTOR SECURITY_DESCRIPTOR構造体




08_PRINTER_INFO_4 構造体


プリンタ名,サーバー名,リモート/ローカルのどちらかなど,最小限のプリンタ情報を格納する構造体です.Windows NT 系のみで使用可能です.


表11
PRINTER_INFO_4構造体のメンバー

pPrinterName DWORD プリンタ名 (ローカルまたはリモート)
pServerName LPTSTR サーバ名
Attributes DWORD 取得するプリンタオブジェクトの種類で,ローカルかリモートかを指定する
値については MSDN ライブラリの記事を参照
PRINTER_INFO_2 の値も検出できる.


EnumPrinters  関数の引数に,PRINTER_INFO_4 を指定して,プリンタ名を列挙するサンプルです.
Attributes 属性が PRINTER_ATTRIBUTE_HIDDEN でないものだけを列挙します.


図13
設計時画面
図14
実行画面

リスト6
PRINTER_INFO_4 構造体を使用して,プリンタ名を列挙
unit Printer_Info4_EnumUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタの情報取得 PRINTER_INFO_4構造体から
//
//  ローカルのプリンタと現在接続が確立されているもの
//  この場合ネットワークのものも含む
//  PRINTER_INFO_4の時に指定可能なのはこの2つのみ
//
//  中村氏がDelphi MLで見えないプリンタがあると言うので見えないプリンタを表示し
//  ないようにしている.この見えないプリンタは,確認したことがあり,なるほどと
//  その時は思ったのであるが,状況については失念
//  使用可能なプリンタがリストできればいいと考えていたので...
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Flags     : Cardinal;
  dwNeeded  : Cardinal;
  dwReturn  : Cardinal;
  pPinfo4   : PPrinterInfo4;
  pWork     : PPrinterInfo4;
  i         : Integer;
  S         : String;
begin
  Memo1.Lines.Clear;

  pPInfo4  := nil;
  dwNeeded := 0;
  dwReturn := 0;

  //列挙対象プリンタの種類
  Flags := PRINTER_ENUM_LOCAL or PRINTER_ENUM_CONNECTIONS;

  //dwNeededを0にして必要なバッファサイズを取得
  EnumPrinters(Flags,
               nil,
               4,
               pPInfo4,
               dwNeeded,
               dwNeeded,
               dwReturn);
  if dwNeeded=0 then exit;

  //構造体の配列に必要なバイト数を確保
  GetMem(pPinfo4, dwNeeded);
  try
    if EnumPrinters(Flags,
                    nil,
                    4,
                    pPinfo4,
                    dwNeeded,
                    dwNeeded,
                    dwReturn) then begin
      pWork := pPinfo4;
      for i := 0 to dwReturn - 1 do begin
        if (pWork.Attributes and PRINTER_ATTRIBUTE_HIDDEN) = 0 then begin
          S := string(pWork.pPrinterName);
          Memo1.Lines.Add(S);
        end;
        //次の構造体の配列に移動
        Inc(pWork);
      end;
    end else begin
      MessageBox(Handle, PChar(SysErrorMessage(GetLastError)), '', 0);
    end;
  finally
    FreeMem(pPinfo4);
  end;
end;

end.




09_PRINTER_INFO_5 構造体


プリンタ属性,タイムアウトの設定などのプリンタ情報を格納する構造体です.


表12
PRINTER_INFO_5 構造体のメンバー

pPrinterName LPTSTR プリンタ名
pPortName LPTSTR ポート名
複数ある場合はカンマ区切り
Attributes DWORD プリンタの属性の組合せ.PINTER_INFO_2 構造体の Attibutes でも同じ値が取得できる
値については MSDN ライブラリの記事を参照
DeviceNotSelectedTimeou DWORD Windows NT 系では未使用
TransmissionRetryTimeout DWORD Windows NT 系では未使用


上のサンプルと同じく,EnumPrinters 関数の引数に,PRINTER_INFO_5 を指定して,プリンタ名を列挙するサンプルです.


図15
設計時画面
図16
実行画面

リスト7
PRINTER_INFO_5 構造体を使用してプリンタ名を列挙
unit Printer_Info5_EnumUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Printers, WinSpool, ExtCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  プリンタの情報取得 PRINTER_INFO_5構造体から
//
//  PRINTER_INFO_4はWindows 9X系では使用できないのでこちらを使用することになる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Flags     : Cardinal;
  dwNeeded  : Cardinal;
  dwReturn  : Cardinal;
  pPinfo5   : PPrinterInfo5;
  pWork     : PPrinterInfo5;
  i         : Integer;
  S         : String;
begin
  Memo1.Lines.Clear;

  pPInfo5  := nil;
  dwNeeded := 0;
  dwReturn := 0;

  //列挙対象プリンタの種類
  Flags := PRINTER_ENUM_LOCAL or PRINTER_ENUM_CONNECTIONS;

  //dwNeededを0にして必要なバッファサイズを取得
  EnumPrinters(Flags,
               nil,
               5,
               pPInfo5,
               dwNeeded,
               dwNeeded,
               dwReturn);
  if dwNeeded = 0 then exit;

  //構造体の配列に必要なバイト数を確保
  GetMem(pPinfo5, dwNeeded);
  try
    if EnumPrinters(Flags,
                    nil,
                    5,
                    pPinfo5,
                    dwNeeded,
                    dwNeeded,
                    dwReturn) then begin
      pWork := pPinfo5;
      for i := 0 to dwReturn - 1 do begin
        S := string(pWork.pPrinterName);
        Memo1.Lines.Add(S);
        //次の構造体の配列に移動
        Inc(pWork);
      end;
    end else begin
      MessageBox(Handle, PChar(SysErrorMessage(GetLastError)), '', 0);
    end;
  finally
    FreeMem(pPinfo5);
  end;
end;

end.




10_PRINTER_INFO_6 構造体


プリンタの状態を格納する構造体です.
このプリンタの状態は,PRINTER_INFO_2 構造体の Status パラメータの値からも取得できます.


表13
PRINTER_INFO_6 構造体のメンバー

dwStatus DWORD 値については MSDN ライブラリの記事を参照


表14
プリンタの状態を表す dwStatus の値

PRINTER_STATUS_BUSY プリンタはヒジー
PRINTER_STATUS_DOOR_OPEN プリンタのカバーが開いている
PRINTER_STATUS_ERROR 未使用
PRINTER_STATUS_INITIALIZING プリンタの初期化中
PRINTER_STATUS_IO_ACTIVE このプリンタはアクティブな入力出力状態
PRINTER_STATUS_MANUAL_FEED 手動フィードの状態
PRINTER_STATUS_NO_TONER トナーがない
PRINTER_STATUS_NOT_AVAILABLE 印刷ができない状態
PRINTER_STATUS_OFFLINE オフライン
PRINTER_STATUS_OUT_OF_MEMORY プリンタのメモリ不足
PRINTER_STATUS_OUTPUT_BIN_FULL ビンが用紙がある
PRINTER_STATUS_PAGE_PUNT 現在のページを印刷できない
PRINTER_STATUS_PAPER_JAM 紙詰まり
PRINTER_STATUS_PAPER_OUT 用紙切れ
PRINTER_STATUS_PAPER_PROBLEM 用紙に問題がある
PRINTER_STATUS_PAUSED 一時停止中
PRINTER_STATUS_PENDING_DELETION 削除の保留中
PRINTER_STATUS_POWER_SAVE 省電力モード
PRINTER_STATUS_PRINTING 印刷中
PRINTER_STATUS_PROCESSING SetPrinter 関数からのコマンドを処理中
PRINTER_STATUS_SERVER_UNKNOWN 状態不明
PRINTER_STATUS_TONER_LOW トナーが不足
PRINTER_STATUS_USER_INTERVENTION ユーザ要求に対するエラー
PRINTER_STATUS_WAITING 印刷待ち状態
PRINTER_STATUS_WARMING_UP ウォーミングアップ中




11_プリンタの状態の取得


PRINTER_INFO_2 構造体のメンバーの Status は,プリンタの状態を表す値の組合せが取得できることになっていますが,本ページのサンプルコードではプリンタの状態の取得はできません.

プリンタ装置とのインターフェイスにポートモニタというがあります.ポートモニタは,プリンタ装置とのデータ転送を担当します.印刷ジョブがポートモニタを経由して,プリンタ装置に送信された時にエラーが発生すると,ポートモニタは印刷ジョブの状態情報として記録します.それを印刷スプーラがプリントキューに報告することにより,Status メンバーの値をセットします.

つまり,プリントキューに印刷ジョブがない状態の時は,常に印刷ジョブの受け入れが可能という結果となります.

サンプルプログラム集の [722_PDF 文書の印刷] では,印刷ジョブを開始してから現在のジョブ数を取得を取得しています.その PDF のサンプルのように,プリンタの状態の取得は,印刷ジョブを開始してから取得することになります.この時,EnumJobs 関数あるいは,GetJob 関数からの情報も必要となるかも知れません.
詳しくは,以下の Microsoft サポートの記事を参考にしてください.