2010年8月27日金曜日

Windows の PageSetupDialog ではまる(2)

結局のところ、PageSetupDlg というコモンコントロールの用紙選択コンボボックスを書き換えるという手法で対応することにしました。
 用紙選択コンボボックスは、Dlgs.h にて cmb2 と定義されています。給紙方法が、cmb3 です。

//
// Combo boxes.
//
#define cmb1 0x0470
#define cmb2 0x0471
#define cmb3 0x0472
#define cmb4 0x0473
...


WTL を使っている場合は、CComboBox を利用して

void CCustomPageSetupDialog::OnInitDialog( HWND hDlg ) {
m_hDlg = hDlg;
m_combo_size.Attach( ::GetDlgItem( hDlg, cmb2 ) );
m_combo_kyusi.Attach( ::GetDlgItem( hDlg, cmb3 ) );
}
void CCustomPageSetupDialog::OnDestroy() {
m_combo_kyusi.Detach();
m_combo_size.Detach();
}

という感じでアタッチできます。
で、プリンタ設定ダイアログを呼び出した直後にプリンタ設定ダイアログの DEVMODE をぱくってきて
以下のような感じで、用紙サイズをシンクロさせます。

void CCustomPageSetupDialog::SyncPaperSize() {
DEVMODE* pDev = GetDevMode();
int n = DeviceCapabilities( (LPCSTR)pDev->dmDeviceName, NULL, DC_PAPERS, NULL, pDev );
struct paper_name { char name_[64]; };
boost::scoped_array<paper_name> pn( new paper_name[ n + 2 ] );
boost::scoped_array<WORD> pi( new WORD[ n + 2 ] );
DeviceCapabilities( (LPCSTR)pDev->dmDeviceName, NULL, DC_PAPERNAMES, (LPSTR)pn.get(), pDev );
DeviceCapabilities( (LPCSTR)pDev->dmDeviceName, NULL, DC_PAPERS, (LPSTR)pi.get(), pDev );
m_combo_size.ResetContent();
for( int i = 0; i < n; ++i ) {
int item = m_combo_size.AddString( pn[i].name_ );
DWORD_PTR siz = pi[i];
m_combo_size.SetItemData( item, siz );
}
// プリンタ設定で選択された用紙を選択
for( int i = 0; i < n; ++i ) {
if( pDev->dmPaperSize == m_combo_size.GetItemData( i ) ) {
m_combo_size.SetCurSel( i );
break;
}
}
GlobalFree( pDev );
}

予想通りというか、dmPaperSize は、コンボボックスのItemDataPtr として保持されていました。
ここは、たぶんドキュメントに記述されていないです。
あほくさ・・・

追記:プリンタ設定ダイアログにてプリンタが変更されている場合、ページ設定ダイアログにて
 用紙サイズを変更した場合の副作用が生じて、ページ設定ダイアログ内のプリンタ情報
(=hDevNames と思われる)をうまく更新しないと、プリンタが元に戻ってしまいます。
 コモンダイアログがプリンタ情報のコピーを使用している場合は、どうにもならないので、
 やはり、車輪の開発で、自前でページ設定ダイアログを作成するほうが良さそうという
 結論にならざるを得ません。

結論:車輪の開発をしないと不可避です。
 PageSetupDlg API がプリンタ情報のコピーを保持しているので、どんなに頑張ってプリンタ情報を
 変更しても、用紙サイズのコンボボックスの値を変更した時点で、DEVMODE構造体が元のプリンタの
 情報に戻されてしまいます。

0 件のコメント: