この特集のトップページへ
Chapter 7:プレゼンテーション層の構築

7.4.8 用紙のサイズとプリンタの機種の変更
●Win32APIを使ってユーザーにプリンタを選択させる

PrintDlg関数の呼び出し
 List 7-54List 7-55の処理によって,DEVMODE構造体とDEVNAMES構造体に現在のプリンタの情報を設定したら,それらをTable 7-18に示したPRINTDLG構造体に格納し,それをPrintDlg関数の引数として渡す。それにより,[印刷]または[プリンタの設定]のダイアログボックスを表示することができる。

 しかし,Table 7-18PRINTDLG構造体の定義を見るとわかるように,DEVMODE構造体とDEVNAMES構造体はそのままの状態で渡すのではなく,メモリハンドルを渡すことになっている。

 ハンドルとは,Win32 APIで使われる用語で,メモリの位置などを示す識別子のことである。ハンドルはVisual BasicにおいてはLong型として表現される。メモリハンドルを取得するには,Win32 APIのGlobalAlloc関数を使う。GlobalAlloc関数の定義は次のようになっている。

Public Declare Function GlobalAlloc Lib _
    "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long

 GlobalAlloc関数の第1引数には,どのようなメモリを設定するのかを指定する。ここでは再配置可能かどうかとか,メモリを0でクリアするかとか,圧縮するか,などという設定ができる。しかし,ここでは深入りはせず,GHNDという定数を指定すればよいとだけ説明しておく。GHND定数は,次のように定義される。

Public Const GMEM_MOVEABLE = &H2
Public Const GMEM_ZEROINIT = &H40
Public Const GHND = GMEM_MOVEABLE Or GMEM_ZEROINIT

 GlobalAlloc関数は,第1引数にGHND定数が指定されると,0でクリアされた移動可能なメモリを確保する。

 GlobalAlloc関数の第2引数には,確保したいバイト数を指定する。すると,GlobalAlloc関数は指定されたメモリを確保し,そのメモリを指し示すメモリハンドルを返す。たとえば,List 7-54で初期化したDEVMODE構造体の変数vDevModeを格納できるだけのメモリを準備するには,次のようにする。

Dim hDevMode As Long
hDevMode = GlobalAlloc(GHND, Len(vDevMode))

 これにより,vDevMode変数の占める容量と同じバイト数のメモリがWindowsによって用意され,変数hDevModeからアクセスできるようになる。

 PRINTDLG構造体のhDevModeメンバには,このようにして定義したhDevMode変数の値を設定することになるが,このままだとhDevMode変数が指し示すメモリエリアは0に初期化されているだけで,vDevMode変数の内容を保持してはいない。つまり,このあとhDevMode変数の値が指すメモリエリアにvDevMode変数の内容をコピーする必要がある。

 メモリの内容をコピーするためには,まず,GlobalLock関数を使ってメモリをロックする。GlobalLock関数は次のように定義される。

Public Declare Function GlobalLock Lib "kernel32" _
    (ByVal hMem As Long) As Long

 GlobalLock関数は,引数に指定されたメモリハンドルが指し示すメモリエリアをロックし,メモリへのアクセスを可能とする。GlobalLock関数は,固定したメモリの場所(C言語的に言うとポインタである)を返し,それを通じてメモリにアクセスすることができる。


One Point! すでにご承知のことと思うが,Windowsはスワップ機能を備えており,メモリ容量が足りなくなると,メモリの内容を一時的にハードディスクに待避してメモリを空ける。GlobalAlloc関数で確保したメモリも,当然ながらこの待避の対象となる(ただし,第1引数の指定によっては,スワップしないメモリ領域を作ることもできる)。当然ハードディスク上に待避されたものには直接アクセスできないわけだが,GlobalLock関数を呼び出すと,その時点で待避されていても,メモリに復元されアクセスできるようになる。こういった理由で,GlobalAlloc関数で確保したメモリにアクセスするためには,GlocalLock関数を呼び出す必要がある。

 GlobalLock関数は,たとえば次のように使う。

Dim lpDevMode As Long
lpDevMode = GlobalLock(hDevMode)

 これにより,hDevMode変数で示されるメモリエリアがlpDevMode変数を通じてアクセスできるようになる。あとは,vDevMode変数が保持する内容をlpDevMode変数が指すメモリの場所にコピーすればよい。メモリ間でのコピーするためには,Win32 APIのCopyMemory関数を使う。CopyMemory関数は次のように定義される。

Public Declare Sub CopyMemory Lib "kernel32" _
    Alias "RtlMoveMemory" (Destination As Any, _
                           Source As Any, _
                           ByVal Length As Long)

 CopyMemory関数は,第1引数にコピー先,第2引数にコピー元,第3引数にコピーするバイト数を指定すると,メモリ間でのコピーができるという仕様になっている。つまり,lpDevMode変数が指す場所に対してvDevMode変数の内容をコピーするには,次のようにすればよい。

CopyMemory ByVal lpDevMode, vDevMode, Len(vDevMode)

 これで,lpDevMode変数が指すメモリエリア(これは,hDevMode変数が指すメモリエリアでもある)にvDevMode変数の内容がコピーされることになる。

 ただし,このままだと,メモリがロックされたままなのでGlobalUnlock関数を呼び出してメモリのロックを解除しなければならない。GlobalUnlock関数は次のように定義される。

Public Declare Function GlobalUnlock Lib "kernel32" _
    (ByVal hMem As Long) As Long

 hDevMode変数に保持されたメモリハンドルが指すメモリエリアのロックを解除するには,次のようにGlobalUnlock関数を呼び出す。

GlobalUnlock hDevMode

 GlobalUnlock関数を呼び出した時点でロックが解除されるので,以降,GlobalLock関数を呼び出して得た値(今回の場合にはlpDevMode変数の内容)を通じてメモリにアクセスすることはできなくなる。もし,その後ふたたびメモリにアクセスしたくなったら,GlobalLock関数を呼び出さなければならない。

 以上のようにして,vDevMode変数の内容をコピーしたメモリエリアのハンドルを用意できる。DEVNAMES構造体を示すvDevNames変数の内容についても同等のことをすればよい。

 以上のことを踏まえ,List 7-54に示した初期化されたvDevMode変数と,List 7-55に示した初期化されたvDevNames変数の値を使い,[プリンタの設定]ダイアログボックスを表示する処理を記述するとList 7-56のようになる。

 List 7-56は,3行目でPRINTDLG構造体を格納するvPrintDlgという名前の変数を用意し,5〜21行目でその構造体を初期化し,24行目でPrintDlg関数を呼び出して印刷するという流れになっている。

 5〜9行目は,メモリを確保してvDevMode変数が指すDEVMODE構造体の内容をコピーするという処理,11〜15行目は,メモリを確保してvDevNames変数が指すDEVNAMES構造体の内容をコピーするという処理になっている。ここでは,vDevMode変数はList 7-54の処理で初期化されていることを,vDevNames変数はList 7-55の処理で初期化されていることを前提としている。

 17〜21行目が,vPrintDlg変数の内容を初期化する処理である。19行目でDEVMODE構造体の内容を保持するメモリハンドルを,20行目でDEVNAME構造体の内容を保持するメモリハンドルを,それぞれ設定している。そして,21行目でFlagsメンバにPD_PRINTERSETUP定数を指定することで,[印刷]ではなく[プリンタの設定]ダイアログボックスを表示するように設定している。

 24行目が実際にPrintDlg関数を呼び出す処理となる。これにより,[プリンタの設定]ダイアログボックスが表示される。ユーザーが[OK]ボタンを押すと,PrintDlg関数は0以外の値を返す。このとき,引数に指定したvPrintDlg変数のhDevModeメンバやhDevNamesメンバが指し示すメモリエリアには,ユーザーが選択したプリンタの情報が格納されている。以下の処理では,このメモリエリアに格納された情報を読み取り,その設定とおりにプリンタの設定を変更する処理を実装するということになる。

prevpg.gif Chapter 7 62/65 nextpg.gif