新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → MFC 通用控件的初始化 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 3805 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: MFC 通用控件的初始化 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 MFC 通用控件的初始化

    摘要:介绍了MFC中通用控件初始化过程.

    关键字:通用控件 初始化 comctl32.dll

    这是我在阅读某源代码时无意中想到的一个问题,进行了一番研究,现在把结果贴出来,希望对感兴趣的人能有所帮助。

    InitCommonControls和InitCommonControlsEx
    从Win95开始,Windows提供了一些新的Win32控件,称为通用控件. 如:Toolbar,Status bar,Tree view,List view,Animation,Hot-key,Image list,Tab等等.这些控件的可执行代码都放在comctl32.dll中.要使用通用控件,必须加载comctl32.dll.

    可以调用函数InitCommonControls或InitCommonControlsEx来初始化控件.这两个函数都是动态链接库comctl32.dll中的函数,两个函数的原型如下:

    void InitCommonControls(VOID);
    BOOL InitCommonControlsEx(LPINITCOMMONCONTROLSEX lpInitCtrls);
    可以看到,InitCommonControls没有参数,表示初始化所有的(实际上是大部分,见下文)通用控件.而InitCommonControlsEx则可以指定初始化什么控件.

    这里"初始化"的含义是明确的,就是指注册相应的窗口类.比如,只有事先注册了"SysTreeView32"窗口类,然后才可以创建该控件的窗口.
    注意,注册窗口类只对当前进程有效,因为注册的时候必须指定一个窗口地址,而地址是只对一个进程有效的.因此,每个进程都必须初始化后才可以使用通用控件.

    函数InitCommonControls是个空函数,不做任何事情.但如果你调用了该函数,则链接器会将你的程序链接到comcl32.lib,然后在程序启动时,会加载comctl32.dll. 真正初始化的工作是在该库的入口点处做的,在这里会注册通用控件窗口类,然后应用程序就可以创建控件窗口,就象创建其它的子窗口控件一样.

    InitCommonControlsEx是实际注册控件窗口类的函数.它根据参数lpInitCtrls->dwICC的内容类决定调用哪些控件的注册代码.相关的值如下:

    #define ICC_LISTVIEW_CLASSES 0x00000001 // listview, header
    #define ICC_TREEVIEW_CLASSES 0x00000002 // treeview, tooltips
    #define ICC_BAR_CLASSES 0x00000004 // toolbar, statusbar, trackbar, tooltips
    #define ICC_TAB_CLASSES 0x00000008 // tab, tooltips
    #define ICC_UPDOWN_CLASS 0x00000010 // updown
    #define ICC_PROGRESS_CLASS 0x00000020 // progress
    #define ICC_HOTKEY_CLASS 0x00000040 // hotkey
    #define ICC_ANIMATE_CLASS 0x00000080 // animate
    #define ICC_WIN95_CLASSES 0x000000FF
    #define ICC_DATE_CLASSES 0x00000100 // month picker, date picker, time picker, updown
    #define ICC_USEREX_CLASSES 0x00000200 // comboex
    #define ICC_COOL_CLASSES 0x00000400 // rebar (coolbar) control

    #define ICC_INTERNET_CLASSES 0x00000800
    #define ICC_PAGESCROLLER_CLASS 0x00001000 // page scroller
    #define ICC_NATIVEFNTCTL_CLASS 0x00002000 // native font control
    注意到ICC_WIN95_CLASSES等于之前所有值的或,因此使用该标记调用InitCommonControlsEx会初始化listview,header,treeview等控件.

    进程初次加载dll时,系统会以DLL_PROCESS_ATTACH参数调用DLLMain. 在动态库comctl32.dll中,会在这时候用ICC_WIN95_CLASSES标记调用InitCommonControlsEx, 因此进程一旦加载了comctl32.dll,就注册了一系列的通用控件.

    进程最后一次卸载dll时,系统会以DLL_PROCESS_DETACH参数调用DLLMain. 在动态库comctl32.dll中,会在这时候调用UnregisterClass取消所有已经册过的通用控件窗口类.

    注意:
    对Windows 95/98/Me来说,dll卸载的时候,在其中注册的所有窗口类会自动取消注册.这是自动进行的,并不需要你写下UnregisterClass的调用代码.
    对Windows NT/2000/XP来说,当dll卸载的时候,在该dll中注册的窗口类并不会自动取消注册,因此必须在DllMain中人为的用代码调用unregisterClass. 否则一旦dll卸载后再次创建控件(因为没有反注册,系统认为窗口类仍有效),则该控件的窗口过程将指向无效的地址.


    MFC中通用控件的初始化

    MFC中采用了延迟加载的办法来初始化通用控件.这样,如果程序不使用任何通用控件,则不会加载comctl32.dll.如果使用了任何通用控件,则会在该控件的PreCreateWindow函数中初始化对应的通用控件.这就是使用depends工具查看一个使用了通用控件的MFC程序,一般都看不到有comctl32.dll存在的原因.这里是说一般,如果在你的代码中直接调用了两个初始化函数之一,就会正常链接到comctl32.dll。

    要在MFC源代码中找到通用控件初始化的地方很简单,只要看看一个使用了通用控件的程序何时加载comctl32.dll就可以了.你可以调试这样一个程序,单步执行或每隔一段代码设置一个断点,然后每次执行后用工具看看exe是否加载了comctl32.dll模块,如此逐步缩小范围,很快就可以找到.查看exe在运行时包含模块的工具很多,比如IceSword或者windows优化大师带的一个进程管理工具都可以.

    以控件syslistview32为例.MFC的包装类是CListView.

    BOOL CListView::PreCreateWindow(CREATESTRUCT& cs)
    {
        return CCtrlView::PreCreateWindow(cs);
    }

    BOOL CCtrlView::PreCreateWindow(CREATESTRUCT& cs)
    {
        ...

        // initialize common controls
        VERIFY(AfxDeferRegisterClass(AFX_WNDCOMMCTLS_REG));
        AfxDeferRegisterClass(AFX_WNDCOMMCTLSNEW_REG);
        ...

        return CView::PreCreateWindow(cs);
    }
    AfxDeferRegisterClass会根据控件的种类不同,设置不同的参数,然后调用_AfxInitCommonControls,比如下面的代码:
     

    init.dwICC = ICC_WIN95_CLASSES;
    fRegisteredClasses |= _AfxInitCommonControls(&init, AFX_WIN95CTLS_MASK);

    函数_AfxInitCommonControls完成实际的通用控件初始化.

    LONG AFXAPI _AfxInitCommonControls(LPINITCOMMONCONTROLSEX lpInitCtrls, LONG fToRegister)
    {
        ...
        HINSTANCE hInst = ::LoadLibraryA("COMCTL32.DLL");
        ...
        (FARPROC&)pfnInit = ::GetProcAddress(hInst, "InitCommonControlsEx");

        if (pfnInit == NULL)
        {
            ...
        }
        else if (InitCommonControlsEx(lpInitCtrls))
        {
            ...
        }

        FreeLibrary(hInst);
        ...
    }

    这里要说明一下,LoadLibrary和FreeLibrary成对出现,保持了对dll的引用计数不变.只有当对dll的引用计数从0变为1时,才会以DLL_PROCESS_ATTACH调用DLLMain函数,只有dll的引用计数从1变为0时,才会以DLL_PROCESS_DETACH调用DLLMain函数.上面的代码中,除了加载卸载DLL外,还有一次对dll中函数InitCommonControlsEx的调用,正是这个调用引起了系统额外的一次LoadLibrary的调用(注意,如前所述,MFC对这个DLL使用了延迟加载技术,因此,dll不会在程序一启动就被加载,而是延迟到第一次访问dll中的任何函数或数据).这个额外的调用使得对通用控件的注册并没有被后面的FreeLibrary取消.因此,程序在这以后就可以生成通用控件的窗口了.

    至于,MFC为何时而采用直接调用,时而采用取函数地址的方法调用InitCommonControlsEx,这个在函数的注释里说的很清楚,是为了适应各种不同的链接选项而已.


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/2/16 17:30:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2025/7/18 16:30:45

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    46.875ms