DEVMODE の dmFields は必要か?

[戻る]
ゲキアツ 2014/06/25(水) 16:35:10
またお世話になります。
VS2010、MFC、SDI で、印刷アプリを作っています。
一例として、次のコードを書いて長3号封筒への印刷を行うようにしています。

::OnPreparePrinting(CPrintInfo* pInfo)
{
    PRINTDLG pd;
    pd.lStructSize = (DWORD)sizeof(PRINTDLG);
    if (AfxGetApp()->GetPrinterDeviceDefaults(&pd))
    {
※→    lp->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERWIDTH | 
DM_PAPERLENGTH;
        lp->dmOrientation = DMORIENT_PORTRAIT;
        lp->dmPaperSize = DMPAPER_USER;
        lp->dmPaperWidth = 1200;
        lp->dmPaperLength = 2350;
    }
}

お尋ねしたいのは、※→ の行が必要か否か、他に必要なコードはないかということで
す。

問題の経緯を下記します。
実は、このアプリはフリーソフトとしてネット上で公開しています。
結構人気があります。(*^_^;)

1.当初は、※→ の行を入れていませんでした。(これを Ver1 とします)
ユーザーのAさんから、『1枚目の印刷は用紙サイズが長3号になるが、2枚目でデフォ
ルトのA4になる。』との指摘があり、※→ の行を入れたところ(Ver2)、問題が解決
しました。
(Aさんの環境:OS=Win7、プリンタは "Canon i2700" インクジェット)
2.その後、別のユーザーのBさんから、『「用紙の種類」を手動で「封筒」に設定する
が、Ver1のときは問題なかったのに Ver2 になってから、2枚目の印刷で用紙の種類がデ
フォルトの「自動選択」になる。』との連絡が来ました。
(Bさんの環境:OS=Win7、プリンタは”京セラKM-C2525E”コピー機のプリンタモード)

自分の環境は、OS=Win8.1、プリンタは EPSON EP-301ですが、※→ の行があってもなく
ても問題なく動作します。

(1) dmFields を指定すると、どんな作用となるのでしょうか?
(2) Bさんの現象に対する対策として、dmMediaType を指定するコードを入れることも考
えたのですが、DMMEDIA_USER 以上のドライバ定義値を指定しなければならず、自分の技
量では出来そうにありません。

以上につき、ご指導のほどよろしくお願いいたします。

ゲキアツ 2014/06/25(水) 16:39:21
1行書き落としました。
※→ の行の前に、
        LPDEVMODE lp = (LPDEVMODE)GlobalLock(pd.hDevMode);
が入ります。

ITO 2014/06/25(水) 22:21:38
うーーん、
 >(1) dmFields を指定すると、どんな作用となるのでしょうか?

>※→    lp->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERWIDTH | 
> DM_PAPERLENGTH;
これの場合、
  dmOrientation, dmPaperSize, dmPaperWidth, dmPaperLength
 の設定が有効になるようです。

(2) 何を設定したいのかわかりません。
  普通紙ならDMMEDIA_STANDARDです。

ITO 2014/06/25(水) 23:00:44
追記、
> lp->dmPaperSize = DMPAPER_USER;
0を設定してみて下さい。

ゲキアツ 2014/06/26(木) 16:43:46
ITO様、いつも有り難うございます。

1.lp->dmPaperSize = 0;
これを実際にやりますと、dmPaperWidth, dmPaperHeight が有効になりません。
デフォルトのA4の寸法になってしまいます。
確かに、MSDNの説明で、dmPaperWidth, dmPaperHeight の両方を指定する場合は 0 とす
るように書いてありますが、実際には dmPaperSize = DMPAPER_USER(256) にしないと駄
目なんです。

2.
> (2) 何を設定したいのかわかりません。
自分でも判らないのです。(^_^;)
自分の環境では(EPSON EP-301)、dmFields の指定をしなくても、dmOrientation, dmPape
rSize, dmPaperWidth, dmPaperLength が有効になります。これはプリンタドライバが気
を利かせて判断しているのではないか。
しかし、前記ユーザーAさんの場合は dmFields がないと、1枚目の印刷では有効で、2
枚目から無効(デフォルトになる)という中途半端な現象。
逆にユーザーBさんの場合は、dmFields で用紙サイズを指定すると、dmFields で指定し
ていない「用紙の種類」に影響が出る。

困ってしまったのです。

ken 2014/06/28(土) 19:26:18
DEVMODEってあまり使ったことないのですが、
論理和でセットするのなら

    lp->dmFields |= ...;

じゃなくていいのでしょうか?

ゲキアツ 2014/06/29(日) 08:50:40
>     lp->dmFields |= ...;
> じゃなくていいのでしょうか?

|= は追加するときではないでしょうか。
自分の前記の例では、実際には次のように書いています。

        lp->dmFields = DM_ORIENTATION;
        lp->dmOrientation = DMORIENT_PORTRAIT;
        if (・・・)
        {
            lp->dmFields |= (DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);
            lp->dmPaperSize = DMPAPER_USER;
            lp->dmPaperWidth = 1200;
            lp->dmPaperLength = 2350;
        }

ken 2014/06/29(日) 09:14:02
> |= は追加するときではないでしょうか。

それは「追加しなくてもいい」自信なり根拠なりがあるってことですか?
「何を設定すればいいのか判らない」のに。

> lp->dmFields = DM_ORIENTATION;
> lp->dmFields |= (DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH);

この4つ以外が、プリンタドライバの初期値とか、ユーザーの選択とかで
あらかじめセットされた状態でここに来ると、消して(無効にして)しまいますよね。

ゲキアツ 2014/06/29(日) 10:55:47
> ・・・・・消して(無効にして)しまいますよね。

そうなんですか。
dmFields は、そういう性質(意味)があるのですか?

ken 様、有り難うございます。

その後調べて次のような書き方をしてみました。これは正しいでしょうか?

    if (lp->dmFields & DM_ORIENTATION) lp->dmOrientation = DMORIENT_PORTRAIT;
    if (・・・)
    {
        if (lp->dmFields & DM_PAPERSIZE)
        {
            lp->dmPaperSize = DMPAPER_USER;
            if (dmFields & DM_PAPERWIDTH) lp->dmPaperWidth = 1200;
            if (dmFields & DM_PAPERLENGTH) lp->dmPaperLength = 2350;
        }
    }

自分の環境では、これで問題なく動作します。

とろ 2014/06/30(月) 20:13:59
1番目の人と2番目の人、もしかして同じこと言ってるってことは
ないですか?
どちらもプリンタの「印字設定」で設定した値に戻ってしまうと書いて
ある風に見えます。

これdmFieldsで指定したメンバ以外は初期値が使用されると言うことでは
ないでしょうか?(詳しくないです)


紙が出てきてしまうのでPDFCreaterというソフトを使ってですが
lp->dmFields = DM_ORIENTATION | DM_PAPERSIZE |  DM_PAPERWIDTH | DM_PAPERLENGTH;
lp->dmPaperWidth = 1200;
lp->dmPaperLength = 2350;

こんなんでどうですか。GetPrinterDeviceDefaultsで現在値を持ってきているので
特に変更する必要はないけど初期化はしない。みたいな。

ken 2014/06/30(月) 21:28:02
> dmFields は、そういう性質(意味)があるのですか?

さぁ?どうなんでしょう?
定義は MSDN を調べてください。

> その後調べて次のような書き方をしてみました。これは正しいでしょうか?

さぁ?どうなんでしょう?
とりあえず、DM_COPIES とか DM_COLOR とかが「クリアされてしまう」ことは
無くなったと*思います*が、私が「それで正しい」と言っても何の保証もありません。

ゲキアツ 2014/07/01(火) 08:55:12
そもそも、dmFields という項目は何のためにあるのだろうかと疑問に思っていました。
いきなり、lp->dmOrientation = DMORIENT_PORTRAIT; 等でいいではないかと。

if (lp->dmFields & DM_ORIENTATION) は、「プリンタドライバが DM_ORIENTATION に対
応しているなら」という意味になるらしく、そう使うのであれば dmFields という項目の
存在意義が解る気がします。

現在、この書き方で作ったアプリをユーザーBさんに試して貰っています。

とろ 2014/07/02(水) 00:38:23
ゲキアツさん。微妙に誤読しているような気がします。
多分MSDNの日本語版の翻訳が分かりにくいです。

http://msdn.microsoft.com/en-us/library/windows/desktop/dd183565%28v=vs.85%29.aspx

dmFields (google適当訳)
>その機能がサポートされているかは判定できます。
>んでこれで指定した機能「以外」は初期化されまっせとも書いてありませんか?

なので初めに手動でユーザがPrtDlg指定しても2回目には元に戻ってしまうのではないかと。
当然トレイの指定も追加しておかないとダメなのではないでしょうか。

#もう縦差しとか横差しとかも忘れた方がいいかと
#プリンタによって違いますよ

ゲキアツ 2014/07/03(木) 09:37:05
とろ様、有り難うございます。
MSDN の英語の説明は見ていませんでした。 Oh, my mistake!
これの日本語のページは見つからないです。

英文は読めるつもりですが、自分のプログラミング技術が未熟なため理解が難しいです。

>>んでこれで指定した機能「以外」は初期化されまっせとも書いてありませんか?
> 当然トレイの指定も追加しておかないとダメなのではないでしょうか。

そうだとすると、酷く厄介なことになります。
ユーザーが設定できる項目を指定しないでおくと、ユーザーが手動で設定しても規定値に
戻ってしまう?
あらゆるプリンタが用意している項目が何々であるか全て把握するには、enum何とかで取
得し、それらを全て dmFields に指定する??

ken様は lp->dmFields |= ...; で始めるべしとのことでした。
そうすることで、指定しない項目はユーザー設定のまま維持されるのであればいいのです
が。

ユーザーBさんから返事が来て、if文で作ったアプリは問題なくOKであるとのこと。
すなわち、「用紙の種類」が手動設定の「封筒」から2枚目の印刷でデフォルトの「自動
選択」に戻ることはないとのこと。
そうなると、ユーザーAさんの場合が心配なので、現在テストして貰っています。
(Aさんの場合は、dmFields を指定しておかないと用紙サイズが2枚目の印刷で規定の
A4に戻ってしまう。)

話変わって、MSDN の訂正ページを見つけました。
http://support.microsoft.com/kb/108924/ja
dmPaperLength と dmPaperWidth を設定するとき、dmPaperSize=0 は間違いで、dmPaperS
ize=DMPAPER_USER が正しいと。
だったら microsoftさんよ、元のページを修正しておいてくれ!

とろ 2014/07/04(金) 00:20:37
いやいや、だからdmFieldsでフラグを指定したからといって
GetPrinterDeviceDefaultsで現在値を取得してるんだから
自分のプログラムでわざわざ再設定する必要はないんですって。たとえば

lp->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERWIDTH | 
DM_PAPERLENGTH;
lp->dmOrientation = DMORIENT_PORTRAIT;
lp->dmPaperSize = DMPAPER_USER;
lp->dmPaperWidth = 12000;
lp->dmPaperLength = 23500;

こんなん固定で指定されたら対応してないプリンタによってはエラー状態になるか
強制排出待ちになってしまうでしょ。余計なことはせずに必要な設定以外は
ほっとけばいいんですよ。

lp->dmFields = DM_ORIENTATION | DM_PAPERSIZE | DM_PAPERWIDTH | 
DM_PAPERLENGTH;
lp->dmPaperWidth = 1200;
lp->dmPaperLength = 2350;

こんなのがやっぱり正解じゃないかなぁと。(この固定値が必要かはわかりませんけど)

#PDFCreater推しじゃないけど印刷系はとにかく紙が勿体無いので
#仮想プリンタでテストするのがお勧めです。

あとゲキアツさん、意外とIDEのデバッグ機能とか苦手ですか?

ゲキアツ 2014/07/06(日) 09:24:08
> ・・・自分のプログラムでわざわざ再設定する必要はないんですって。
> ・・・余計なことはせずに必要な設定以外はほっとけばいいんですよ。

なら、よかった (^o^)

lp->dmOrientation = DMORIENT_PORTRAIT; を入れているのは、他のメニューで横向き印
刷をするので、そこからこっちに切り替えたとき必要なもんで・・・。

> あとゲキアツさん、意外とIDEのデバッグ機能とか苦手ですか?

Yes, I am.  So sorry! (^_^;)

ユーザーAさんからの連絡がまだありません。
自分はこれから1週間留守するので、ここは保留とさせて戴きます。

ゲキアツ 2014/07/12(土) 14:06:11
[[解決]]
留守をして済みませんでした。

端的に注釈を付けたサンプルページがありました。こちらです。
http://www.codeproject.com/Articles/132365/Configuring-Printer-Settings-Programm
atically
多くのサンプルでは dmFields = ○○○; となっているのに、ここでは ken様がおっしゃ
るように dmFields |= ○○○; となっています。

これをお手本にしました。以下のコードとなります。

::OnPreparePrinting(CPrintInfo* pInfo)
{
    PRINTDLG pd;
    pd.lStructSize = (DWORD)sizeof(PRINTDLG);
    if (AfxGetApp()->GetPrinterDeviceDefaults(&pd))
    {
        LPDEVMODE lp = (LPDEVMODE)GlobalLock(pd.hDevMode);
        if (lp->dmFields & DM_ORIENTATION)
        {
            lp->dmOrientation |= DMORIENT_PORTRAIT;
            lp->dmFields = DM_ORIENTATION;
        }
        if (・・・)
        {
            if (lp->dmFields & DM_PAPERSIZE)
            {
                lp->dmPaperSize = DMPAPER_USER;
                lp->dmFields |= DM_PAPERSIZE;
                if (lp->dmFields & DM_PAPERWIDTH)
                {
                    lp->dmPaperWidth = 1200;
                    lp->dmFields |= DM_PAPERWIDTH;
                }
                if (lp->dmFields & DM_PAPERLENGTH)
                {
                    lp->dmPaperLength = 2350;
                    lp->dmFields |= DM_PAPERLENGTH;
                }
    }
}

これで、ユーザーAさんにテストして戴き、問題なく印刷できたとのこと。
解決です。
教えて戴いた皆様、ありがとうございました。

古いスレッドには追加発言できません。