引言

近期在看Notepad++的源代码,学习学习Win32 原生API的开发技巧。

本文以Notepad++ 1.0版本的源代码为例讲解如何封装windows窗口,实现面向对象开发,如何通过窗口的继承实现代码的重用,并且利用C++的动态绑定特性实现多态,另外说明窗口封装过程中如何封装消息处理程序,这是实现面向对象的关键所在。听我细细道来。

实现窗口类

下图是Notepad++1.0版本窗口类的继承层次:

在Notepad++ 1.0 中所有的窗口元素:编辑窗口、选项卡窗口、工具栏、状态栏、对话框等等都有一个共同的父类:Window类,该类是一个虚基类,不能被实例化,其中的detroy函数是纯虚函数。里面声明了每个窗口所必须包含的变量:自身的窗口句柄_hSelf,父窗口句柄 _hParent 和 整个程序的实例句柄 _hInst。该类实现了一些窗口的基本操作,部分为虚函数。下面我们看看它的源代码:

#include <windows.h>

class Window //虚基类
{
public:
Window():_hSelf(NULL), _hParent(NULL), _hInst(NULL){}; // 构造函数,在子类中的构造函数调用,为三个变量赋值,
virtual ~Window() {}; virtual void init(HINSTANCE hInst, HWND parent) // 虚函数、子类中实现自己的版本,如注册窗口,创建窗口等等
{
_hInst = hInst;
_hParent = parent;
} virtual void destroy() = 0; // 资源释放等等 virtual void display(bool toShow = true) const {// 显示窗口
::ShowWindow(_hSelf, toShow?SW_SHOW:SW_HIDE);
}; virtual void reSizeTo(RECT & rc) // should NEVER be const !!!
{ // 这里特别强调rc不能为 const, 因为有时候要通过它返回
// 它上面的客户区,让客户上的窗口重置大小。如选项卡窗口
// reSizeTo返回选项卡的客户区、编辑窗口用返回的矩形区域
// 重置大小
::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE);
redraw();
}; virtual void redraw() const { // 强制刷新窗口
::InvalidateRect(_hSelf, NULL, TRUE);
::UpdateWindow(_hSelf);
}; virtual void getClientRect(RECT & rc) const { // 得到用户区矩形
::GetClientRect(_hSelf, &rc);
}; virtual int getWidth() const {
RECT rc;
::GetClientRect(_hSelf, &rc);
return (rc.right - rc.left);
}; virtual int getHeight() const {
RECT rc;
::GetClientRect(_hSelf, &rc);
return (rc.bottom - rc.top);
}; virtual bool isVisible() const {
return bool(::IsWindowVisible(_hSelf));
}; HWND getHSelf() const { // 得到自身窗口句柄
if (!_hSelf)
{
::MessageBox(NULL, "_hSelf == NULL", "class Window", MB_OK);
throw int(999);
}
return _hSelf;
}; void getFocus() const {
::SetFocus(_hSelf);
}; HINSTANCE getHinst() const {
if (!_hInst)
{
::MessageBox(NULL, "_hInst == NULL", "class Window", MB_OK);
throw int(1999);
}
return _hInst;
};
protected:
HINSTANCE _hInst; // 程序实例句柄
HWND _hParent; // 父窗口句柄
HWND _hSelf; // 自身窗口句柄
};

这就是窗口的基类,用这个基类我们就能派生出自己的实现特定功能的窗口。下面讲解几个典型的窗口。

对话框的封装

Notepad++ 的对话框继承StaticDialog,StaticDialog又继承上面的Window类。对话框基类的声明如下:

class StaticDialog : public Window
{
public :
StaticDialog() : Window() {};
~StaticDialog(){};
virtual void create(int dialogID);
virtual bool isCreated() const {
return reinterpret_cast<bool>(_hSelf);
};
//virtual do
void destroy() {
::DestroyWindow(_hSelf);
};
protected :
static BOOL CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0;
};

对话框的封装关键在于create函数的实现。该函数传入对话框的资源ID然后创建,函数实现如下:

void StaticDialog::create(int dialogID)
{
_hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, (DLGPROC)dlgProc, (LPARAM)this); if (!_hSelf)
{
systemMessage("StaticDialog");
throw int(666);
}
display();
}

函数基本就是对话框创建的API调用,传入对话框资源、消息处理程序:dlgProc,这个函数是静态 static 函数,因此可以传入该函数调用,最后将this 指针传入其中,WM_INITDIALOG消息中可以获取这个指针。

下面看看dlgProc 的实现:

BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG :
{
StaticDialog *pStaticDlg = (StaticDialog *)(lParam);
pStaticDlg->_hSelf = hwnd;
::SetWindowLong(hwnd, GWL_USERDATA, (long)lParam);
pStaticDlg->run_dlgProc(message, wParam, lParam);
return TRUE;
} default :
{
StaticDialog *pStaticDlg = reinterpret_cast<StaticDialog *>(::GetWindowLong(hwnd, GWL_USERDATA));
if (!pStaticDlg)
return FALSE;
return pStaticDlg->run_dlgProc(message, wParam, lParam);
}
}
}

在WM_INITDIALOG 消息中将lParam转换成StaticDialog指针,这样就能获取窗口句柄_hSelf(基类成员), 同时将指针放在USERDATA中,在其他消息中取出,指针并调用成员函数:run_dlgProc,这个函数是纯虚函数,继承的对话框子类就能实现自己的特定消息处理了。这个就是消息处理程序的封装。在最后我们还将讲解主窗口的消息处理的封装,其实和对话框所用的方法大同小异。

选项卡窗口

写累了,待续

主窗口类

写累了,待续

封装消息处理程序(Encapsulating WndProc)

这里已Notepad++ 1.0 版本的代码讲解如何封装窗口消息处理程序。

写累了,待续

Notepad++源代码阅读——窗口封装与继承的更多相关文章

  1. Notepad++源代码阅读——窗口元素组织与布局

    1.1 前言 这两天在看notepad++ 1.0版本的源代码.看了许久终于把程序的窗口之间的关系搞清楚了现在把其组织的要点写于此,希望对大家有所帮助. 1.2 窗口元素之间的关系 Notepad++ ...

  2. 非常好!!!Linux源代码阅读——环境准备【转】

    Linux源代码阅读——环境准备 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/0_prepare.html 目录 Linux 系统环境准备 ...

  3. Java语言简介、基础组成、封装、继承、多态、抽象类、内部类、接口

    目录 Java简介 Java语言基础组成 面向对象 对象 封装 构造函数 this关键字 static(静态关键字) 主函数 静态什么时候用呢? 面向对象(数组工具对象建立) 设计模式 继承 成员变量 ...

  4. Mongodb源代码阅读笔记:Journal机制

    Mongodb源代码阅读笔记:Journal机制 Mongodb源代码阅读笔记:Journal机制 涉及的文件 一些说明 PREPLOGBUFFER WRITETOJOURNAL WRITETODAT ...

  5. 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

    本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...

  6. 利用doxygen提高源代码阅读效率

    阅读开源项目的源代码是提高自己编程能力的好方法,而有一个好的源代码阅读工具无疑能够让你在阅读源代码时事半功倍.之前找过不少源代码阅读工具,像SourceInsight.sourcenav.scitoo ...

  7. 04-OC属性的使用、自动释放池、封装和继承

    目录: 一.IOS6声明式属性的使用 二.autoreleasepool自动释放池 三.封装.继承 回到顶部 一.IOS6声明式属性的使用 注:声明式属性默认情况下,并没有解决内存问题, 当使用@pr ...

  8. C++内存中的封装、继承、多态(上)

    C++内存中的封装.继承.多态(上) 继我的上一篇文章:浅谈学习C++时用到的[封装继承多态]三个概念 此篇我们从C++对象内存布局和构造过程来具体分析C++中的封装.继承.多态. 一.封装模型的内存 ...

  9. JavaScript 定义类的最佳写法——完整支持面向对象(封装、继承、多态),兼容所有浏览器,支持用JSDuck生成文档

    作者: zyl910 [TOC] 一.缘由 由于在ES6之前,JavaScript中没有定义类(class)语法.导致大家用各种五花八门的办法来定义类,代码风格不统一.而且对于模拟面向对象的三大支柱& ...

随机推荐

  1. 读书笔记 effective C++ Item 33 避免隐藏继承而来的名字

    1. 普通作用域中的隐藏 名字实际上和继承没有关系.有关系的是作用域.我们都知道像下面的代码: int x; // global variable void someFunc() { double x ...

  2. 洛谷P3760异或和

    传送门啦 传送门啦 一般这种位运算的题都要把每一位拆开来看,因为位运算每个位的结果这和这一位的数有关. 这样我们用s[i]表示a的前缀和,即 $ a[1]+a[2]+....a[i] $ ,然后我们从 ...

  3. 浮动元素垂直居中,bootstrap栅格布局垂直居中

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. sqlserver中的全局变量总结

    @@CONNECTIONS返回自上次启动 Microsoft? SQL Server? 以来连接或试图连接的次数.@@CPU_BUSY返回自上次启动 Microsoft? SQL Server? 以来 ...

  5. SQL SERVER2008 存储过程、表、视图、函数的权限

    EXEC sp_addrolemember N'db_owner', N'db'----将db 设置为 db_owner 角色中的一员 EXEC sp_droprolemember N'db_owne ...

  6. DDD领域模型企业级系统(一)

    领域模型的基本构造块: 1.实体(Entity):有业务生命周期,使用标识进行跟踪. 2.值对象(Value Object):无业务生命周期,用来描述实体. 3.服务(Service):无状态的行为类 ...

  7. 批处理命令篇--配置免安装mysql

    免安装版的mysql是进行软件绿色发布的绝佳助手,本文介绍一种使用批处理命令自动配置mysql的方法. (1)建立三个文件,分别是:service install.bat,temp.txt,updat ...

  8. CCF CSP 201409-3 字符串匹配

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF CSP 201409-3 字符串匹配 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那 ...

  9. day7 socket网络编程基础

    Socket Socket是什么? 下面来看一下网络的传输过程: 上面图片显示了网络传输的基本过程,传输是通过底层实现的,有很多底层,我们写传输过程的时候,要知道所有的过程那就太复杂了,socket为 ...

  10. elasticsearch-.yml(中文配置详解)

    此elasticsearch-.yml配置文件,是在$ES_HOME/config/下 elasticsearch-.yml(中文配置详解) # ======================== El ...