none
Visual Studio Professional 2012(VC++ MFC)の「プリンタの設定」ダイアログについて RRS feed

  • 質問

  • 2012年10月22日
    Visual Studio Professional 2012(VC++ MFC)の「プリンタの設定」ダイアログについて

    お世話になります。Visual C++のMFCでプログラムを作成しようとしています。当方はMFCの初心者ですが、ごく初歩的なことは理解しているつもりです。
    MFCのシングルドキュメント形式(SDI)で、標準メニューに用意されている、「プリンタの設定」メニューを使用して、「プリンタの設定」ダイアログを表示し、そこに用紙サイズ(A4、A5等)を設定したとき、それに応じた用紙の縦・横のサイズを読みだそうとしていす。ところが、当方のプログラムが悪いのかもしれませんが、用紙サイズにA4を設定しても、A5を設定しても、縦・横のサイズはA4のサイズが返ってきます。
    本件について、解決策とまでいかなくても、なんらかの情報でも結構ですので、ご教示いただければ幸いです。

    1.当方のパソコン
    メーカ、型式 eMachines ET1710-01j
    OS  Windows 7 Ultimate 64bit Service Pack 1
    VS2012のバージョン 11.0.50727.1 RTMREL

    2.問題点
    (1) VC++2012(MFC)を使用して、「プリンタの設定」メニューから「プリンタの設定」ダイアログを表示し、そこで用紙サイズ(A4、A5等)を設定したとき、それに対応した用紙の縦・横のサイズを取得しようとしています。
    ところが、用紙ザイズをA4にしても、A5にしても、縦・横のサイズはA4のサイズしか返って来ません。
    (2) 当方のプログラム
    「プリンタの設定」ダイアログの表示方法や、データの取得方法は自分ではまだ良くわからないため、ネットにあったサンプルプログラムを参考に、次のようにプログラムを作成しました。

    ①ヘッダファイル(メンバ変数、メンバ関数の宣言)
    private:
     CImage img;  // イメージファイル読込用オブジェクト
     int kaiten;  // 画像回転角度
     int w;   // 画像横幅
     int h;   // 画像縦幅
     float bai;  // 画面表示用の倍率
     float bairitu;
     bool start;  // 起動時一回のみtrue
     bool save;  // 画面保存時true
     int width;
     int height;
     int max_wh;
     CString filename;  // 「開く」、及び「保存」のファイル名とパス
     CRect m_rcMargin;;  // 余白部分を記憶する
     DEVMODE* m_pDevMode; // ページ設定保存用
     DEVMODE* p_devmode;  // 印刷出力用
     int paper_size_x;  // 用紙横サイズ(0.01mm単位)
     int paper_size_y;  // 用紙縦サイズ(0.01mm単位)
     int size_no;   // 「プリンタの設定」ダイアログ 用紙ザイズの番号
     int direction_no;  // プリンタの最低余白(エプソン用下15mm)にクランプ
     void bai_calc();  // 画面表示用倍率計算
     void yohaku_henkan(int vh_mode); // 「プリンタの設定」で余白を縦・横に変換する。
     void yohaku_saitei(RECT* rect);  // エプソンプリンタの最低余白(下15mm)にクランプ

    ②ソースファイル(.cpp)
    void CpictureView::OnFilePrintSetup() // 「プリンタの設定」メニュー
    {
     // TODO: ここにコマンド ハンドラー コードを追加します。
     // Web「VC++ Ver1.5でプリンタの設定」のコードを参考にした。

     CPrintDialog dlg(TRUE);

     dlg.GetDefaults();
        LPDEVMODE lpMode = dlg.GetDevMode();   // デバイスモード取得
        lpMode->dmPaperSize   = size_no;      // 用紙サイズA4=9
        lpMode->dmOrientation = direction_no; // 用紙方向 縦=1(DMORIENT_PORTRAIT)
           //横=2(DMORIENT_LANDSCAPE)
        lpMode->dmFields      = DM_ORIENTATION |DM_PAPERSIZE;
        if( dlg.m_pd.Flags & PD_RETURNDEFAULT )
            dlg.m_pd.Flags = dlg.m_pd.Flags ^ PD_RETURNDEFAULT;

        if(dlg.DoModal() == IDOK ){  //ここで設定した値が出ます。
            lpMode = dlg.GetDevMode();
      p_devmode = lpMode;  // 最終出力用DEVMODE*

      if ((lpMode->dmOrientation != direction_no) ||
       (size_no != lpMode->dmPaperSize)) {
       size_no = lpMode->dmPaperSize;      //用紙サイズ
       direction_no = lpMode->dmOrientation;    //向き
       yohaku_henkan(direction_no); // 余白縦横変換プログラム
       if (direction_no == DMORIENT_PORTRAIT) { // 用紙向き縦
        paper_size_x = lpMode->dmPaperWidth * 10;
        paper_size_y = lpMode->dmPaperLength * 10;
       }
       else {      // 用紙向き横
        paper_size_x = lpMode->dmPaperLength * 10;
        paper_size_y = lpMode->dmPaperWidth * 10;
       }
       //start = true;
      }
      //Invalidate();
        }
    }

    // 「プリンタの設定」で余白を縦・横に変換する。
    void CpictureView::yohaku_henkan(int vh_mode)
    {
     RECT rect = m_rcMargin;  //余白データをセーブ

     if (vh_mode == DMORIENT_LANDSCAPE) { // 縦→横に変更
      m_rcMargin.left = rect.top;
      m_rcMargin.right = rect.bottom;
      m_rcMargin.top = rect.right;
      m_rcMargin.bottom = rect.left;
     }
     else {  // 横→縦に変更
      m_rcMargin.left = rect.bottom;
      m_rcMargin.right = rect.top;
      m_rcMargin.top = rect.left;
      m_rcMargin.bottom = rect.right;
     }
    }
    以上
    (3)プログラムvoid CpictureView::OnFilePrintSetup()をデバッグ実行したときの変数の値
    ①lpMode->dmPaperSize
    A4に設定すると9が返って来ます。A5に設定すると11が返って来ます。
    この動作は正常だと思います。
    ②lpMode->dmPaperLength = 2970
    lpMode->dmPaperWidth = 2100
    「プリンタの設定」ダイアログでA4に設定しても、A5に設定しても同じで、上記の値(A4の値)が返ってきます。A5に設定したらA5の値が返ってきて欲しいのですが、そうなりません。

    3.参考にしたネットのサンプルプログラム
    (1)タイトル:「VC++ Ver1.5でプリンタの設定」
    (2)URL:http://dss.o.oo7.jp/cgi/PT.cgi?VCPP/MFC/Print/SetPrinter15
    (3)サンプルプログラム(ネットからの引用です)
    2002/09/11
    VC++ Ver1.5 では SelectPrinter() が存在しない.

    「プリンタ設定」ダイアログボックスの「用紙サイズ」と「用紙の向き」を
    アプリケーションが持っている値を設定するには以下の方法で行う.

        CPrintDialog dlg(TRUE);
        dlg.GetDefaults();      // デフォルトを取得
        LPDEVMODE lpMode = dlg.GetDevMode();   // デバイスモード取得
        lpMode->dmPaperSize   = m_FormSize;             // 用紙サイズ
        lpMode->dmOrientation = m_FormDirection;        // 用紙向き
        lpMode->dmFields      = DM_ORIENTATION |DM_PAPERSIZE;
        if( dlg.m_pd.Flags & PD_RETURNDEFAULT )
            dlg.m_pd.Flags = dlg.m_pd.Flags ^ PD_RETURNDEFAULT;

        if(dlg.DoModal() == IDOK ){ //ここで設定した値が出ます。
            lpMode = dlg.GetDevMode();
            m_FormSize      = lpMode->dmPaperSize;      //用紙サイズ
            m_FormDirection = lpMode->dmOrientation;    //向き
        }
    参照
    プリンタの設定
    dmFields(用紙サイズの変更)

    4.以上ですが、私のプログラムが悪いのでしょうか。原因を調べるために何かやってみることがあるでしょうか。またマイクロソフト様には失礼ですが、CPrintDialogクラスのバグの可能性は考えられないのでしょうか。
    どんなことでも結構ですので、何か情報をいただければ、有りがたいです。
    以上、よろしくお願します。

    2012年10月22日 12:46

回答

  • その動作は正しいです。

    基本的に、DM_PAPERSIZEを指定しているのであればdmPaperSizeの設定が有効で、dmPaperLengthとdmPaperWidthは無効であり、その値は正しいものと考えてはいけません。逆に、DM_PAPERLENGTHとDM_PAPERWIDTHを指定しているのであれば、dmPaperSizeは無効で、その値は正しいものと考えてはいけません。

    dmFieldsのことを、もう少し調べてみてください。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:48
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月22日 14:22
  • CPrintDialog dlg(FALSE);

    にして試してみてください。

    この方法で進める場合は「解説」の記述に注意してください。


    • 編集済み BlueSkyColors 2012年10月23日 14:40 注意事項を追記
    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月23日 14:35
  •  何点か気になったところを。

    • 「<q>参考にしたネットのサンプルプログラム</q>」

      あなたが使用しているのは Ver 11.0 で、参考にしているのが Ver 1.5 ですか。余りにもバージョンが違うと思うのですが?

      「VC++ Ver1.5 では SelectPrinter() 」とありますが、VC11 にはありますよ?

    • プリンターの設定について

      おそらく大丈夫だと思いますが、「デバイスとプリンター」フォルダーを開き、印刷サーバーのプロパティを出して下さい。そこに用紙が定義されていますが、A5 のサイズは正しいでしょうか。

      また、プリンターのプロパティに利用可能な用紙の一覧がありますが、A5は登録されていますか。

    • GetDeviceCaps() は使えないですか?

    Jitta@わんくま同盟

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月24日 12:15
  • 参考に私が試したのを示します。

    環境はWindows 7 64bitのVisual Studio 2012です。
    C++ MFCのソリューションをウィザードで新規作成しました。
    デフォルトとの違いは「シングルドキュメント」に変更しただけです。

    そして標準の印刷セットアップ コマンドの部分を、下記のように修正しました。

    #include "afxdialogex.h"
    
    ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
    
    void CPrtSettingsApp::OnFilePrintSetup()
    {
    	int	length,width,size;
    	INT_PTR ret;
    
    	CPrintDialog dlg(FALSE);
    
    	dlg.GetDefaults();
    	LPDEVMODE lpMode = dlg.GetDevMode();
    	lpMode->dmFields      = DM_ORIENTATION | DM_PAPERLENGTH | DM_PAPERWIDTH;
    	if( dlg.m_pd.Flags & PD_RETURNDEFAULT )
            dlg.m_pd.Flags = dlg.m_pd.Flags ^ PD_RETURNDEFAULT;
    
    	GlobalUnlock(lpMode);
    
    	ret = dlg.DoModal();
    	DWORD dwret = CommDlgExtendedError();
    	lpMode = dlg.GetDevMode();
    	length = lpMode->dmPaperLength;
    	width = lpMode->dmPaperWidth;
    	size = lpMode->dmPaperSize;
    	GlobalUnlock(lpMode);
    }

    これをビルドし実行します。
    修正したのは「プリンターの設定」メニューなので、これを選択し、ダイアログを表示します。
    プロパティで(私の環境ではEPSON PX-G5100ですが)用紙設定をA3縦に変更。
    結果、
     length = 4200
     width = 2970
     size = 8
     orientation = 1
    でした。
    また、A5横にすると、
     length = 2100
     width = 1480
     size = 11
     orientation = 2
    でした。

    なので、正しく取得できています。

    使用しているプリンタドライバーを教えてもらえますか?

    可能であればそれで試します。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月24日 12:48
  • Windows Updateから該当ドライバをインストールしました(ベンダーのホームページではダウンロード提供していませんでした)。

    A5とハガキサイズを試したところ、確かに2100、2970で変化がありませんでした。また、DM_PAPERSIZEをセットしていないにもかかわらずdmPaperSizeに値がセットされていました。

    結論として、このドライバは「こういう実装」をしているので、このドライバを使用することが前提の場合は、処理方法を変更せざるを得ないと思います(dmPaperSizeとdmOrientationの情報をもとに用紙サイズを判断する、など)。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月25日 12:41

すべての返信

  • その動作は正しいです。

    基本的に、DM_PAPERSIZEを指定しているのであればdmPaperSizeの設定が有効で、dmPaperLengthとdmPaperWidthは無効であり、その値は正しいものと考えてはいけません。逆に、DM_PAPERLENGTHとDM_PAPERWIDTHを指定しているのであれば、dmPaperSizeは無効で、その値は正しいものと考えてはいけません。

    dmFieldsのことを、もう少し調べてみてください。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:48
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月22日 14:22
  • BlueSkyColors様                                                                        2012年10月23日

    早速のご回答ありがとうございます。ご指摘の通り、DEVMODE構造体でdmFieldsを
    DM_PAPERLENGTH と DM_PAPERWIDTHに設定する必要があるとネットに出ていました。
    そこで下記の通り、dmFieldsの設定値を変更してみたのですが、現象は変わりません。

    1.dmFieldsの設定
     次のように3通りで行ってみました。
     (1)lpMode->dmFields =  DM_PAPERLENGTH | DM_PAPERWIDTH;
     (2)lpMode->dmFields = DM_ORIENTATION |  DM_PAPERLENGTH | DM_PAPERWIDTH;
     (3)lpMode->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERLENGTH | DM_PAPERWIDTH;
     ここ以外はプログラムは変更していません。

    2.結果
     A4・縦とA5・横に設定を変更して、下記データの数値がかわるかどうか調べました。
     (1)lpMode->dmOrientation
        dmFieldsの設定(1項の3種類)に関係なく、縦(数値=1)、横(数値=2)と変化します。
     (2)lpMode->dmPaperSize
        dmFieldsの設定(1項の3種類)に関係なく、A4(数値=9)、A5(数値=11)と変化します。
     (3)lpMode->dmPaperLength
        dmFieldsの設定(1項の3種類)に関係なく、A4の値(数値=2970)のまま変わりません。
     (4)lpMode->dmPaperWidth
        dmFieldsの設定(1項の3種類)に関係なく、A4の値(数値=2100)のまま変わりません。

    以上の通りですが、プログラムが悪いのか、Visual C++ 2012の設定などがあるのか、VC++のバグなのか
    わかりません。またそれを調べる方法があるのかどうかもわかりません。何か試してみることがあるでしょうか。
    直接解決に結びつくことでなくても、またはっきりわかっていることでなくても(推定、参考などでも)結構ですので、
    何らかの情報がありましたらご教示いただきたく、よろしくお願いします。

    2012年10月23日 0:41
  • CPrintDialog dlg(FALSE);

    にして試してみてください。

    この方法で進める場合は「解説」の記述に注意してください。


    • 編集済み BlueSkyColors 2012年10月23日 14:40 注意事項を追記
    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月23日 14:35
  • BlueSkyColors様    2012年10月24日

    早速のご回答ありがとうございます。ご指摘の通り、CPrintDialog dlg(FALSE);に変更してみたのですが、現象は変わりません。詳細は以下の通りです。

    1.プログラムの変更
     (1)CPrintDialogオブジェクトの作成
        ご指摘の通り、CPrintDialog dlg(FALSE);に変更しました。
     (2)dmFieldsの設定
        lpMode->dmFields =  DM_PAPERLENGTH | DM_PAPERWIDTH;
        としました。

    2.結果
     CPrintDialog dlg(FALSE);とすることにより、「プリンタの設定」の代わりに「印刷」ダイアログボックスが出るようになりました。 これは正常だと思います。この「印刷」ダイアログボックスで以下のように設定を行い、どのような数値が返ってくるか試しました。

     2.1「印刷」ダイアログの「プロパティ」ボタンの「用紙/品質」タブで「詳細設定」ボタンを押し、用紙サイズをA4からA5に変更しました。
     (1)lpMode->dmOrientation = 1 : これは正しい値です。
     (2)lpMode->dmPaperSize = 11 : これは正しい値です。
     (3)lpMode->dmPaperLength = 2970 : これまでと同じく、A5の設定なのにA4の値が返って来ました。
     (4)lpMode->dmPaperWidth = 2100  : 同上

     2.2「印刷」ダイアログの「プロパティ」ボタンの「レイアウト」タブで「印刷yの向き」を「縦」から「横」に変更しました。
        また「レイアウト」タブの「詳細設定」ボタンを押し、用紙サイズをA4からA5に変更しました。
     (1)lpMode->dmOrientation = 2 : これは正しい値です。
     (2)lpMode->dmPaperSize = 11 : これは正しい値です。
     (3)lpMode->dmPaperLength = 2970 : これまでと同じく、A5の設定なのにA4の値が返って来ました。
     (4)lpMode->dmPaperWidth = 2100  : 同上

    以上の通りですが、他に何か試してみることがあるでしょうか。また、「「解説」の記述に注意してください」とのことですが、
    「解説の記述」とは、msdnのサイトの、
     (1)CPrintDialogクラスの説明の「解説」欄
     (2)CPrintDialogクラスのコンストラクター、CPrintDialog::CPrintDialogの「解説」欄
    のことを言われているのだと思います。これを読んでもよく理解できない所もあるのですが、今回の結果では今迄通り「ダメ」
    なため、「解説」欄の記述については特に調べていません。何か特に調べる必要があれば、何を調べればよいか、ご教示
    いただけるとありがたいです。

    以上ですが、よろしくお願いします。

    2012年10月24日 0:03
  •  何点か気になったところを。

    • 「<q>参考にしたネットのサンプルプログラム</q>」

      あなたが使用しているのは Ver 11.0 で、参考にしているのが Ver 1.5 ですか。余りにもバージョンが違うと思うのですが?

      「VC++ Ver1.5 では SelectPrinter() 」とありますが、VC11 にはありますよ?

    • プリンターの設定について

      おそらく大丈夫だと思いますが、「デバイスとプリンター」フォルダーを開き、印刷サーバーのプロパティを出して下さい。そこに用紙が定義されていますが、A5 のサイズは正しいでしょうか。

      また、プリンターのプロパティに利用可能な用紙の一覧がありますが、A5は登録されていますか。

    • GetDeviceCaps() は使えないですか?

    Jitta@わんくま同盟

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月24日 12:15
  • 参考に私が試したのを示します。

    環境はWindows 7 64bitのVisual Studio 2012です。
    C++ MFCのソリューションをウィザードで新規作成しました。
    デフォルトとの違いは「シングルドキュメント」に変更しただけです。

    そして標準の印刷セットアップ コマンドの部分を、下記のように修正しました。

    #include "afxdialogex.h"
    
    ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
    
    void CPrtSettingsApp::OnFilePrintSetup()
    {
    	int	length,width,size;
    	INT_PTR ret;
    
    	CPrintDialog dlg(FALSE);
    
    	dlg.GetDefaults();
    	LPDEVMODE lpMode = dlg.GetDevMode();
    	lpMode->dmFields      = DM_ORIENTATION | DM_PAPERLENGTH | DM_PAPERWIDTH;
    	if( dlg.m_pd.Flags & PD_RETURNDEFAULT )
            dlg.m_pd.Flags = dlg.m_pd.Flags ^ PD_RETURNDEFAULT;
    
    	GlobalUnlock(lpMode);
    
    	ret = dlg.DoModal();
    	DWORD dwret = CommDlgExtendedError();
    	lpMode = dlg.GetDevMode();
    	length = lpMode->dmPaperLength;
    	width = lpMode->dmPaperWidth;
    	size = lpMode->dmPaperSize;
    	GlobalUnlock(lpMode);
    }

    これをビルドし実行します。
    修正したのは「プリンターの設定」メニューなので、これを選択し、ダイアログを表示します。
    プロパティで(私の環境ではEPSON PX-G5100ですが)用紙設定をA3縦に変更。
    結果、
     length = 4200
     width = 2970
     size = 8
     orientation = 1
    でした。
    また、A5横にすると、
     length = 2100
     width = 1480
     size = 11
     orientation = 2
    でした。

    なので、正しく取得できています。

    使用しているプリンタドライバーを教えてもらえますか?

    可能であればそれで試します。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月24日 12:48
  • Jitta 様    2012年10月25日

    貴重なご意見ありがとうございます。下記の通り返信します。

    1.「あなたが使用しているのは Ver 11.0 で、参考にしているのが Ver 1.5 ですか。余りにもバージョンが違うと思うのですが?」
      おっしゃる通りで、バージョンが違いすぎることは私も心配な点です。ただ「プリンターの設定」ダイアログのサンプルプログラムが、私の本にもなく、またネットにもこのサイトしかみつけられないでいます。私の検索の仕方が悪いのかもしれませんが、このサンプルしか見当たらず、ここからコードを持ってきた次第です。

    2.プリンターの設定について
     ご指摘の点を調べてみました。
     2.1 プリンターの設定について以下の通り、確認、設定をしました。
     (1)「スタート」→「デバイスとプリンター」をクリック
     (2)「EPSON PM-820C ESC/P R」(通常使うプリンター)の「プリンターのプロパティ」をクリック
     (3)「EPSON PM-820C ESC/P Rのプロパティ」のダイアログで次の確認、設定を行いました。
       ①「全般」タブの「利用可能な用紙」には「A4」だけしか表示されていませんでした。
       ②「基本設定」ボタン→「レイアウト」タブの「詳細設定」ボタンをクリック
        ③「用紙サイズ」にはA4,A5,その他多数のサイズが表示されています。その中で「A5」を選択しました。
       ④少し気になるのですが、「A5]を選択しても、「全般」タブの「利用可能な用紙」には「A4」とだけ表示されていました。
     2.2 印刷結果
      「A5」に設定した状態で「メモ帳」で文書を印刷したところ、「A5」サイズ(縦210mm、横148mm)に印刷されました。

    3.SelectPrinter() 、及びGetDeviceCaps() について
      情報ありがとうございます。調べてみますので、少し時間をください。何かわかったら報告いたします。

    以上です。よろしくお願いします。

    2012年10月25日 10:32
  • BlueSkyColors様    2012年10月25日

    サンプルプログラムまで提示いただき、ありがとうございます。全く同じことを私の環境(Windows 7 Ultimate 64bit, Visual Studio 2012)で試してみたのですが、現象は同じで、「A4」を設定しても、「A5」の値が返って来ます。用紙サイズと縦・横は正しい値が返って来ます。
    私がまだVisual C++ MFCをよく理解していない点もありますので、やってみたことを下記いたします。この操作でよいと思うのですが、特にON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)のコードの変更は下記3項(2)の方法でよいでしょうか。

    1.新しいプロジェクトの作成
    (1)テンプレート:「MFCアプリケーション」
    (2)名前:          「PrtSettings」
    (3)アプリケーションの種類:「シングルドキュメント」
     その他はデフォルトのままとし、変更していません。

    2.「プリンターの設定」メニューのイベントハンドラーの追加
    (1)メッセージの種類:「COMMAND」(デフォルト)
    (2)関数ハンドラー名:「OnFilePrintSetup」(デフォルト)
    (3)クラスの一覧:「CPrtSettingsApp」

    3.コードの修正、入力
    PrtSettings.cppのコードを修正、入力しました。
    (1)#include "afxdialogex.h"
     このコードは自動的に作成されていました。
    (2)ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)のコード修正
     BEGIN_MESSAGE_MAP(...)とEND_MESSAGE_MAP()の間に次の2行がありました。
       ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinAppEx::OnFilePrintSetup)
       ON_COMMAND(ID_FILE_PRINT_SETUP, &CPrtSettingsApp::OnFilePrintSetup)
     この2行を注釈にして、代わりにご指示のとおり、
       ON_COMMAND(ID_FILE_PRINT_SETUP, OnFilePrintSetup)
    としました。
    (3)イベントハンドラ関数CPrtSettingsApp::OnFilePrintSetup()の中身
     提示いただいたプログラムを全くそのままコピーして入れました。

    4.デバッグ実行の結果
    (1)最終行GlobalUnlock(lpMode);にブレークポイントを設定してデバッグ実行しました。
    (2)「プリンターの設定」メニューで「印刷」ダイアログが表示されました。
    (3)「A4」→「A5」、「縦」→「横」に設定変更して「OK]としました。
    (4)ブレークポイントで停止した状態で、lpModeの中を表示すると次の通りでした。
     dmOrientation = 2(正常)
      dmPaperSize = 11(正常)
      dmPaperLength = 2970(「A5」に設定したのに「A4」の」値が返って来ました。)
      dmPaperWidth = 2100 (同上)

    5.プリンタドライバー
     プリンターはEPSON PM-820Cです。プリンタのプロパティの「詳細設定」タブででドライバー名には「EPSON PM-820C ESC/P R」と表示されます。

    6.プリンタの設定、「A5」サイズの認識について
     同じプログラムを実行して、こちらでだけダメなのは、当方の環境、設定などに問題がある気がします。ただし、「プリンターのプロパティ」で「A5」サイズに設定して実際に「メモ帳」で印刷すると、「A5」サイズ(縦210mm、横148mm)で印刷されます。この点については、「Jitta」様への返信を参照ください。通常のアプリケーションでは「A5」の設定が正しく反映されるようです。

    以上ですが、よろしくお願いします。

    2012年10月25日 11:33
  • Windows Updateから該当ドライバをインストールしました(ベンダーのホームページではダウンロード提供していませんでした)。

    A5とハガキサイズを試したところ、確かに2100、2970で変化がありませんでした。また、DM_PAPERSIZEをセットしていないにもかかわらずdmPaperSizeに値がセットされていました。

    結論として、このドライバは「こういう実装」をしているので、このドライバを使用することが前提の場合は、処理方法を変更せざるを得ないと思います(dmPaperSizeとdmOrientationの情報をもとに用紙サイズを判断する、など)。

    • 回答の候補に設定 佐伯玲 2012年10月29日 0:47
    • 回答としてマーク 佐伯玲 2012年10月30日 5:19
    2012年10月25日 12:41
  • BlueSkyColors様    2012年10月26日

    プリンタドライバまで試していただき、本当にありがとうございます。EPSON PM-820Cのドライバに原因があって、現状のプログラム(GetDevMode関数による用紙ザイズの取得)がうまく行かないということですね。

    ところで、現在Jitta様より指摘のあった、GetDeviceCaps() 関数について調べ始めました。今のところまだ「プリンタの設定」ダイアログが表示されないなど、思うとおりに動いていない所もあるのですが、コントロールパネルでEPSON PM-820Cの用紙サイズを「A4」に設定した時と、「A5」に設定した時で違う値が返ってくることがわかりました。

    用紙サイズの縦・横の値がピクセル単位で返って来るのですが、これが設定した用紙サイズ「A4」、「A5」に応じて、それに近い値が返って来ることがわかりました。

    この件はJitta様への返信で詳しい報告をしますので、そちらを見ていただきたいと思います。

    2012年10月26日 1:02
  • Jitta 様    2012年10月26日

    ご指摘のあったSelectPrinter()関数 、及びGetDeviceCaps()関数 について少し調べ始めました。今迄にわかったことを下記いたします。

    1.CWinApp::SelectPrinterについて
     msdnのサイトによればプリンタを選択するための関数のようです。今回はこの関数を使用せず、dlg.GetDefaults()で「通常使用するプリンタ」を選択しています。デバッグ実行でlpMode->dmDevicveNameに「EPSON PM-820C ESC/P R」と表示されることから、現状でもプリンタの取得は正常に行われていると思います。従ってSelectPrinter()関数を使用する必要はないと思われます。

    2.CDC::GetDeviceCaps() について
    (1)サンプルプログラム
      msdnのCPrintDialog::GetDefaultsのページにGetDeviceCapsのサンプルプログラムがありましたので、これをいれてみました。

    (2)サンプルプログラムの修正
      msdnの「GetDeviceCaps function」のページにGetDeviceCapsの引数についての説明がありました。それに従ってサンプルプログラムのGetDeviceCapsの引数を修正して、「HORZRES」と「VERTRES」を渡すようにしました。これによりプリンタの用紙サイズが取得できるかもしれないと考えました。

    (3)プログラムの実行結果
     ①「プリンタの設定」ダイアログは表示されません。そこで、コントロールパネルの「プリンタのプロパティ」で用紙サイズを設定してからサンプルプログラムを実行し、どのような値が返って来るか調べました。その結果、次のように、用紙サイズ「A4」、「A5」に対応した値が返って来ました。
     ②「A5」を設定した時のGetDeviceCapsの返り値は次のようにだいたい「A5」に対応した値となりました。
      横 2014ピクセル(約146mm)
      縦 2736ピクセル(約205mm)
     ③「A4」を設定した時のGetDeviceCapsの返り値は次のように「A4」に対応した値となりました。
      横 2892ピクセル(約210mm)
      縦 3969ピクセル(約297mm) 

    (4)プログラムソースリスト
    void CGetDevTest2View::OnFilePrintSetup() // 「プリンターの設定」メニュー
    {
     // TODO: ここにコマンド ハンドラー コードを追加します。
     CPrintDialog dlg(FALSE);

     if (!dlg.GetDefaults())
      AfxMessageBox(_T("You have no default printer!"));
     else
     {
      // attach to the DC we were given
      CDC dc;
      dc.Attach(dlg.m_pd.hDC);

      // ask for the measurements
      //int nHorz = dc.GetDeviceCaps(LOGPIXELSX);
      //int nVert = dc.GetDeviceCaps(LOGPIXELSY);
      int nHorz = dc.GetDeviceCaps(HORZRES);
      int nVert = dc.GetDeviceCaps(VERTRES);

      // almost always the same in both directions, but sometimes not!
      CString str;
      if (nHorz == nVert)
       str.Format(_T("Your printer supports %d pixels per inch"), nHorz);
      else
       str.Format(_T("Your printer supports %d pixels per inch ")
        _T("horizontal resolution, and %d pixels per inch vertical ")
        _T("resolution"), nHorz, nVert);

      // tell the user
      AfxMessageBox(str);

      // Note: no need to call Detach() because we want the CDC destructor
      // to call FreeDC() on the DC we borrowed from the common dialog
     }
    }

    (5)今後の調査
     現在のサンプルプログラムでは、「プリンタの設定」ダイアログが表示されません。これを「プリンタの設定」ダイアログを表示するようにし、そこで設定した用紙サイズ「A4」、「A5」等が、GetDeviceCaps関数で取得できるかどうかを調べようと思います。
    何かわかりましたら、また報告させていただきます。

    2012年10月26日 4:13
  • BlueSkyColors様                                                              2012年10月28日
    Jitta様

    Jitta様より指摘のあった、GetDeviceCaps() 関数について調べた結果を報告します。結論から申しますと,GetDeviceCaps()関数を使用することにより、EPSON PM-820Cのプリンタドライバでも、「A4」、「A5」等の設定を反映した用紙サイズが取得可能です。
    以下にやってみたことを報告します。プリンタドライバはEPSON PM-820CとCanon MG8130の2つについて調べました。
    またGetDeviceCaps()関数の引数は、ピクセル単位の値を返すHORZRES、VERTRESと、mm単位の値を返す、HORZSIZE、VERTSIZEの2つを試しました。これらはいずれもプリンタの印刷可能範囲を返すもので、用紙サイズよりも少し小さな値が返って来ます。
    これに対し、用紙サイズそのものをピクセル単位で返す引数PHYSICALOFFSETX、PHYSICALOFFSETYがあることもわかりました。

    1.プログラムリスト
     BlueSkyColors様のサンプルプログラムに、GetDeviceCaps()関数による用紙サイズの取得を追加しました。

    void CPrtSettingsApp::OnFilePrintSetup() // 「プリンタの設定」メニュー
    {
       // TODO: ここにコマンド ハンドラー コードを追加します。
       int length,width,size;
       INT_PTR ret;

       CPrintDialog dlg(FALSE);

       dlg.GetDefaults();
       LPDEVMODE lpMode = dlg.GetDevMode();
       lpMode->dmFields      = DM_ORIENTATION | DM_PAPERLENGTH | DM_PAPERWIDTH;
       if( dlg.m_pd.Flags & PD_RETURNDEFAULT )
             dlg.m_pd.Flags = dlg.m_pd.Flags ^ PD_RETURNDEFAULT;

       GlobalUnlock(lpMode);

       ret = dlg.DoModal();
       // attach to the DC we were given
       CDC dc;   // 追加
       dc.Attach(dlg.m_pd.hDC); // 追加

       DWORD dwret = CommDlgExtendedError();
       lpMode = dlg.GetDevMode();
       length = lpMode->dmPaperLength;
       width = lpMode->dmPaperWidth;
       size = lpMode->dmPaperSize;

       //int nHorz = dc.GetDeviceCaps(HORZRES); // 追加
       //int nVert = dc.GetDeviceCaps(VERTRES); // 追加
       //int nHorz = dc.GetDeviceCaps(HORZSIZE); // 追加
       //int nVert = dc.GetDeviceCaps(VERTSIZE); // 追加
       int nHorz = dc.GetDeviceCaps(PHYSICALWIDTH); // 追加
       int nVert = dc.GetDeviceCaps(PHYSICALHEIGHT); // 追加

       GlobalUnlock(lpMode);

    }

    2.プログラム実行結果

    2.1 EPSON PM-820Cを選択し、「印刷」ダイアログで「A4」、「縦」を設定した時
     lpMode
        dmOrientation = 1 (正しい値です)
        dmPaperSize  = 9   (正しい値です)
        dmPaperLength = 2970 (正しい値です)
        dmPaperWidth  =  2100 (正しい値です)

                HORZRES、VERTRE指定時      HORZSIZE、VERTSIZE指定時
      nHorz =        2892(ピクセル)                       204(mm)
      nVert =        3969(ピクセル)                       280(mm)
      nHorz,nVertはA4に近い値となっています。

    2.2 EPSON PM-820Cを選択し、「印刷」ダイアログで「A5」、「横」を設定した時
     lpMode
        dmOrientation = 2 (正しい値です)
        dmPaperSize  = 11 (正しい値です)
        dmPaperLength = 2970 (正しくない値です(A4の値))
        dmPaperWidth  =  2100 (正しくない値です(A4の値))

                HORZRES、VERTRE指定時      HORZSIZE、VERTSIZE指定時
      nHorz =        2736(ピクセル)                      193(mm)
      nVert =        2014(ピクセル)                      142(mm)
     nHorz,nVertはA5に近い値となっています。

    2.3 Canon MG8130を選択し、「印刷」ダイアログで「A4」、「縦」を設定した時
     lpMode
        dmOrientation = 1 (正しい値です)
        dmPaperSize  = 9   (正しい値です)
        dmPaperLength = 2970 (正しい値です)
        dmPaperWidth  =  2100 (正しい値です)

                HORZRES、VERTRE指定時      HORZSIZE、VERTSIZE指定時
      nHorz =        4800(ピクセル)                       203(mm)
      nVert =        6826(ピクセル)                       289(mm)
      nHorz,nVertはA4に近い値となっています。
     
     ここで試しにCanonの設定を「ふちなし全面印刷」に設定したら、nHorz,nVertは完全に
    A4サイズに一致しました。
      nHorz =        210(mm)
      nVert =        297(mm)

    2.4 Canon MG8130を選択し、「印刷」ダイアログで「A5」、「横」を設定した時
     lpMode
        dmOrientation = 2 (正しい値です)
        dmPaperSize  = 11 (正しい値です)
        dmPaperLength = 2100 (正しい値です)
        dmPaperWidth  =  1480 (正しい値です)

                HORZRES、VERTRE指定時      HORZSIZE、VERTSIZE指定時
      nHorz =        4771(ピクセル)                      202(mm)
      nVert =        3335(ピクセル)                      141(mm)
     nHorz,nVertはA5に近い値となっています。

    2.5 用紙サイズそのものを取得したい場合
    2.4項までは、いずれもプリンタの印刷可能範囲が取得されます。これに対して、引数に、PHYSICALOFFSETX、PHYSICALOFFSETYを指定すると用紙サイズそのものが、ピクセル単位で取得できるようです。たとえばEPSON PM-820Cで「A4」を設定したときは、次のようになります。
        nHorz = 2976(ピクセル)
        nVert = 4209(ピクセル)
    2.1項 2892ピクセルで204mm。この比率で行くと、2976ピクセルは210mmになります。
    また、4209ピクセル0は297mmになります。これはA4の用紙そのもののサイズです。

    3.結論
    以上の通りですが、みなさまのおかげで不具合の原因がわかり、またその対策も何とかメドがたちました。一般的にはGetDevMode()関数で用紙サイズが取得可能であること、今回の不具合はEPSON PM-820Cのプリンタドライバに起因するものであること、またその場合でもGetDeviceCaps()関数を使用すれば用紙サイズの取得は可能であることがわかりました。

    初心者の私に丁寧にご教示いただき、ありがとうございました。また、BlueSkyColors様からは、サンプルプログラムを提示いただいたり、プリンタドライバまでテストしていただき、大変お世話になりました。
    厚くお礼申し上げる次第です。どうもありがとうございました。

    2012年10月28日 10:08