実は、ただ印刷するだけならそんなに難しくはありません。 しかし、実際には印刷の形式だの途中で印刷をやめる機能だのを付けなくてはいけないので そのあたりが面倒くさいのです。今回は、全部省略してもっともシンプルなものを 作ります。ですから、全く汎用性はありません。
要するに、画面に表示するときとほとんど同じ要領です。 hdcは必ずCreateDC関数で作ります。hdcができたらStartDoc, StartPage関数を 呼びます。hdcに書き込んだ内容が印刷されます。後は、後始末をすればよいわけです。1.DOCINFO構造体のメンバをセット(di) 2.hdc = CreateDC(プリンタドライバ名, プリンタ名, ポート, NULL); 3.StartDoc(hdc, &di); 4.StartPage(hdc); 5.hdcに書き込みをする 6.EndPage(hdc); 7.EndDoc(hdc); 8.DeleteDC(hdc);
cbSizeは、この構造体のサイズを設定します。typedef struct { // di int cbSize; LPCTSTR lpszDocName; LPCTSTR lpszOutput; LPCTSTR lpszDatatype; // Windows 95 のみ DWORD fwType; // Windows 95 のみ } DOCINFO;
lpszOutputは、印刷をするときはNULLにします。ファイル名を設定して 印刷ジョブをファイルにリダイレクトすることもできます。
最後の2つのメンバは無視してください。
次にCreateDC関数ですが、これは第47章で少しだけ出てきました。
ドライバ名にはプリンタドライバの名前を指定します。 筆者の環境ではEPMJ3です。HDC CreateDC( LPCTSTR lpszDriver, // ドライバ名 LPCTSTR lpszDevice, // デバイス名 LPCTSTR lpszOutput, // 使いません( NULL ) CONST DEVMODE *lpInitData // オプションデータ );
[windows] NullPort=None load= run= device=EPSON MJ-3000CU,EPMJ3,LPT1:という具合になっています。
ポートというのはコンピュータの玄関みたいなものです。 本当は、プログラム中でwin.iniファイルを調べて これらの文字列を取得しなくてはいけないのですが、 今回は簡単のために直接プログラムにこれらの文字を 書き込みます。ですから、汎用性は全くないプログラムとなります。
これで印刷ジョブが開始されます。成功したときは 0より大きな数値を返します。失敗したときは0または マイナスの数値を返します。int StartDoc( HDC hdc, // デバイスコンテキストハンドル CONST DOCINFO *lpdi // DOCINFO構造体のアドレス );
この関数はプリンタドライバの準備を行います。int StartPage( HDC hDC // デバイスコンテキストハンドル );
この関数はデバイスに「1ページ分書き込んだよ!」と知らせます。 また、新しいページに進むようにドライバに連絡します。 成功したときは0より大きい数値を、失敗したときは0以下の数値を 返します。int EndPage( HDC hdc // デバイスコンテキストハンドル );
印刷終了直後にこの関数を呼び出します。成功したら0より大きい数値を、 失敗したときは0以下の数値を返します。int EndDoc( HDC hdc // デバイスコンテキストハンドル );
最後にDeleteDC関数を呼んでデバイスコンテキスハンドルを解放します。
では、サンプルのプログラムを見てみましょう。
リソーススクリプトは単にメニュー項目を決めているだけです。// prn01.rcの一部 // 自分で作る人はwindows.hと自作ヘッダーファイルをinclude するのを忘れない ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END MENUITEM "印刷(&P)", IDM_PRINT END END
これは、いつもと同じです。// prn01.cpp #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int MyPrint(void); char szClassName[] = "prn01"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
これもいつもとほぼ同じです。クラスメニューの名前を登録するのを 忘れないでください。//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }
ここはいつもと同じです。//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかる印刷処理", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
メニューで「印刷」が選ばれたら自作関数MyPrintを呼ぶようにしています。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_PRINT: MyPrint(); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }
hdcをCreateDCで作ったら後は画面表示の時と全く同じですね。 テキストの色を変えていますが、プリンタがカラーでないと 多分無視されるか、エラーになります。int MyPrint(void) { HDC hdc; DOCINFO docinfo; TEXTMETRIC textmetric; memset(&docinfo, 0, sizeof(DOCINFO)); docinfo.cbSize = sizeof(DOCINFO); docinfo.lpszDocName = "testprint"; //注意!CreateDCの引数は各個人の環境で異なります //Windows3.1では3番目の引数も設定が必要です //Windows95では最初の引数は無視されるのでNULLにしてもよいです hdc = CreateDC("EPMJ3", "EPSON MJ-3000CU", NULL, NULL); StartDoc(hdc, &docinfo); StartPage(hdc); GetTextMetrics(hdc, &textmetric); TextOut(hdc, 10, textmetric.tmHeight * 1, "これはテスト印刷です。", 22); TextOut(hdc, 10, textmetric.tmHeight * 2, "テスト印刷の2行目です。", 24); SetTextColor(hdc, RGB(255, 0, 0)); TextOut(hdc, 10, textmetric.tmHeight * 3, "色も変えられます!", 18); EndPage(hdc); EndDoc(hdc); DeleteDC(hdc); return 0; }
現在のフォントに対するメトリックを取得します。 成功すればTRUE、失敗したときはFALSEを返します。BOOL GetTextMetrics( HDC hdc, // デバイスコンテキストハンドル LPTEXTMETRIC lptm // TEXTMETRIC構造体のアドレス );
メンバがいっぱいあります。全部については説明しません。 tmHeightは、文字セルの高さを表します。 文字セルの高さはtmAscent + tmDescentのことです。typedef struct tagTEXTMETRIC { // tm LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; LONG tmWeight; LONG tmOverhang; LONG tmDigitizedAspectX; LONG tmDigitizedAspectY; BCHAR tmFirstChar; BCHAR tmLastChar; BCHAR tmDefaultChar; BCHAR tmBreakChar; BYTE tmItalic; BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet; } TEXTMETRIC;
tmDescentとは、基準線より下に行く部分です。(j,y,pなど)
tmAscentとは、基準線から文字セルの上端までです。
後は、各自ヘルプなどで調べてみてください。
左の図は実際に印刷をした用紙をスキャナで読みとったものです。
[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]
Update Sep/22/1997 By Y.Kumei