概要

ウィンドウに円弧を描画するサンプルです。

テスト環境

コンパイラ

Visual C++ 2008 Standard 32/64bit
Visual C++ 2013 Express 32/64bit

実行環境

Windows 8.1 Enterprise 64bit
Windows 7 EnterPrise Service Pack 1 64bit
Windows Vista Ultimate Service Pack 2 32bit
Windows XP Professional Service Pack 3 32bit

プログラムソースの概要

_tWinMain関数

Windowsから最初に_tWinMain関数が呼び出されます。 ウィンドウを作成する場合は、RegisterClass APIによりウィンドウクラスを定義してからCreateWindow APIを呼び出しウィンドウを作成します。 Windowsは入力等のイベントが発生するとアプリケーションにメッセージを送付します。 メッセージはキューに保管されます。アプリケーションはメッセージを取り出し、該当ウィンドウにメッセージを配信します。 メッセージの取り出しから配信までループで処理を行いウィンドウから終了メッセージが届くと、ループを抜けるように記述します。 これらの一連の処理は、通常にCreateWindow APIの後に記述しこれをメッセージループと呼んでいます。 詳細は以下を参照してください。
メッセージループ

WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)ウィンドウプロシージャー

RegisterClass APIにより登録することによりWindowsから呼び出されます。
第2引数にメッセージの種類が格納されていますので、switchステートメントによりメッセージごとの処理を振り分けます。
自分で処理しないメッセージはDefWindowProc APIに渡せばWindowsが標準的な処理を行ってくれます。

case WM_CREATE:

ウィンドウの初期化時に呼び出されます。
今回のプログラムでは、DefWindowProc APIにそのままメッセージを渡して処理しています。

case WM_SIZE:

ウィンドウのサイズが変更されたときに呼び出されます。
今回のプログラムでは、DefWindowProc APIにそのままメッセージを渡して処理しています。

case WM_DESTROY:

ウィンドウが閉じるときに呼び出されます。
PostQuitMessage APIにより終了コードを指定して、ウィンドウプロシージャーを終了させます。

case WM_PAINT:

ウィンドウを再描画する必要があるときに呼び出されます。
他のウィンドウに隠れ再びフォアグラウンドになった場合などウィンドウの再描画はWindowsが面倒を見ないのでプログラマの仕事となっています。
BeginPaint APIを呼び出して、描画に必要なデバイスコンテキストのハンドルを取得します。
BeginPaint APIの第2引数のポインタにはPAINTSTRUCT構造体のポインタを渡します。
BeginPaint API終了後、PAINTSTRUCT構造体には再描画が必要な領域の座標等の値が格納されています。
線を使用して描画するには、ペンを作成しなければなりません。
ペンの作成方法の詳細は以下を参照してください。
GDIのペンの作成方法
ペンを作成したらそのペンで描画させるためにSelectObject APIによりペンを選択します。戻り値は、前回のペンのハンドルですので保存しておき、デバイスコンテキストの解放前にペンの設定をもとに戻すときに使用します。
円弧を描画するにはArc APIを使用します。
円の大きさは、円を内接円とする正方形の左上(x1,y1)、右下(x2,y2)で指定します。 円弧の始点と終点は、円の中心と始終点を結ぶ直線で決まります。したがって、x3,y3,x4,y4は円弧上の点でなくても正常に動作します。なお、x3,y3方向から左向きにx4,y4まで作図されます。 ただし、(x3,y3)と(x4,y4)の距離が2ピクセルを下回ると円弧を作図するつもりが円が作図されてしまいます。したがって、円弧の長さが極端に短い場合は、中心から(x3,y3) (x4,y4)の距離を大きくとれば正常に作図されます。まあ、この程度であれば、直線で結んでも問題ないと思いますが。

x1,y1 x2,y2 x3,y3 x4,y4 cx,cy y x SVGの代替画像
SelectObject APIでペンを元に戻します。
再描画が終了したことをWindowsに知らせるためにEndPaint APIを使用してます。このAPIを呼び出さないと何度もWM_PAINTメッセージが発生し暴走します。

プログラムソース

arc.cpp


//      円弧の描画
//      Visual C++ 2008/2013    32/64bit

#include <windows.h>
#include <tchar.h>
#include <math.h>

//      ウィンドウプロシージャー
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

TCHAR szClassName[] = TEXT("Arc");

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,TCHAR* lpszCmdLine, int nCmdShow){
        HWND hWnd;
        MSG lpMsg;
        WNDCLASS myProg;

        if (!hPreInst) {
                myProg.style =CS_HREDRAW | CS_VREDRAW;
                myProg.lpfnWndProc =WndProc;
                myProg.cbClsExtra =0;
                myProg.cbWndExtra =0;
                myProg.hInstance =hInstance;
                myProg.hIcon =NULL;
                myProg.hCursor =LoadCursor(NULL, IDC_ARROW);
                myProg.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);
                myProg.lpszMenuName =NULL;
                myProg.lpszClassName =szClassName;
                if (!RegisterClass(&myProg))
                        return FALSE;
        }
        hWnd = CreateWindow(szClassName,
        szClassName,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        300,
        300,
        NULL,
        NULL,
        hInstance,
        NULL);
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);
        while (GetMessage(&lpMsg, NULL, 0, 0)) {
                TranslateMessage(&lpMsg);
                DispatchMessage(&lpMsg);
        }
        return int(lpMsg.wParam);
}

//      ウィンドウプロシージャー

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
        HDC hdc;
        PAINTSTRUCT ps;

        switch (msg) {
                case WM_DESTROY:
                        PostQuitMessage(0);
                        break;
                case WM_PAINT:{
                        hdc = BeginPaint(hWnd, &ps);
                        HPEN hPen, hOldPen;

                        hPen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
                        hOldPen = (HPEN)SelectObject(hdc, hPen);

                        double cx=100;  //      円弧中心座標
                        double cy=100;  //      円弧中心座標
                        double r=100;   //      円弧半径
                        double qs=3.14/180*90;  //      円弧開始角 X軸から右回りの角度
                        double qe=3.14/180*0;   //      円弧終了角 X軸から右回りの角度
                        double x1=r*cos(qs)+cx;
                        double y1=r*sin(qs)+cy;
                        double x2=r*cos(qe)+cx;
                        double y2=r*sin(qe)+cy;
                        
                        Arc(hdc, int(cx-r), int(cy-r), int(cx+r), int(cy+r),int(x1),int(y1),int(x2),int(y2));

                        SelectObject(hdc, hOldPen);
                        DeleteObject(hPen);
                        EndPaint(hWnd, &ps);
                        break;}
                default:
                        return(DefWindowProc(hWnd, msg, wParam, lParam));
        }
        return (0L);
}

ソースファイルと実行ファイルのダウンロード

ダウンロード arc.zip(41.0kByte)

ZIPファイルに含まれるファイル
arc.cpp
arc.exe