这是我在阅读某源代码时无意中想到的一个问题,进行了一番研究,现在把结果贴出来,希望对感兴趣的人能有所帮助。
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卸载后再次创建控件(因为没有反注册,系统认为窗口类仍有效),则该控件的窗口过程将指向无效的地址.