Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2004年02月14日] [更新 2012年04月09日] Delphi サンプルプログラム集
   
016_プリンタ設定ダイアログの遠隔操作
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro
016_PrinterSetupDialog.zip [3,262 KB] 2012年04月09日版(EXE同梱)



  • 2009年10月13日
  • プリンタ設定コンポーネントの記事から移動
  • 2012年04月09日
  • サンプルコードと記事の修正・整備




プリンタの設定ダイアログを遠隔操作


プリンタの設定ダイアログの遠隔操作に関するサンプルコードです.

これらは,他のダイアログの遠隔操作の参考ともなるものです.ダイアログのハンドルとコントロールの ID 番号を使用して操作します.ただし,本ページのコードは,同一プロセス内で表示するダイアログでないと操作できない操作があります.
Windows API やフック関数,サブクラス化の説明はしていません.必要であれば,参考書や関係サイトなどで調べて下さい.

Delphi の [印刷] ダイアログには,オプションプロパティに多くの設定項目があります.以下のような遠隔操作が必要な場面はあまりないと思われますが,必要な場面もあるかも知れません.
ダイアログの設定などの操作は,[015_プリンタ設定関係ダイアログ API の使用方法] のように,ダイアログ関係の Windows API を使用しても可能です.






01_ダイアログのコントロールを無効または非表示


ダイアログ上のコントロールを無効化または非表示にする例です.ユーザに操作してもらいたくないボタンやコンボボックスを使用不可にする場合の参考です.


図1
設計時画面
  • 実行して Button1 をクリックする
図2
変更したダイアログ
  • プリンタ名コンボボックス使用不可
  • [プロパティ] ボタン使用不可
  • [キャンセル] ボタン使用不可
    (絶対設定しないとダメ?)
  • [ネットワーク] ボタン非表示

リスト1
ダイアログ上のコントロールの無効化や非表示
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    PrinterSetupDialog1: TPrinterSetupDialog;
    procedure Button1Click(Sender: TObject);
    procedure PrinterSetupDialog1Show(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
// プリンタの設定ダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  プリンタセットアップダイアログを表示したらハンドルを取得して各コントロール
//  の使用不可と非表示を設定.
//  ダイアログの閉じるボタン[×]はキャンセルと同じ機能なので無効となる.
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
  WndDialog : HWND;
  WndCotrol : HWND;
begin
  //ダイアログのハンドル
  WndDialog := Form1.PrintersetupDialog1.Handle;

  //[プロパティ]ボタンのハンドルを取得して無効化
  WndCotrol := GetDlgItem(WndDialog, 1025);
  EnableWindow(WndCotrol, False);

  //[キャンセル]ボタンのハンドルを取得して無効化
  WndCotrol := GetDlgItem(WndDialog, 2);
  EnableWindow(WndCotrol, False);

  //プリンタ名選択コンボボックスのハンドルを取得して無効化
  WndCotrol := GetDlgItem(WndDialog, 1136);
  EnableWindow(WndCotrol, False);

  //ネットワークボタンのハンドルを取得して非表示にする
  WndCotrol := GetDlgItem(WndDialog, 1037);
  ShowWindow(WndCotrol, SW_HIDE);
end;

end.




02_ダイアログのボタンクリックで自前のコードを実行


ダイアログのボタンに別の機能を持たせたい時の参考コードです.
次のコードでは,マウスの動作をフックすることによって実現しています.この様な処理で使用するフック関数は,基本的に戻り値が変更可能なものでないと実現できません.フック関数には,DLL 内に置かないと動作しないもの,同一スレッド内に置くものと,目的に応じてどちらでも構わないものがあります.

ダイアログ上のボタン操作のコード関係については,次の項目,[03_ダイアログのサイズを変更してボタンを追加] も参考にしてください.


図3
設計時画面
図4
実行画面
  • [プロパティ] ボタンをクリックすると全く別のダイアログが現れる
  • [キャンセル] ボタンはコードなし
    (クリックしても何も起こらない)

リスト2
ダイアログのボタンクリックの動作を,WH_MOUSE フック関数を使用して変更
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    PrinterSetupDialog1: TPrinterSetupDialog;
    procedure Button1Click(Sender: TObject);
    procedure PrinterSetupDialog1Show(Sender: TObject);
    procedure PrinterSetupDialog1Close(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

//フック関数のコールバック関数を,コード内の任意の場所で使用するために,
//interface部で定義しておく
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  MouseHook   : HHOOK = 0;
  WndProperty : HWND;
  WndCancel   : HWND;

//=============================================================================
//   プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  プリンタセットアップダイアログを表示したらハンドルを取得してWH_MOUSEフック
//  関数をインストール.また[プロパティ]ボタンのアクセレータキーを無効にするた
//  めにキャプションを変更
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
  WndDialog : HWND;
begin
  //ダイアログのハンドル
  WndDialog := Form1.PrintersetupDialog1.Handle;

  //[プロパティ]ボタンのハンドル取得
  WndProperty :=GetDlgItem(WndDialog,1025);

  //[キャンセル]ボタンのハンドル取得
  WndCancel := GetDlgItem(WndDialog,2);
  //[プロパティ]ボタンのキャプションを変更
  //アクセラレータキーの定義部分を削除
  SetWindowText(WndProperty, PChar('プロパティ'));

  //フック関数をダイアログのスレッドとしてインストール
  MouseHook := SetWindowsHookEx(WH_MOUSE,
                                @WatchProc,
                                hInstance,
                                GetWindowThreadProcessID(WndDialog, nil));
end;

//=============================================================================
//  PrinterSetupDialog1のOnCloseベント処理
//
//  ダイアログを閉じる時は,フック関数をアンインストール
//=============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
 if MouseHook <> 0 then begin
   UnhookWindowsHookEx(MouseHook);
   MouseHook := 0;
 end;
end;

//-----------------------------------------------------------------------------
//  WH_MOUSEフック関数
//  ここではボタンの左クリック時のみ処理
//  したがって,該当コントロールにフォーカスがある時の[Enter]では既定の動作となる
//  アプリケションで処理する場合はフック関数の戻値をResult=-1とする
//
//  MouseHook    フック関数のハンドル
//  WndProperty  プロパティボタンのハンドル
//  WndCancel    キャンセルボタンのハンドル
//  はグローバル変数
//-----------------------------------------------------------------------------
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  Msg : PMouseHookStruct;
begin
  Result := 0;
  if nCode < 0 then begin
    Result := CallNextHookEx(MouseHook, nCode, wParam, lParam);
    exit;
  end;

  Msg := PMouseHookStruct(lParam);

  //nCodeがHC_ACTIONの時のみ操作可能
  if nCode = HC_ACTION then begin
    //ハンドルが[プロパティ]ボタンの時
    if Msg.hwnd = WndProperty then begin
      if (wParam = WM_LBUTTONUP) then begin
        Application.MessageBox(
              '[プロパティ]ボタンは使用できません.  ',
               PChar('プロパティの設定'),
               MB_ICONINFORMATION or MB_OK);
        Result := -1;
      end;
    end;
    //ハンドルが[キャンセル]ボタンの時は単に無効にするだけ
    if Msg.hwnd = WndCancel then begin
      if (wParam = WM_LBUTTONDOWN) then begin
        Result := -1;
      end;
    end;
  end;
  CallNextHookEx( MouseHook, nCode, wParam, lParam);
end;

end.


上のコードでは,マウスのフック関数を使用しましたが,以下の様に,ボタン専用のコールバック関数で処理する方法もあります.この場合,[プロパティ] ボタン専用なので便利な場合もあります.
なお,上のコードでは,WM_LBUTTONDOWN と WM_LBUTTONUP の両方を使用していますが,Windows の標準動作では,マウスのボタンを放した時,つまり WM_LBUTTONUP の時に動作します.


リスト3
ダイアログのボタンクリックの動作を,WindowProc のサブクラス化で変更
unit Unit2;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    PrinterSetupDialog1: TPrinterSetupDialog;
    procedure Button1Click(Sender: TObject);
    procedure PrinterSetupDialog1Show(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

//サブクラス関数の定義
function WatchProc(hWindow: HWND; uMsg: UINT; wParam: WPARAM;
 lParam: LPARAM): LRESULT; stdcall;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  OriginalProc : Pointer;

//=============================================================================
//  プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  プリンタセットアップダイアログを表示したらハンドルを取得してそのハンドルを
//  元に[プロパティ]ボタンのサブクラス関数を定義
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
  WndDialog   : HWND;
  WndProperty : HWND;
begin
  //ダイアログのハンドル
  WndDialog := PrintersetupDialog1.Handle;

  //[プロパティ]ボタンのハンドル取得
  WndProperty :=GetDlgItem(WndDialog, 1025);
  //[プロパティ]ボタンのキャプションを変更
  SetWindowText(WndProperty,PChar('プロパティ'));

  //ボタンのWindowProcをサブクラス化
  //元のWindowProcをOrignalProcに保存しWatchProcを新WindowProcに
  OriginalProc := Pointer(SetWindowLong(WndProperty,
                                        GWL_WNDPROC,
                                        NativeInt(@WatchProc)));
end;

//-----------------------------------------------------------------------------
//  [プロパティ]ボタンのサブクラス化関数
//  ここではボタンの左クリック時のみ処理
//-----------------------------------------------------------------------------
function WatchProc(hWindow: HWND; uMsg: UINT; wParam: WPARAM;
    lParam: LPARAM): LRESULT; stdcall;
begin
  case uMsg of
  WM_LBUTTONUP:
    begin
      ShowMessage('[プロパティ]ボタンをクリック');
      //メッセージを処理したら1を返す
      Result := 1;
      exit;
    end;
  end;

  //通常の処理を継続
  Result := CallWindowProc(OriginalProc, hWindow, uMsg, wParam, lParam);
end;

end.




03_ダイアログのサイズを変更してボタンを追加


ダイアログのサイズを大きくし,そこにボタンを追加します.
追加したボタンのサブクラスを作成して,その中でクリックを検出して処理します.ボタンクリックの処理は,ローカルフックでメッセージを捕らえる方法も考えられます.

Delphi のフォーム上にコントロールを配置して,自前でダイアログを作成する場合であれば,実行時にボタンを生成する際に,そのイベントにメソッドを割当てたり,そのボタンのクラスのイベントで処理することができます.
一方,このサンプルのように,Windows が提供しているダイアログ上に配置したボタンの場合,イベントを定義して使用することもできますが,Delphi で使用可能なイベント全てが使えるわけではありません.例えば OnClick には反応しません.


図5
設計時画面
図6
追加ボタンをクリック
  • ダイアログの高さを高くして
  • [新規ボタン] というボタンを追加

リスト4
ダイアログのサイズを変更して,ボタンを追加
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  StdCtrls, Dialogs, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    PrinterSetupDialog1: TPrinterSetupDialog;
    procedure Button1Click(Sender: TObject);
    procedure PrinterSetupDialog1Show(Sender: TObject);
    procedure PrinterSetupDialog1Close(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    MYButton     : TButton;
    OriginalProc : TWndMethod;
    procedure SubClassProc(var Message:TMessage);
  end;

var
  Form1: TForm1;

implementation

uses DebugWndUnit;

{$R *.DFM}

//=============================================================================
//  [新規ボタン]用のサブクラス関数
//=============================================================================
procedure TForm1.SubClassProc(var Message: TMessage);
var
  MousePos : TPoint;
  RectWnd  : TRect;
  KeyState : TKeyboardState;
begin
  OriginalProc(Message);

  if (Message.Msg = WM_LBUTTONDOWN) then begin
    Application.MessageBox('追加したボタンが押されました.  ',
                           PChar('新規設定'),
                           MB_ICONINFORMATION or MB_OK);
  end;
end;

//=============================================================================
// プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  プリンタセットアップダイアログを表示したらボタンを作成
//  追加したボタンにID番号も割り振る(何かのテストで,IDでアクセス可能なように)
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
  WndDialog    : HWND;
  WndNetBtn    : HWND;
  RectDialog   : TRect;
  DialogHeight : Integer;
  DialogWidth  : Integer;
  RectBtn      : TRect;
begin
  //ダイアログのハンドル取得
  WndDialog := Form1.PrintersetupDialog1.Handle;

  //ダイアログの位置とサイズを取得
  GetWindowRect(WndDialog, RectDialog);
  DialogHeight := RectDialog.Bottom - RectDialog.Top;
  DialogWidth  := RectDialog.Right  - RectDialog.Left;

  //ダイアログの高さ方向のサイズを変更
  SetWindowPos(WndDialog,
               0,
               0,
               0,
               DialogWidth,
               DialogHeight + 40,
               SWP_NOMOVE or SWP_SHOWWINDOW);

  //[ネットワーク]ボタンのハンドル取得
  WndNetBtn := GetDlgItem(WndDialog, 1037);
  //その位置とサイズを取得
  GetWindowRect(WndNetBtn, RectBtn);

  //ボタンを新規作成
  MyButton := TButton.Create(nil);

  //ボタンの動作はサブクラスで対応
  OriginalProc        := MyButton.WindowProc;
  MyButton.WindowProc := SubClassProc;

  //Parentの設定.Parentを指定しないと表示しない
  MyButton.ParentWindow := WndDialog;

  //作成したボタンの位置とサイズを設定
  //[ネットワーク]ボタンの少し下に位置
  //高さと幅は[ネットワーク]ボタンと同じにする
  RectBtn.TopLeft     := MyButton.ScreenToClient(RectBtn.TopLeft);
  RectBtn.BottomRight := MyButton.ScreenToClient(RectBtn.BottomRight);

  MyButton.Left    := RectBtn.Left;
  MyButton.Top     := RectBtn.Bottom + 5;
  MyButton.Width   := RectBtn.Right  - RectBtn.Left;
  MyButton.Height  := RectBtn.Bottom - RectBtn.Top;
  MyButton.Caption := '新規ボタン';

  //MyButtonのID値を9000にする
  SetWindowLong(MyButton.Handle, GWL_ID, 9000);
end;

//=============================================================================
//  PrinterSetupDialog1のOnCloseベント処理
//
//  [プリンタの設定]ダイアログを閉じる時は,作成したボタンを破棄
//=============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
  if Assigned(MYButton) then FreeAndNil(MYButton);
end;

end.


上のコードでは,WM_LBUTTONDOWN メッセージを処理していますが,実際のアプリケーションでボタンをクリックするという動作は,
  • マウスの左ボタン押して,放すとクリックとなる
  • マウスの左ボタンを押したまま,そのボタン以外の場所で放すと何もしない
  • ボタンにフォーカスがある時は,[Enter] キーでクリックと同じ動作となる
となっています.実際,Delphi の OnClick イベントもこのような動作となっています.
これを実装したのが次のコードです.単に [Enter] キーを検出して処理するだけですと,追加したボタンの [Enter] キーで表示されるダイアログを,[Enter] キーで閉じると,また [Enter] キーを押した時と同じ動作となってしまいます.[Enter] キーで操作する限り,プログラムが永遠に終了しなくなってしまいます.この対策もしています.

この他,アクセラレータキー (ニーモニック) を処理するには,ダイアログ本体側でメッセージを処理することになります.
TEdit 等を配置した場合は,Delphi の一般的な方法と同じです.TEdit の生成の前に,例えば TLabel を生成して,それにアクセラレータキーを設定します.そうすると,そのキー操作で TEdit にフォーカス移動します.これはタブ順が TLabel の次に TEdit になることを利用します.

このように, 本格的に実装するとなると結構面倒なことが分かります.このようなテストをしてみると,Delphi の VCL は,実にうまくできていることを実感させられます.


リスト5
ボタンクリック動作を,Windows の動作仕様と同じにするコード例

//=============================================================================
//  [新規ボタン]用のサブクラス関数
//
//  以下の対策をしたコード
//  (1) 左ボタンを押下したまま,ボタン以外の場所で放した時は動作しない
//  (2) [Enter]キーの押下も検出する
//  (3) MessageBoxの[OK]を[Enter]で終了した場合の,MessageBoxの再表示を防止
//=============================================================================
procedure TForm1.SubClassProc(var Message: TMessage);
var
  MousePos : TPoint;
  RectWnd  : TRect;
  KeyState : TKeyboardState;
begin
  OriginalProc(Message);
  if not Assigned(MYButton) then exit;

  GetCursorPos(MousePos);
  GetWindowRect(MYButton.Handle, RectWnd);
  GetKeyboardState(KeyState);

  if ((Message.Msg = WM_LBUTTONUP) and (PtInRect(RectWnd, MousePos))) or
     ((Message.WParam = VK_RETURN) and (KeyState[VK_RETURN] > 0)) then begin
    KeyState[VK_RETURN] := 1;
    SetKeyboardState(KeyState);
    Application.MessageBox('追加したボタンが押されました.  ',
                           PChar('新規設定'),
                           MB_ICONINFORMATION or MB_OK); 
  end;
end;




04_ダイアログの項目値を取得


ダイアログで設定した項目の値を取得するサンプルです.
ダイアログの OnClose イベント内で取得します.ここでは,プリンタ名のコンボボックスの値,用紙サイズのコンボボックスの値と,印刷の向きのラジオボタンの状態を取得して表示します.


図7
設計時画面
図8
ダイアログを閉じた後の画面
  • 設定内容がメモに表示されている
  • [Button1] はダイアログ表示用

リスト6
ダイアログで設定して値を取得するサンプルプログラム
unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnCloseベント処理
//  ダイアログを閉じる時に各コンボボックスの値を取得
//=============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
var
  WndDialog      : HWND;
  PrinterName    : array[0..127] of Char;
  PaperName      : array[0..127] of Char;
  BinName        : array[0..127] of Char;
  WndCheckOrient : HWND;
begin
  Memo1.Clear;

  //ダイアログのハンドルを取得
  WndDialog := PrintersetupDialog1.Handle;

  //プリンタ名の文字列を取得してTMemoに表示
  GetDlgItemText(WndDialog, 1136, PrinterName, SizeOf(PrinterName));
  Memo1.Lines.Add(String(PrinterName));

  //用紙サイズ名を取得してTMemoに表示
  GetDlgItemText(WndDialog, 1137, PaperName, SizeOf(PaperName));
  Memo1.Lines.Add(String(PaperName));

  //給紙方法の文字列を取得してTMemoに表示
  GetDlgItemText(WndDialog, 1138, BinName, SizeOf(BinName));
  Memo1.Lines.Add(String(BinName));

  //[印刷の向き]のハンドルを取得
  WndCheckOrient := GetDlgItem(WndDialog,1056);
  //印刷の向きのラジオボタンチェック状態を調査し表示
  if SendMessage(WndCheckOrient, BM_GETCHECK, 0, 0) = BST_CHECKED then begin
    Memo1.Lines.Add('印刷の向き 縦');
  end else begin
    Memo1.Lines.Add('印刷の向き 横');
  end;
end;

end.




05_ダイアログの項目値をリアルタイムで取得


こんどは,ダイアログが閉じた時ではなく,ダイアログで設定を変更すると,その変更した値をリアルタイムで取得するサンプルです.
フック関数の WH_CALLWNDPROCRET を,ローカルフックで使用して処理しています.


図9
設計時画面
図10
実行画面
  • 設定内容を変更すると...
  • 変更した設定内容を逐次表示する
  • [Button1] はダイアログ表示用

リスト7
ダイアログの設定内容をリアルタイムで取得するサンプル
unit Unit1;

interface

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

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

//フック関数のコールバック関数を,コード内の任意の場所で使用するために,
//interface部で定義しておく
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;

var
  Form1: TForm1;

implementation

{$R *.DFM}

var
  HookHandle : HHOOK = 0;
  WndDialog  : HWND;

//=============================================================================
//  プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
   PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  プリンタセットアップダイアログを表示したらハンドルを取得して
//  WH_CALLWNDPROCRETフック関数をインストール.
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
begin
  Memo1.Clear;

  //ダイアログのハンドルを取得
  WndDialog := PrintersetupDialog1.Handle;
  //フック関数をダイアログのスレットとしてインストール
  HookHandle := SetWindowsHookEx(WH_CALLWNDPROCRET,
                                 @WatchProc,
                                 hInstance,
                                 GetWindowThreadProcessID(WndDialog, nil));
end;

//=============================================================================
//  PrinterSetupDialog1のOnCloseベント処理
//
//  ダイアログを閉じる時は,フック関数をアンインストール
//=============================================================================
procedure TForm1.PrinterSetupDialog1Close(Sender: TObject);
begin
  if HookHandle<>0 then begin
    UnhookWindowsHookEx(HookHandle);
    HookHandle := 0;
  end;
end;

//-----------------------------------------------------------------------------
//  WH_CALLWNDPROCRETフック関数
//-----------------------------------------------------------------------------
function WatchProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM):
    LRESULT; stdcall;
var
  Msg         : PCWPRetStruct;
  PrinterName : array[0..127] of Char;
  PaperName   : array[0..127] of Char;
  BinName     : array[0..127] of Char;
begin
  Result :=0;

  if nCode < 0 then begin
    Result := CallNextHookEx(HookHandle, nCode, wParam, lParam);
    exit;
  end;

  Msg := PCWPRetStruct(lParam);

  //nCodeがHC_ACTIONの時のみ操作可能
  if nCode = HC_ACTION then begin

    //コンボボックスの選択状態が変化した時だけ処理
    //上位16bitに通知コードが入っている
    if HIWORD(Msg.wParam) = CBN_SELCHANGE then begin

      //下位16bitにコントロールのIDが入っている
      //プリンタ名を変更すると用紙サイズと給紙装置名も変わる可能性が
      //あるのでそれらも表示
      if LOWORD(Msg.wParam) = 1136 then begin
        GetDlgItemText(WndDialog, 1136, PrinterName, SizeOf(PrinterName));
        GetDlgItemText(WndDialog, 1137, PaperName,   SizeOf(PaperName));
        GetDlgItemText(WndDialog, 1138, BinName,     SizeOf(BinName));

        Form1.Memo1.Lines.Add((PrinterName));
        Form1.Memo1.Lines.Add((PaperName));
        Form1.Memo1.Lines.Add((BinName));

      //用紙サイズ
      end else if LOWORD(Msg.wParam) = 1137 then begin
        GetDlgItemText(WndDialog, 1137, PaperName, SizeOf(PaperName));
        Form1.Memo1.Lines.Add(String(PaperName));

      //給紙装置
      end else if LOWORD(Msg.wParam) = 1138 then begin
        GetDlgItemText(WndDialog, 1138, BinName, SizeOf(BinName));
        Form1.Memo1.Lines.Add(String(BinName));
      end;
    end;
  end;
  CallNextHookEx(HookHandle, nCode, wParam, lParam);
end;

end.




06_ダイアログの項目値をコードで選択 ( 項目値の削除 )


ダイアログの項目をコードで設定する例です.
ついでにコンボボックスのリストも制御して,'A5' または 'A4' という文字が含まれる用紙サイズだけをコンボボックスのリストにしてしまいます.


図11
設計時画面
図12
実行してダイアログ表示
  • A5 と A4 サイズしか選択できない

リスト8
ダイアログ上の項目をコードで設定
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, 
  StdCtrls, Dialogs, Buttons;

type
  TForm1 = class(TForm)
    Button1: TButton;
    PrinterSetupDialog1: TPrinterSetupDialog;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure PrinterSetupDialog1Show(Sender: TObject);
    procedure FormShow(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;


var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  選択するプリンタ名をセット
//  この名前を含むプリンタ名をコンボボックスから自動選択する(先頭一致検索)
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  Edit1.Text := 'EPSON VP';
end;

//=============================================================================
//  プリンタセットアップダイアログを表示
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  PrinterSetupDialog1.Execute;
end;

//=============================================================================
//  PrinterSetupDialog1のOnShowイベント処理
//
//  以下を自動設定
//  (1) ダイアログを表示したら指定プリンタ名をリストから選択
//  (2) 用紙サイズ名のA5またA4以外のサイズは,コンボボックスのリストから削除
//  (3) 印刷の向きは横とする
//=============================================================================
procedure TForm1.PrinterSetupDialog1Show(Sender: TObject);
var
  WndDialog           : HWND;
  WndPrinterNameCombo : HWND;
  WndPaperNameCombo   : HWND;
  WndOrientation      : HWND;
  StrSearch           : String;
  AIndex              : Integer;
  i                   : Integer;
  ACount              : Integer;
  ALen                : Integer;
  pStringText         : PChar;
begin
  //ダイアログのハンドルを取得
  WndDialog := PrinterSetupDialog1.Handle;

  //プリンタ名選択コンボボックスのハンドル取得
  WndPrinterNameCombo := GetDlgItem(WndDialog, 1136);
  //検索するプリンタ名(先頭一致検索)
  StrSearch := Trim(Edit1.Text);
  AIndex    := SendMessage(WndPrinterNameCombo,
                           CB_FINDSTRING,
                           -1,
                           LongInt(PChar(StrSearch)));
  //見つかったらそれを選択状態にする
  if AIndex >= 0 then begin
    SendMessage(WndPrinterNameCombo, CB_SETCURSEL, AIndex, 0);
  end;

  //用紙サイズ選択コンボボックスのハンドルを取得
  WndPaperNameCombo := GetDlgItem(WndDialog, 1137);
  //その項目数を取得
  ACount := SendMessage(WndPaperNameCombo, CB_GETCOUNT, 0, 0);

  //削除を伴う処理は降順で行う
  for i := ACount - 1 downto 0 do begin
    //メモリ確保のために文字列の長さを取得して,メモリ確保
    ALen := SendMessage(WndPaperNameCombo, CB_GETLBTEXTLEN, i, 0);
    pStringText := StrAlloc(ALen + 1);
    try
      //i番目の項目の文字列を取得
      SendMessage(WndPaperNameCombo, CB_GETLBTEXT, i, LongInt(pStringText));
      StrSearch := pStringText;
    finally
      StrDispose(pStringText);
    end;
    //用紙サイズ名にA5かA4が含まれていない場合は項目から削除
    if ((Pos('A4', StrSearch) = 0) and (Pos('A5', StrSearch) = 0)) then begin
      SendMessage(WndPaperNameCombo, CB_DELETESTRING, i, 0);
    end;
  end;
  //最初の項目を選択状態にしておく
  SendMessage(WndPaperNameCombo, CB_SETCURSEL, 0, 0);

  //[印刷の向き]の[横]のラジオボタンのハンドルを取得してクリック
  WndOrientation := GetDlgItem(WndDialog, 1057);
  SendMessage(WndOrientation, BM_CLICK, 0, 0);
end;

end.




07_ダイアログのコントロールの ID 値


ダイアログのコントロールの ID の値は,[645_ウィンドウの階層のツリー表示] のようなプログラムで調べることができます.

一般的なウィンドウの場合には,ウィンドウのハンドル値も,この ID 値もシステムが自動で割り当てます.ウィンドウを起動,表示するたびに違う値となります.ウィンドウのハンドルの値は変えることはできませんが,ID の値は,[660_他のアプリへ文字列送信 (Delphi 同士)] のサンプルのように,任意の値に設定することができます.
自分で作成するアプリケーションで,任意の値に設定しておくと,後でそのアプリなりフォームのコントロールを制御したい時に便利です.