一个时钟程序(c语言)

这几天看完了windows程序设计的GDI部分,贴上一段代码,演示的是一个时钟程序.

///////////////////////////////////////////////////////////////
//  by tianzhihen 

#include 
< windows.h >
#include 
" resource.h "
#include 
< math.h >

LRESULT __stdcall WndProc(HWND, UINT, WPARAM, LPARAM);

int  __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,  int  nShowCmd)
{
    
char szWindowClass[] = "MyClock";
    
    
// 注册窗口类
    WNDCLASSEX wcex;
    
    wcex.cbSize        
= sizeof(WNDCLASSEX); 
    wcex.style        
= CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    
= (WNDPROC)WndProc;
    wcex.cbClsExtra        
= 0;
    wcex.cbWndExtra        
= 0;
    wcex.hInstance        
= hInstance;
    wcex.hIcon        
= LoadIcon(hInstance, (LPCTSTR)IDI_MAIN);
    wcex.hCursor        
= LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground    
= (HBRUSH)(COLOR_3DFACE+1);
    wcex.lpszMenuName    
= NULL;
    wcex.lpszClassName    
= szWindowClass;
    wcex.hIconSm        
= NULL;
    
    ::RegisterClassEx(
&wcex);
    
    
    
// 创建并线程主窗口
    HWND hWnd = ::CreateWindowEx( 
        WS_EX_CLIENTEDGE,    
// 扩展样式
        szWindowClass,        // 类名
        "时钟",    // 标题
        WS_POPUP|WS_SYSMENU|WS_SIZEBOX,    // 窗口样式
        100,    // 初始 X 坐标
        100,    // 初始 X 坐标
        300,    // 宽度
        300,    // 高度
        NULL,        // 父窗口句柄
        NULL,            // 菜单句柄
        hInstance,    // 程序实例句柄
        NULL);     
    
    ::ShowWindow(hWnd, nShowCmd);
    ::UpdateWindow(hWnd);
    
    
// 进入消息循环
    MSG msg;
    
while(::GetMessage(&msg, NULL, 00))
    
{
        ::TranslateMessage(
&msg);
        ::DispatchMessage(
&msg); 
    }

    
    
return 1;
}



//  消息处理代码

#define  IDT_CLOCK 1
const   int  IDM_HELP  =   100 ;
const   int  IDM_TOPMOST  =   101 ;

//  实现函数
void  SetIsotropic(HDC hdc,   int  cxClient,  int  cyClient);
void  DrawClockFace(HDC hdc);
void  DrawHand(HDC hdc,  int  nLength,  int  nWidth,  int  nDegrees, COLORREF clrColor);

//  上一次Windows通知时的时间
static   int  s_nPreHour;         //  小时    
static   int  s_nPreMinute;     //  分钟
static   int  s_nPreSecond;     //  秒
//  窗口客户区的大小
static   int  s_cxClient;        
static   int  s_cyClient;
//  是否位于最顶层
static  BOOL s_bTopMost;

//  消息处理函数
LRESULT __stdcall WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
    
switch(message)
    
{
        
    
case WM_CREATE:        //CreateWindowEx()时调用
        {    
            
            HMENU hSysMenu;
            hSysMenu
=::GetSystemMenu(hWnd,FALSE);
            ::AppendMenu(hSysMenu,MF_SEPARATOR,
0,NULL);
            ::AppendMenu(hSysMenu,MF_STRING,IDM_TOPMOST,
"总在最前");
            ::AppendMenu(hSysMenu,MF_STRING,IDM_HELP,
"帮助");
            
// 设置时间
            SYSTEMTIME time;
            ::GetLocalTime(
&time);
            s_nPreHour 
= time.wHour%12;
            s_nPreMinute 
= time.wMinute;
            s_nPreSecond 
= time.wSecond;
            
            
// 创建定时器
            ::SetTimer(hWnd, IDT_CLOCK, 1000, NULL);
            
return 0;
        }

        
    
case WM_SIZE:    //ShowWindow()时调用
        {
            s_cxClient 
= LOWORD(lParam);
            s_cyClient 
= HIWORD(lParam);
            
return 0;
        }

    
case WM_PAINT:    //客户区无效时调用
        {
            PAINTSTRUCT ps;
            HDC hdc 
= ::BeginPaint(hWnd, &ps);
            
            
// 设置坐标系
            SetIsotropic(hdc, s_cxClient, s_cyClient);
            
            
// 绘制时钟外观
            DrawClockFace(hdc);
            
            
// 绘制指针
            
            
// 经过1个小时时针走30度(360/12),经过1分钟时针走0.5度(30/60)
            DrawHand(hdc, 2008, s_nPreHour*30 + s_nPreMinute/2, RGB(000));
            
// 经过1分钟分针走6度(360/60)
            DrawHand(hdc, 4006, s_nPreMinute*6, RGB(000));
            
// 经过1秒钟秒针走6度(360/60)
            DrawHand(hdc, 4001, s_nPreSecond*6, RGB(000));
            
            ::EndPaint(hWnd, 
&ps);
            
return 0;
        }

        
    
case WM_TIMER:
        
{
            
// 如果窗口处于最小化状态就什么也不做
            if(::IsIconic(hWnd))    // IsIconic函数用来判断窗口是否处于最小化状态
                return 0;
            
            
// 取得系统时间
            SYSTEMTIME time; 
            ::GetLocalTime(
&time);
            
            
// 建立坐标系
            HDC hdc = ::GetDC(hWnd);
            SetIsotropic(hdc, s_cxClient, s_cyClient);
            
            
// 以COLOR_3DFACE为背景色就可以擦除指针了(因为窗口的背景色也是COLOR_3DFACE)
            COLORREF crfColor = ::GetSysColor(COLOR_3DFACE); 
            
            
// 如果分钟改变的话就擦除时针和分针
            if(time.wMinute != s_nPreMinute)
            
{
                
// 擦除时针和分针
                DrawHand(hdc, 2008, s_nPreHour*30 + s_nPreMinute/2, crfColor);
                DrawHand(hdc, 
4006, s_nPreMinute*6, crfColor);
                s_nPreHour 
= time.wHour;
                s_nPreMinute 
= time.wMinute;
            }

            
            
// 如果秒改变的话就擦除秒针,然后重画所有指针
            if(time.wSecond != s_nPreSecond)
            
{
                
// 擦除秒针
                DrawHand(hdc, 4001, s_nPreSecond*6, crfColor);
            }

            
            
// 重画所有指针
            DrawHand(hdc, 4001, time.wSecond*6, RGB(000));
            DrawHand(hdc, 
2008, time.wHour*30 + time.wMinute/2, RGB(000));
            DrawHand(hdc, 
4006, time.wMinute*6, RGB(000));
            s_nPreSecond 
= time.wSecond;
            
            
return 0;
        }

        
    
case WM_NCHITTEST:
        
        
{
            UINT nHitTest;
            nHitTest
=::DefWindowProc(hWnd,WM_NCHITTEST,wParam,lParam);
            
// 如果鼠标左键按下,GetAsyncKeyState函数的返回值小于0
            if(nHitTest==HTCLIENT&&::GetAsyncKeyState(MK_LBUTTON)<0
                nHitTest
=HTCAPTION;
            
            
return nHitTest;
        }

        
    
case WM_CONTEXTMENU:    //点击鼠标右键
        
        
{
            POINT pt;
            pt.x
=LOWORD(lParam);
            pt.y
=HIWORD(lParam);
            HMENU hSysMenu
=::GetSystemMenu(hWnd,FALSE);
            
            
int nID=::TrackPopupMenu(hSysMenu,TPM_LEFTALIGN|TPM_RETURNCMD,
                pt.x,pt.y,
0,hWnd,NULL);
            
if(nID>0)
                ::SendMessage(hWnd,WM_SYSCOMMAND,nID,
0);
            
return 0;
        }

        
    
case WM_SYSCOMMAND:
        
{
            
int nID = wParam;
            
{
                
if(nID == IDM_HELP)
                
{
                    ::MessageBox(hWnd, 
"一个时钟的例子""时钟"0);
                }

                
else if(nID == IDM_TOPMOST)
                
{
                    HMENU hSysMenu 
= ::GetSystemMenu(hWnd, FALSE);
                    
if(s_bTopMost)
                    
{
                        ::CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_UNCHECKED);
                        ::SetWindowPos(hWnd, HWND_NOTOPMOST, 
0000
                            SWP_NOMOVE 
| SWP_NOREDRAW | SWP_NOSIZE);
                        s_bTopMost 
= FALSE;
                    }

                    
else
                    
{
                        ::CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_CHECKED);
                        ::SetWindowPos(hWnd, HWND_TOPMOST, 
0000,
                            SWP_NOMOVE 
| SWP_NOREDRAW | SWP_NOSIZE);
                        s_bTopMost 
= TRUE;
                    }

                }


                  
break;
                
            }

        }

        
        
        
        
    
case WM_CLOSE:
        
{
            ::KillTimer(hWnd, IDT_CLOCK);
            ::DestroyWindow(hWnd);
            
return 0;
        }
    
        
    
case WM_DESTROY:    // 窗口正在被销毁
        {        
            ::PostQuitMessage(
0);
            
return 0;
        }

        
    }

    
    
    
return ::DefWindowProc(hWnd, message, wParam, lParam);
}




// 绘制坐标系
void  SetIsotropic(HDC hdc,  int  cx,  int  cy)
{
    ::SetMapMode(hdc, MM_ISOTROPIC);
    ::SetWindowExtEx(hdc, 
10001000, NULL);
    ::SetViewportExtEx(hdc, cx, 
-cy, NULL);
    ::SetViewportOrgEx(hdc, cx
/2, cy/2, NULL);
}


//  绘制时钟的外观
void  DrawClockFace(HDC hdc)
{
    
const int SQUARESIZE = 20;
    
static POINT pt[] =
    
{
        
0450,        // 12点
            225390,    // 1点
            390225,    // 2点
            4500,        // 3点
            390-225,    //... 下面的坐标是上面的点的对称点(以X轴、Y轴或原点对称)
            225-390,
            
0-450,
            
-225-390,
            
-390-225,
            
-4500,
            
-390225,
            
-225390
    }
;
    
    
// 选择一个黑色的画刷
    ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH));
    
    
// 画12个圆
    for(int i=0; i<12; i++)
    
{
        ::Ellipse(hdc, pt[i].x 
- SQUARESIZE, pt[i].y + SQUARESIZE,
            pt[i].x 
+ SQUARESIZE, pt[i].y - SQUARESIZE);
    }

}




//  指针的长度、宽度、相对于0点偏移的角度、颜色分别由参数nLength、nWidth、nDegrees、clrColor指定
void  DrawHand(HDC hdc,  int  nLength,  int  nWidth,  int  nDegrees, COLORREF clrColor)
{
    
// 将角度nDegrees转化成弧度 .    2*3.1415926/360 == 0.0174533
    double nRadians = (double)nDegrees*0.0174533;
    
    
// 计算坐标
    POINT pt[2];
    pt[
0].x = (int)(nLength*sin(nRadians));
    pt[
0].y = (int)(nLength*cos(nRadians));
    pt[
1].x = -pt[0].x/5;
    pt[
1].y = -pt[0].y/5;
    
    
// 创建画笔,并选如DC结构中
    HPEN hPen = ::CreatePen(PS_SOLID, nWidth, clrColor);
    HPEN hOldPen 
= (HPEN)::SelectObject(hdc, hPen);
    
    
// 画线
    ::MoveToEx(hdc, pt[0].x, pt[0].y, NULL);
    ::LineTo(hdc, pt[
1].x, pt[1].y);
    
    ::SelectObject(hdc, hOldPen);
    ::DeleteObject(hPen);
}




总的来说程序不是很难,可能有时候对windows内部一些机制还存在疑惑,不过没办法,谁让它不开源呢?

还存在一点疑惑,记录下来:

在这段代码中:

     case  WM_SYSCOMMAND:
        
{
            
int nID = wParam;
            
{
                
if(nID == IDM_HELP)
                
{
                    ::MessageBox(hWnd, 
"一个时钟的例子""时钟"0);
                }

                
else if(nID == IDM_TOPMOST)
                
{
                    HMENU hSysMenu 
= ::GetSystemMenu(hWnd, FALSE);
                    
if(s_bTopMost)
                    
{
                        ::CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_UNCHECKED);
                        ::SetWindowPos(hWnd, HWND_NOTOPMOST, 
0000
                            SWP_NOMOVE 
| SWP_NOREDRAW | SWP_NOSIZE);
                        s_bTopMost 
= FALSE;
                    }

                    
else
                    
{
                        ::CheckMenuItem(hSysMenu, IDM_TOPMOST, MF_CHECKED);
                        ::SetWindowPos(hWnd, HWND_TOPMOST, 
0000,
                            SWP_NOMOVE 
| SWP_NOREDRAW | SWP_NOSIZE);
                        s_bTopMost 
= TRUE;
                    }

                }


                  
break;
                
            }

        }

返回值不能是0,一定要是DefWindowProc函数,因此用break弹出switch语句,再去执行DefWindowProc函数,否则该始终程序不能移动,不知缘何?若有哪位朋友知道的还望告诉我一下,不盛感激.

你可能感兴趣的:(windows,programming)