/// <summary> /// 窗体句柄附加数据长度 /// </summary> #define CTRLWINDOWEXTRA 32 /// <summary> /// 声明回调函数的宏 /// </summary> #define DECLARE_WNDPROC(ProcName) \ LRESULT CALLBACK ProcName(HWND, UINT, WPARAM, LPARAM); /// <summary> /// 声明钟表组件的消息回调函数 /// </summary> DECLARE_WNDPROC(ClockCtrlProc) /// <summary> /// 注册窗体类 /// </summary> static ATOM _RegistCtrlClass(HINSTANCE hInst, LPCTSTR lpszClsName, WNDPROC pWndProc) { WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS, pWndProc, 0, CTRLWINDOWEXTRA, hInst, NULL, NULL, (HBRUSH)(COLOR_BTNFACE + 1), 0, lpszClsName, NULL }; return RegisterClassEx(&wcex); } /// <summary> /// 初始化用户自定义控件 /// </summary> BOOL InitializeUserControls() { ATOM atom = _RegistCtrlClass(_INSTANCE, _T("ClockCtrl"), (WNDPROC)ClockCtrlProc); _ASSERT(atom); if (atom == 0) return FALSE; return TRUE; }
/// <summary> /// 保存绘图对象的结构体 /// </summary> typedef struct tagCLK_GDIOBJ { HBRUSH brushBackground, // 控件背景画刷 brushDigitalDark, // 数字表盘暗色画刷 brushDigitalLight, // 数字表盘亮色画刷 brushClockBackground; // 表盘背景 LOGFONT fontDate, // 控件全局字体 fontClockNumber; // 表盘数字字体 HPEN penBorder, // 控件边框画笔 penDigitalBorder, // 数字边框画笔 penClockBorder, // 表盘边框画笔 penClockArrow; // 表盘箭头画笔 } CLK_GDIOBJ, *LPCLK_GDIOBJ; /// <summary> /// 保存运行时信息的结构体 /// </summary> typedef struct tagCLK_RUNTIME { SYSTEMTIME stimNow; // 当前时间 BOOL isDigitalSecondDark; // 数字表盘的秒针是否为灰色 UINT_PTR timSecond; // 按秒进行的定时器句柄 } CLK_RUNTIME, *LPCLK_RUNTIME; /// <summary> /// 表示数据位置的常量 /// </summary> #define CLKP_GDIOBJ 0 // 0~3字节存放CLK_GDIOBJ结构体变量地址 #define CLKP_RUNTIME (CLKP_GDIOBJ + sizeof(LPCLK_GDIOBJ)) // 4~7字节存放CLK_RUNTIME结构体变量地址这里定义了两个结构体,一个用于存储GDI对象,一个用于存储运行时状态,这两个结构体的变量都应该和对应的窗口(HWND)句柄进行绑定,这样才能做到令窗体类为“ClockCtrl”的不同窗体都能取到正确的,属于自己的数据。
/// <summary> /// 窗口创建消息 /// </summary> static int _OnCreate(HWND hCtrl, LPCREATESTRUCT lpcs) { LPCLK_GDIOBJ pGdi; LPCLK_RUNTIME pRunTm; HFONT fontDef; if (!_LOCHEAP) _LOCHEAP = HeapCreate(0, 0, 0); // 创建本地堆句柄 fontDef = (HFONT)GetStockObject(DEFAULT_GUI_FONT); /// 设置默认的GDI对象 pGdi = HeapAlloc(_LOCHEAP, HEAP_ZERO_MEMORY, sizeof(CLK_GDIOBJ)); GetObject(fontDef, sizeof(LOGFONT), &pGdi->fontDate); GetObject(fontDef, sizeof(LOGFONT), &pGdi->fontClockNumber); pGdi->brushBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); pGdi->brushClockBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); pGdi->brushDigitalDark = CreateSolidBrush(RGB(0xEE, 0xEE, 0xEE)); pGdi->brushDigitalLight = CreateSolidBrush(RGB(0xFF, 0, 0)); pGdi->penBorder = CreatePen(PS_SOLID, 1, RGB(0x90, 0xC4, 0xE8)); pGdi->penClockArrow = (HPEN)GetStockObject(BLACK_PEN); pGdi->penClockBorder = (HPEN)GetStockObject(BLACK_PEN); pGdi->penDigitalBorder = (HPEN)GetStockObject(WHITE_PEN); SetWindowLongPtr(hCtrl, CLKP_GDIOBJ, (LONG_PTR)pGdi); // 存储GDI对象结构体指针 /// 设置默认的运行时状态 pRunTm = HeapAlloc(_LOCHEAP, HEAP_ZERO_MEMORY, sizeof(CLK_RUNTIME)); pRunTm->isDigitalSecondDark = FALSE; GetLocalTime(&pRunTm->stimNow); SetWindowLongPtr(hCtrl, CLKP_RUNTIME, (LONG_PTR)pRunTm); // 存储运行时状态结构体指针 SendMessage(hCtrl, WM_START, 0, 0); // 发送启动消息 return 0; }可以看到,上述代码使用 SetWindowLongPtr函数,在0(CLKP_GDIOBJ)位置存储了CLK_GDIOBJ结构体的指针,在4(CLKP_RUNTIME)位置存储了CLK_RUNTIME结构体的指针,这样就相当于我们扩展了HWND句柄,在其中存储了我们所需的数据。
/// <summary> /// 窗口销毁消息 /// </summary> static void _OnDestory(HWND hCtrl) { LPCLK_GDIOBJ pGdi; LPCLK_RUNTIME pRunTm; pGdi = (LPCLK_GDIOBJ)GetWindowLongPtr(hCtrl, CLKP_GDIOBJ); // 获取GDI对象结构体 if (pGdi) { /// 删除所有的GDI对象 DeleteObject(pGdi->brushBackground); DeleteObject(pGdi->brushClockBackground); DeleteObject(pGdi->brushDigitalDark); DeleteObject(pGdi->brushDigitalLight); DeleteObject(pGdi->penBorder); DeleteObject(pGdi->penClockArrow); DeleteObject(pGdi->penClockBorder); HeapFree(_LOCHEAP, 0, pGdi); // 从内存中删除结构体 } pRunTm = (LPCLK_RUNTIME)GetWindowLongPtr(hCtrl, CLKP_RUNTIME); // 获取运行时状态结构体 if (pRunTm) { KillTimer(hCtrl, pRunTm->timSecond); // 停止定时器 HeapFree(_LOCHEAP, 0, pRunTm); // 从内存中删除结构体 } }可以看到,上述代码使用 GetWindowLongPtr函数,在0(CLKP_GDIOBJ)位置获取了CLK_GDIOBJ结构体的指针,在4(CLKP_RUNTIME)位置获取了CLK_RUNTIME结构体的指针,这样就取到了我们之前存储的数据。