環境:Visual C++.NET2003、WindowsXPProSP2
開発:MFC、Windowsアプリケーション
CPrintDialogクラスを使ってプリンタのHDCを取得しています。
下記の処理にて詰まっております。
<ダイアログ非表示>
GetDefaultsの後にGetPrinterDCで取得
→デフォルト設定のプリンターのDCしか取得できない。
<ダイアログ表示>
DoModalの後にCreatePrinterDCで取得
→任意のプリンタを選択可能だが、ダイアログが必ず出てしまう。
たとえばプリンター名やOSの内部的な
プリンターの列挙番号(というものがあるのかどうかわからないのですが)など
で、
出力先のプリンターを指定したい場合はどのような処理になるのでしょうか?
具体的にはそのアプリケーションのデフォルトプリンターを指定して、
初期設定として保存したいと考えています。
ご存知の方いらっしゃいましたらご教授願います。
宜しくお願い致します。
--
== HIKARU SAYAMA ==
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
> 環境:Visual C++.NET2003、WindowsXPProSP2
> 開発:MFC、Windowsアプリケーション
>
>
> たとえばプリンター名やOSの内部的な
> プリンターの列挙番号(というものがあるのかどうかわからないのですが)など
> で、
> 出力先のプリンターを指定したい場合はどのような処理になるのでしょうか?
>
OpenPriner , EnumPrinter あたりを中心に探ってみてください。
一応、この辺押さえられれば、独自処理も可能です。
「ただし」MFC は内部にプリンタ情報を抱えています。
そのため、その部分(CWinAppが持ってます)も含めて
フォローしていかないと、情報がおかしくなったり、
メモリーリークしたり(CRTでは検出できません)します。
特に、印刷プレビューとかが絡んでくると、むっちゃ悲惨です。
このへん注意しておかないと思わぬしっぺ返しを食らいますのでご注意を(^^;
// とっちゃん(高萩 俊行)
// http://tocchan.cocolog-nifty.com/
// Microsoft MVP for Windows SDK (Oct 2005 - Sept 2006)
レス有難うございます。
確認が遅くなりましてすみません。
> OpenPriner , EnumPrinter あたりを中心に探ってみてください。
> 一応、この辺押さえられれば、独自処理も可能です。
>
> 「ただし」MFC は内部にプリンタ情報を抱えています。
> そのため、その部分(CWinAppが持ってます)も含めて
> フォローしていかないと、情報がおかしくなったり、
> メモリーリークしたり(CRTでは検出できません)します。
# ある程度のところを操作しようとすると結構複雑であることを実感・・・
CWinAppクラスのメンバにCreatePrinterDCとSelectPrinterという関数があります
が、
この2つで任意にプリンタのセレクトは出来るのでしょうか?
手順として予想しているのは、
1)CPrintDialogでプリンタの選択画面を表示(DoModal)
2)m_pdからDEVMODEとDEVNAMESを抜き出して保存
3)SelectPrinterで2項で保存した設定内容を使用
4)CWinAppのCreatePrinterDCでプリンタDCを作成
5)印刷
というものなのですが、
こんな簡単な発想で問題ないのでしょうか?
> 特に、印刷プレビューとかが絡んでくると、むっちゃ悲惨です。
> このへん注意しておかないと思わぬしっぺ返しを食らいますのでご注意を(^^;
印刷プレビュー機能は当面必要ないので忘れておきます。
宜しくお願いします。
> CWinAppクラスのメンバにCreatePrinterDCとSelectPrinterという関数があります
> が、
> この2つで任意にプリンタのセレクトは出来るのでしょうか?
>
MFCのソース見てみました?
MFCが提供している機能を拡張する(似て非なるものを作る)場合は
MFC が何をやっているかを先に把握しておかないと結構痛い目にあいます。
(過去に何度そういう憂き目にあったことか...(^^;)
#敢えて可否を書いてないのは、確認してほしいからです(少なくとも上記2つは一目瞭然です)。
> 手順として予想しているのは、
> 1)CPrintDialogでプリンタの選択画面を表示(DoModal)
> 2)m_pdからDEVMODEとDEVNAMESを抜き出して保存
> 3)SelectPrinterで2項で保存した設定内容を使用
> 4)CWinAppのCreatePrinterDCでプリンタDCを作成
> 5)印刷
> というものなのですが、
> こんな簡単な発想で問題ないのでしょうか?
>
この予想が、MFC の動きを簡易的に書いているというのであればおおむね合ってます。
もう少し、関数レベルで書いていくと
CView::OnFilePrint の呼び出し(印刷コマンドの実行)
印刷処理全般の前準備(必要なリソース(CPrintInfoなど)の確保などなど)
OnPreparePrinting()を呼び出し
DoPrepearePrinting() を呼び出して、印刷ダイアログの表示や
HDCの構築まで一通り済ませて帰ってくる(中身のほとんどは、CWinAppにある)
OnBeginPrinting()を呼び出し
印刷中に必要になるリソースを前もって準備しておくなどを行う。
実際の印刷処理(指定ページ数分印刷)
OnEndPrinting() を呼び出し
OnBeginPrinting()で確保したものを開放
OnFilePrintの終了処理
HDCの開放などなど
となります。
ここで、実際にカスタマイズしていくのは、OnPreparePrinting() の部分がメインになると思います。
いずれにしても、その処理の大半が MFC の中で行われているため、
まずは、MFC が具体的にどういうことを行っていて、どういうデータをキャッシュしているのかを
把握するところからは入っていかないと、かなり難しいと思いますよ。
お付き合いいただき有難うございます。
> MFCのソース見てみました?
> MFCが提供している機能を拡張する(似て非なるものを作る)場合は
> MFC が何をやっているかを先に把握しておかないと結構痛い目にあいます。
> (過去に何度そういう憂き目にあったことか...(^^;)
>
> #敢えて可否を書いてないのは、確認してほしいからです(少なくとも上記2つは一
> 目瞭然です)。
確認いたしました。恐れ入ります。
非常に簡単にメンバ変数に入れられていて驚きました。
まず背景が遅れて申し訳ないのですが、
現状ダイアログベースのMFCアプリケーションにて開発を行い、
DBのデータを検索してデータがあったら印刷するという自動印字プログラムを構成
しています。
そのためCWinAppクラスでのプリンタ選択(予定)と、
ダイアログクラスのメンバで直接DC書き込み&印刷をしています。
(StartDocからEndDocまでの処理のみ実施)
> ここで、実際にカスタマイズしていくのは、OnPreparePrinting() の部分がメイ
> ンになると思います。
Onxxxの印刷前後処理イベントハンドラを実装せずに印刷しているのですが、
CViewクラスを使って実装したほうがよいのでしょうか?
> いずれにしても、その処理の大半が MFC の中で行われているため、
> まずは、MFC が具体的にどういうことを行っていて、どういうデータをキャッ
> シュしているのかを
> 把握するところからは入っていかないと、かなり難しいと思いますよ。
結局のところいまいちつかめていないのですが、
ダイアログベースでCViewを利用していない場合も、
OnPreparePrintingやOnBeginPrintingなどの処理は呼ばれているということなので
しょうか?
つまるところ普段は自動的に印字の開始終了がダイアログの表示無しで実施されま
す。
そのためCWinAppクラスの標準のコマンドによる印刷実装は検討していない状況で
す。
内容が煩雑になってしまい申し訳ないのですが、
アドバイスをいただければ幸いです。
>
> 確認いたしました。恐れ入ります。
> 非常に簡単にメンバ変数に入れられていて驚きました。
>
なんですよ。あの関数だけを見るとほとんど何も考えていない(ように見える)のです。
実際のもろもろはそこを呼び出しているところにすべてあるのですけどね(^^;
> まず背景が遅れて申し訳ないのですが、
> 現状ダイアログベースのMFCアプリケーションにて開発を行い、
> DBのデータを検索してデータがあったら印刷するという自動印字プログラムを構成
> しています。
> そのためCWinAppクラスでのプリンタ選択(予定)と、
> ダイアログクラスのメンバで直接DC書き込み&印刷をしています。
> (StartDocからEndDocまでの処理のみ実施)
>
>> ここで、実際にカスタマイズしていくのは、OnPreparePrinting() の部分がメイ
>> ンになると思います。
>
> Onxxxの印刷前後処理イベントハンドラを実装せずに印刷しているのですが、
> CViewクラスを使って実装したほうがよいのでしょうか?
>
>> いずれにしても、その処理の大半が MFC の中で行われているため、
>> まずは、MFC が具体的にどういうことを行っていて、どういうデータをキャッ
>> シュしているのかを
>> 把握するところからは入っていかないと、かなり難しいと思いますよ。
>
> 結局のところいまいちつかめていないのですが、
> ダイアログベースでCViewを利用していない場合も、
> OnPreparePrintingやOnBeginPrintingなどの処理は呼ばれているということなので
> しょうか?
>
> つまるところ普段は自動的に印字の開始終了がダイアログの表示無しで実施されま
> す。
> そのためCWinAppクラスの標準のコマンドによる印刷実装は検討していない状況で
> す。
>
フムフム。ダイアログベースなんですね。
ということは、幸か不幸か MFC の印刷サポートは使えません(^^;
コモンダイアログを除いて、MFCの印刷関連の仕組みはアンドキュメントな部分も
含めて全部使えないと思ってください(使えるところもありますが、使わないほうが無難です)。
コモンダイアログ自身は、CWinApp 側とやり取りすることはありませんので利用可能ですが
それ以外の部分(CWinApp側を含め)は、CView から使うことしか考えられていません。
もちろん全く利用できないわけではないのですが、基本的には使わない方向で考えたほうが
無難だと思います。
コモンダイアログについては、利用したほうがユーザーにとって都合がいいかどうかというところでしょう。
結構がりがりと印刷をカスタマイズしているソフトでは使っていないのが多いので
必ずしもというものでもありませんしね(実際、Officeも使っていませんし(^^;)。
参考までに印刷関連のリンク(日本語)を張っておきます。
印刷/印刷ジョブのスプール
http://www.microsoft.com/japan/msdn/library/ja/jpgdi/html/Toppage_PrintingPrintSpooler.asp
現在利用可能なプリンタの列挙は、EnumPrinters() API
デフォルトプリンタは GetDefaultPrinter() でわかります。
DeviceCapabilities()を使えば、プリンタがどの程度の能力を持っているかがおおむねわかりますし
DocumentProperties でプリンタの詳細設定ができるようになっています。
実際に印刷する場合は、CreateDC でデバイスを作って後は、HDCへの出力となります。
StartDoc/EndDoc などは HDCへの操作になりますが、大半の初期化処理はドライバ側と直接的に
やり取りする形になります。
かなり面倒なところもありますが、がんばってみてください。
英語のリファレンスを探れば、サンプルコードがあるかもしれません(^^;
早速のレス有難うございます。
> フムフム。ダイアログベースなんですね。
> ということは、幸か不幸か MFC の印刷サポートは使えません(^^;
そうですか・・・。(泣)
なんとなく予想はしていたのですが残念です。
> コモンダイアログを除いて、MFCの印刷関連の仕組みはアンドキュメントな部分も
> 含めて全部使えないと思ってください(使えるところもありますが、使わないほう
> が無難です)。
>
> コモンダイアログ自身は、CWinApp 側とやり取りすることはありませんので利用
> 可能ですが
> それ以外の部分(CWinApp側を含め)は、CView から使うことしか考えられていませ
> ん。
>
> もちろん全く利用できないわけではないのですが、基本的には使わない方向で考
> えたほうが
> 無難だと思います。
つまりはCCommonDialogの派生であるCPrintDialogに関しては、
直接的にCWinAppと関連が無いのでその範囲であれば問題ないということなのです
ね。
しかしながらダイアログを出さずに出来るのはGetDefaultPrinterのみ・・・。
もし任意プリンタを印刷に使いたかったら、
m_pdで無理やり設定して直後にCreatePrinterDCとかになるのでしょうか。
> コモンダイアログについては、利用したほうがユーザーにとって都合がいいかど
> うかというところでしょう。
> 結構がりがりと印刷をカスタマイズしているソフトでは使っていないのが多いの
> で
> 必ずしもというものでもありませんしね(実際、Officeも使っていませんし
> (^^;)。
実際のところ設定時にのみ必要となります。
普段は自動運用でデータをばんばんプリンタへ送信するものなので。
> 参考までに印刷関連のリンク(日本語)を張っておきます。
>
> 印刷/印刷ジョブのスプール
> http://www.microsoft.com/japan/msdn/library/ja/jpgdi/html/Toppage_PrintingPrintSpooler.asp
有難うございます。大変助かります。早速参考にさせていただきます。
> 現在利用可能なプリンタの列挙は、EnumPrinters() API
> デフォルトプリンタは GetDefaultPrinter() でわかります。
> DeviceCapabilities()を使えば、プリンタがどの程度の能力を持っているかがお
> おむねわかりますし
> DocumentProperties でプリンタの詳細設定ができるようになっています。
拝見いたしました。
気が遠くなりました。これは一仕事って感じです。
(MFCの偉大さを改めて痛感しました)
設定関連の部分が出来上がるまでは、
デフォルトプリンタで対応といった感じになりそうです。
とても参考になりました。有難うございました。
今後とも宜しくお願いします。
>
> つまりはCCommonDialogの派生であるCPrintDialogに関しては、
> 直接的にCWinAppと関連が無いのでその範囲であれば問題ないということなのです
> ね。
>
はい。
> しかしながらダイアログを出さずに出来るのはGetDefaultPrinterのみ・・・。
> もし任意プリンタを印刷に使いたかったら、
> m_pdで無理やり設定して直後にCreatePrinterDCとかになるのでしょうか。
>
この場合は、CPrintDialog に任せる必要すらないというのが正直なところです。
DCを作る部分は、コモンダイアログのフラグで作成させることも可能ですが
設定が面倒なだけなのであまり意味ないですしね(^^;
直接 CreateDC API を呼び出してしまえば十分事足りてしまいます(^^;
> 拝見いたしました。
> 気が遠くなりました。これは一仕事って感じです。
> (MFCの偉大さを改めて痛感しました)
>
結構ボリュームがあるんですよねぇ(^^;
リンク先の関数を全部使うわけではありませんが、
それでもいくつかの関数は必須ですし...
いずれにしても印刷処理で一番わけのわからない部分は
HDCを作るまでの作業です。
HDCさえできてしまえば、後は多少のおまじない(StartDocとか)があるくらいで
普通に画面出力するのと大差ありませんからね(^^;
> 設定関連の部分が出来上がるまでは、
> デフォルトプリンタで対応といった感じになりそうです。
>
最初のうちはこれでよいと思います。
開発環境なら、デフォルトプリンタを都合のよいものにして置けますからね(^^;
一通り印刷処理ができるようになったら、最後に DEVNAMES を管理する部分を
作りこんでいけばよいのではないでしょうか。
といっても、DEVNAMES をそのまま保持しておくのではなく
DEVNAMESが保持しているデータの実体部分だけを持っていればよいだけですが(^^;
MFC にあわせると DEVNAMES を使うことになりますが(コモンダイアログが
これを要求するため)、使わないほうが実管理は楽だったりします(^^;;
HANDLE hPrinter;
if (!::OpenPrinter((LPTSTR) szPrinterName, (LPHANDLE) &hPrinter, NULL)) {
return FALSE;
}
DWORD dwNeeded = DocumentProperties(NULL, hPrinter, (LPSTR) szPrinterName,
NULL, NULL, 0);
HGLOBAL hDevMode = GlobalAlloc(GHND, dwNeeded);
DEVMODE* pDevMode = (DEVMODE *)GlobalLock(hDevMode);
LONG lFlag = DocumentProperties(NULL, hPrinter, (LPSTR) szPrinterName,
pDevMode, NULL, DM_OUT_BUFFER);
pDevMode->dmPaperSize = DMPAPER_A4; // 用紙サイズ
pDevMode->dmOrientation = DMORIENT_LANDSCAPE; // 横向き設定
::ClosePrinter(hPrinter);
CDC DC;
BOOL bRet = DC.CreateDC ("WINSPOOL", (LPCSTR) szPrinterName, NULL,
pDevMode);
if (bRet) {
// 印刷処理
DOCINFO stDocInfo;
ZeroMemory( &stDocInfo, sizeof(stDocInfo) );
stDocInfo.cbSize = sizeof(stDocInfo);
stDocInfo.lpszDocName = "szDocName";
stDocInfo.lpszOutput = NULL;
stDocInfo.lpszDatatype = NULL;
stDocInfo.fwType = 0;
// 印刷開始
int iRet = DC.StartDoc(&stDocInfo);
if( iRet > 0 ) {
DC.StartPage();
// 実際の描画処理
DC.DrawText (,,,,,,,,);
DC.EndPage();
DC.EndDoc();
}
DC.DeleteDC ();
}
GlobalUnlock(hDevMode);
GlobalFree(hDevMode);
Inoueさんレスありがとうございます。
> プリンタ名はEnumPrinterで取得できます。
> 印刷部分は、試していないけど下記のような感じでできると思います。
# 後々の参考に保存、保存・・・
ソース掲載ありがとうございました。
しっかり考えていなかったのですが(取り敢えず逃げてたので・・・)、
具体的にソースを提示してくださいまして感謝感激です。
ぜひ参考にさせていただきます。
>> プリンタ名はEnumPrinterで取得できます。
>> 印刷部分は、試していないけど下記のような感じでできると思います。
>
> # 後々の参考に保存、保存・・・
>
> ソース掲載ありがとうございました。
> しっかり考えていなかったのですが(取り敢えず逃げてたので・・・)、
> 具体的にソースを提示してくださいまして感謝感激です。
>
自分でも後回しでいいんじゃないの?なんていっててすっかり忘れてました(^^;
EnumPrinters を使ったサンプルコードはローカルのMSDNにもあります。
かなり古いコードなので、どのバージョンにでも入ってるんじゃないかな。
EnumPrinters でMSのサポート技術情報を検索すると
C のサンプルソースも出てきます。
ちょっと古いですが、XPでも段取りは一緒です。
その場限りでOKなら、CPrintDialog でプリンタの選択も可能です。
こちらは、UIとか何にも考えずに、情報が取り出せますが、
ドライバのバージョンが一致していないと情報がずれてしまう可能性があるので
保持するのには向きません。
保存する場合は、とっておいても問題ない情報だけを抜き出しておいて
別途保存という形になります。
再構築が面倒ですが(^^;
やるときになったら、またスレッド起こしてもらえば、また細かい段取りも出せます(^^;
ここは2ヶ月たつとアーカイブ行きで消えてしまうので(^^;
// とっちゃん(高萩 俊行)@プリンタ制御仕事の一つです(^^;