MONTH / May, 2009

用 前置声明,还是用 #include?

为了能让编译速度快一点,明智之举是使用前置声明,而不是 #include 整个头文件。

那声明时候可以用前置声明,而什么时候必须 #include 头文件呢?简单的说:

  1. 当不需要用到类型的具体实现时,包括构造器赋值运算符成员函数等,只需要前置声明就可以了
  2. 当需要用到类型的以上方法时,就不得不 #include 整个头文件

对该技巧的具体分析可以参考这里这里


征服 Visual Studio 的 Editor

  • 块选中
  • 类似 UltraEdit 里的“列模式”,按住 Alt 键再移动光标,就可以选择一块范围内的内容,而不必被束缚在只能一行接一行的选中方式了。

  • 整行剪切 / 复制 / 粘贴
  • 平时我都是用鼠标移到一行最左边的行号区去选中一行,然后执行复制或拖动等操作,其实,只要光标在某一行,直接按 Ctrl + X / Ctrl + C,就等于剪切/复制整行了,非常方便。

  • Clipboard Ring
  • 一直以来,当需要在多个文件中复制粘贴多个内容时,我都很笨地挨个 Ctrl + C / Ctrl + V,从来也懒得想是不是该搞个 multi-clipboard 工具来提高效率 -,-|||

    其实,从 VS2003 开始,VS 就已经内置了多重剪贴板的功能,虽然只支持当前 VS 进程内多文件间的复制粘贴,这就是 Clipboard Ring。Clipboard Ring 采用 LIFO(后进先出)的方式组织,即最后被复制或粘贴的内容排在环的最前面,最大支持 10 块剪贴板。用法很简单,对需要复制的多个内容块按 Ctrl + C,然后到需要粘贴的地方按 Ctrl + Shift + V,选择需要粘贴的内容就 Ok 了。每个 VS 版本的 Clipboard Ring 操作略有不同,在此不赘述了。

  • 代码重构
  • 待续……


禁止 Windows Update 安装更新后自动重启

Windows XP中如果将 Windows Update 设置成“自动”,那么 Windows 每次安装完更新以后会非常“体贴地”进入自动重启倒计时,一不留神,一道惨叫便回荡在夜空……(这种侵犯人权的贴心服务在 Vista 被去掉了)

要关闭这一讨厌的特性,还没有简便的方法,只有修改注册表才行:

  1. 运行 regedit,打开注册表编辑器;
  2. 进入键:HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\WindowsUpdate\AU\,如果没有找到这个键,新建一个;
  3. 新建一个名为 NoAutoRebootWithLoggedOnUsers 的 DWORD 值,值设为 1(为 0 则允许自动重启)。

修改完似乎不用重启即可生效。

除了强制自动重启,Windows 还懂得攻心战,隔三差五地弹出对话框提示需要自动重启,即使选择了稍后重新启动(Vista 可以选择提示间隔,已经是巨大的进步了)。

对于这个问题,组策略编辑器里倒是提供了关闭的功能:

  1. 运行 gpedit.msc,打开组策略编辑器;
  2. 进入“计算机配置” > “管理模板” > “Windows组件” > “Windows Update”,双击“计划的自动更新安装后不自动重启动”,在弹出的对话框中选择“已启用”,然后“确定”即可。

这项设置重启后生效。


遍历 CTreeCtrl

CTreeCtrl 的 GetNextItem 成员函数很诡异,nCode 设为 TVGN_NEXT | TVGN_CHILD 会一直返回传进去的 hItem 值,而不是返回下一个兄弟 item “或”第一个 child item。

所以,只好自己写遍历函数,没有采用递归的做法,用了一个 STL List 容器来保存下一个兄弟 item 和第一个 child item,遍历返回的依据是 item 的 lParam 值等于给定的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
HTREEITEM CModelNodeTreePane::_GetTreeItemByID(const unsigned int uiItemID)
{
    HTREEITEM   hCurrItem = m_kModelNodeTreeCtrl.GetRootItem(),
                hItem;
    std::list kItemList;
 
    kItemList.push_back(hCurrItem);
 
    while (kItemList.size() > 0)
    {
        hCurrItem = kItemList.front();
        kItemList.pop_front();
 
        if ((unsigned int)m_kModelNodeTreeCtrl.GetItemData(hCurrItem) == uiItemID)
        {
            return hCurrItem;
        }
 
        if ((hItem = m_kModelNodeTreeCtrl.GetChildItem(hCurrItem)) != NULL)
        {
            kItemList.push_back(hItem);
        }
        if ((hItem = m_kModelNodeTreeCtrl.GetNextSiblingItem(hCurrItem)) != NULL)
        {
            kItemList.push_back(hItem);
        }
    }
 
    return NULL;
}

动态创建的 CTreeCtrl 实例的消息响应

由于程序里的 CTreeCtrl 控件实例是通过 CTreeCtr::Create() 来创建的,无法通过 VS 的 Properties 面板里的 Control Events 工具来生成消息映射函数,但控件又需要响应鼠标点击事件,这时最简单的办法就是重载 CTreeCtr 实例的 owner 的 OnNotify() 成员虚函数(这个 owner 也必然是 CWnd 的子类):

protected:
    virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
BOOL CTreePane::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    NMHDR* pNMHDR = (NMHDR*)lParam;
    ASSERT(pNMHDR != NULL);
 
    switch (pNMHDR->code)
    {
    case TVN_SELCHANGED:
        _OnTreeCtrlSelChanged(wParam, lParam, pResult);
        break;
 
    default:
        break;
    }
 
    return CWnd::OnNotify(wParam, lParam, pResult);
}