Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1804350
  • 博文数量: 290
  • 博客积分: 10653
  • 博客等级: 上将
  • 技术积分: 3178
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 23:08
文章存档

2013年(6)

2012年(15)

2011年(25)

2010年(86)

2009年(52)

2008年(66)

2007年(40)

分类:

2008-01-10 00:54:37

Tutorial 18: Common Controls

第十八课:通用控件


We will learn what common controls are and how to use them. This tutorial will be a quick introduction to them only.

我们将学习什么是通用控件,如何应用通用控件。这一课仅仅是快速的介绍它们。
Download the example source code here.

Theory:

原理:

Windows 95 comes with several user-interface enhancements over Windows 3.1x. They make the GUI richer. Several of them are in widely used before Windows 95 hit the shelf, such as status bar, toolbars etc. Programmers have to code them themselves. Now Microsoft has included them with Windows 9x and NT. We will learn about them here.

Windows95比起windows3.1x来提供了几个加强的用户界面控件。他们让Gui更富有。他们中的几个在windows95架构之前就被使用,例如:状态栏,工具栏等等。程序员必须为它们编码才能使用它们。现在windows把它们包含在windows9xwindowsNt中。我们将在这儿学习它们。
These are the new controls:

这些是新控件:

  • Toolbar  工具栏
  • Tooltip  提示文本
  • Status bar  状态栏
  • Property sheet 属性表
  • Property page 属性页
  • Tree view    树视图  (如:目录树)
  • List view    列表视图
  • Animation    动画
  • Drag list    拖动列表框
  • Header       标题
  • Hot-key    热键
  • Image list 图像列表
  • Progress bar 进度条
  • Right edit
  • Tab    标签 工作表选项卡
  • Trackbar 跟踪条
  • Up-down 滚动条

Since there are many of them, loading them all into memory and registering them would be a waste of resource. All of them, with the exception of rich edit control, are stored in comctl32.dll with applications can load when they want to use the controls. Rich edit control resides in its own dll, richedXX.dll, because it's very complicated and hence larger than its brethren.

 

因为通用控件很多,把所有的控件载入内存并注册它们将造成资源的浪费。除了编辑框控件,其它所有的控件都储存在comctl32.dll中,应用程序能在用到这些控件时再装载它们。编辑框控件驻留在它自己的dll文件richedxx.dll中,

因为这个控件是非常复杂的,所有它也比其它的兄弟控件大很多。


You can load comctl32.dll by including a call to InitCommonControls in your program. InitCommonControls is a function in comctl32.dll, so referring to it anywhere in your code will make PE loader load comctl32.dll when your program runs.You don't have to execute it, just include it in your code somewhere. This function does NOTHING! Its only instruction is "ret". Its sole purpose is to include reference to comctl32.dll in the import section so that PE loader will load it whenever the program is loaded. The real workhorse is the DLL entrypoint function which registers all common control classes when the dll is loaded. Common controls are created based on those classes just like other child window controls such as edit, listbox etc.

 

你能通过在程序中调用InitCommonControls函数,来把comctl32.dll模块装入使用。InitCommonControlscomctl32.dll中的一个函数,所以无论你在代码的什么地方引用它,当你的程序运行时,都将使PE装载器加载comctl32.dll文件。你并不是执行它(InitCommonControls函数),仅仅是把它包含在程序代码中。这个函数不做任何事情!它仅仅是一条ret指令。它唯一的目的是包含一个comctl32.dll的说明信息在输入节区中,这样,无论程序何时被装入,PE装载器都载入comctl32.dll文件。而真正工作的是dll文件的入口函数,当dll文件被装载时,入口函数注册所有的通用控件类。通用控件就像其它的子窗口控件(editlistbox等等)一样,也是基于这些控件类创建的。


Rich edit is another matter entirely. If you want to use it, you have to call LoadLibrary to load it explicitly and call FreeLibrary to unload it.

 

Rich edit编辑控件完全不同于通用控件。如果你想使用它,你必须调用LoadLibrary函数明确地装载它。在不需要的时候,你还要调用FreeLibrary函数来卸载它

 

 

 


Now we learn how to create them. You can use a resource editor to incorporate them into dialog boxes or you can create them yourself. Nearly all common controls are created by calling CreateWindowEx or CreateWindow, passing it the name of the control class. Some common controls have specific creation functions , however, they are just wrappers around CreateWindowEx to make it easier to create those controls. Existing specific creation functions are listed below:

现在我们学习如何创建它们。你可以用资源编辑器来把它们并入到对话框中,也可以自己创建它们。几乎创建所有通用控件的方法,都是把控件名作为参数传递给CreateWindowExCreateWindow函数并调用这两个函数。一些通用控件有它们自己的创建函数,然而,它们仅仅是对CreateWindowEx函数的包装以便让它更容易地创建这些控件。现有指定的创建函数列表如下:

 

  • CreateToolbarEx
  • CreateStatusWindow
  • CreatePropertySheetPage
  • PropertySheet
  • ImageList_Create

In order to create common controls, you have to know their class names. They are listed below:

为了创建通用控件,你必须知道它们的类名.类列表如下:


 

Class Name

类名

Common Control

通用控件

ToolbarWindow32

Toolbar

tooltips_class32

Tooltip

msctls_statusbar32

Status bar

SysTreeView32

Tree view

SysListView32

List view

SysAnimate32

Animation

SysHeader32

Header

msctls_hotkey32

Hot-key

msctls_progress32

Progress bar

RICHEDIT

Rich edit

msctls_updown32

Up-down

SysTabControl32

Tab

 

Property sheets and property pages and image list control have their own specific creation functions. Drag list control are souped-up listbox so it doesn't have its own class. The above class names are verified by checking resource script generated by Visual C++ resource editor. They differ from the class names listed by Borland's win32 api reference and Charles Petzold's Programming Windows 95. The above list is the accurate one.

Property sheets property pages image list 控件有它们自己指定的创建函数。Drag list 控件其实是可以拖拉的Listbox 控件,所以它不需要有它自己类。要验证上面的这些类名,可以通过检查Visual C++资源编辑器产生资源脚本。它们的类名和borland公司的win32手册还有charles petzolds 的《Programming Windows 95》所列出的类名有不同之处。上面的类列表是精确的。


Those common controls can use general window styles such as WS_CHILD etc. They also have their own specific styles such as TVS_XXXXX for tree view control, LVS_xxxx for list view control, etc. Win32 api reference is your best friend in this regard.

这些通用控件可以使用所有的窗口样式例如:WS_CHILD等等。它们也有它们自己特有的样式例如:tree View 控件的TVS_XXXX ,list view 控件的LVS_XXXX。等等。在这点上win32API手册是你最好的朋友。
Now that we know how to create common controls, we can move on to communication method between common controls and their parent. Unlike child window controls, common controls don't communicate with the parent via WM_COMMAND. Instead they send WM_NOTIFY messages to the parent window when some interesting events occur with the common controls. The parent can control the children by sending messages to them. There are also many new messages for those new controls. You should consult your win32 api reference for more detail.

既然我们知道了如何创建一个窗口,我们就可以往前走到通用控件和它们父窗口的通讯方法上。通用窗口并不像子窗口控件一样用WM_COMMAND消息来和父窗口通讯。当在通用控件上那些令人感兴趣的事件发生时,通用控件发送WM_NOTIFY消息给它们的父窗口。通过发送消息给通用控件,这个父亲就能够控制它的孩子们。对于这些新的控件,这里有很多新的消息。至于更多的细节你应该参考你的Win32 API手册。


Let's examine progress bar and status bar controls in the following example.

在下面的例子中,让我们来分析进度条和状态栏控件。

Sample code:

.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comctl32.inc
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD

.const
IDC_PROGRESS equ 1            ; control IDs
IDC_STATUS equ 2
IDC_TIMER  equ 3

.data
ClassName  db "CommonControlWinClass",0
AppName    db "Common Control Demo",0
ProgressClass  db "msctls_progress32",0       ; the class name of the progress bar
Message  db "Finished!",0
TimerID  dd 0

.data?
hInstance  HINSTANCE ?
hwndProgress dd ?
hwndStatus dd ?
CurrentStep dd ?
.code
start:
    invoke GetModuleHandle, NULL
    mov    hInstance,eax
    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax
    invoke InitCommonControls

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    LOCAL wc:WNDCLASSEX
    LOCAL msg:MSG
    LOCAL hwnd:HWND

    mov   wc.cbSize,SIZEOF WNDCLASSEX
    mov   wc.style, CS_HREDRAW or CS_VREDRAW
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInst
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_APPWORKSPACE
    mov   wc.lpszMenuName,NULL
    mov   wc.lpszClassName,OFFSET ClassName
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov   wc.hIcon,eax
    mov   wc.hIconSm,eax
    invoke LoadCursor,NULL,IDC_ARROW
    mov   wc.hCursor,eax
    invoke RegisterClassEx, addr wc
    invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
    mov   hwnd,eax
    .while TRUE
         invoke GetMessage, ADDR msg,NULL,0,0
        .BREAK .IF (!eax)
        invoke TranslateMessage, ADDR msg
        invoke DispatchMessage, ADDR msg
    .endw
    mov eax,msg.wParam
    ret
WinMain endp

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg==WM_CREATE
         invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
            WS_CHILD+WS_VISIBLE,100,\
            200,300,20,hWnd,IDC_PROGRESS,\
            hInstance,NULL
        mov hwndProgress,eax
        mov eax,1000               ; the lParam of PBM_SETRANGE message contains the range
        mov CurrentStep,eax
        shl eax,16                   ; the high range is in the high word
        invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
        invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0
        invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
        mov hwndStatus,eax
        invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; create a timer
        mov TimerID,eax
    .elseif uMsg==WM_DESTROY
        invoke PostQuitMessage,NULL
        .if TimerID!=0
            invoke KillTimer,hWnd,TimerID
        .endif
    .elseif uMsg==WM_TIMER        ; when a timer event occurs
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0    ; step up the progress in the progress bar
        sub CurrentStep,10
        .if CurrentStep==0
            invoke KillTimer,hWnd,TimerID
            mov TimerID,0
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
            invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
            invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
        .endif
    .else
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .endif
    xor eax,eax
    ret
WndProc endp
end start

Analysis:

分析:

    invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
    invoke ExitProcess,eax
    invoke InitCommonControls

I deliberately put InitCommonControls after ExitProcess to demonstrate that InitCommonControls is just there for putting a reference to comctl32.dll in the import section. As you can see, the common controls work even if InitCommonControls doesn't execute.

我故意把InitCommonControls放在ExitProcess之后来证明那个InitCommonControls函数在这里仅仅是放置comctl32.dll的说明信息在输入节区中。如你所见,即使是InitCommonControls不执行,通用控件也能正常工作。

 

.if uMsg==WM_CREATE
         invoke CreateWindowEx,NULL,ADDR ProgressClass,NULL,\
            WS_CHILD+WS_VISIBLE,100,\
            200,300,20,hWnd,IDC_PROGRESS,\
            hInstance,NULL
        mov hwndProgress,eax

 

Here is where we create the common control. Note that this CreateWindowEx call contains hWnd as the parent window handle. It also specifies a control ID for identifying this control. However, since we have the control's window handle, this ID is not used. All child window controls must have WS_CHILD style.

 

这儿是我们创建通用控件的地方。注意CreateWindowEx函数中的参数hWnd是父窗口的句柄。为了识别一个控件,这个函数也指定了一个控件的ID号。然而,因为我们有了这个控件的窗口句柄,这个控件ID就没有被使用。另外,所有的子窗口控件必须有WS_CHILD样式。

 

        mov eax,1000
        mov CurrentStep,eax
        shl eax,16
        invoke SendMessage,hwndProgress,PBM_SETRANGE,0,eax
        invoke SendMessage,hwndProgress,PBM_SETSTEP,10,0

 

After the progress bar is created, we can set its range. The default range is from 0 to 100. If you are not satisfied with it, you can specify your own range with PBM_SETRANGE message. lParam of this message contains the range, the maximum range is in the high word and the minimum one is in the low word. You can specify how much a step takes by using PBM_SETSTEP message. The example sets it to 10 which means that when you send a PBM_STEPIT message to the progress bar, the progress indicator will rise by 10. You can also set your own indicator level by sending PBM_SETPOS messages. This message gives you tighter control over the progress bar.

在进度条被创建之后,我们能设置它的范围。默认的范围是0-100.如果你对这个值并不满意,你能在传递PBM_SETRANGE消息的时候指定你想要的范围。消息的lParam包含这个范围,最大的范围值在高字节中,最小的范围值在低字节中。你通过使用PBM_SETSTEP消息,可以指定进度条移动一格的步长。例子中设置它的值为10,这意味着当你发送PBM_STEPIT消息给进度条的时候,这个进度条的指示器将以10为单位增长。你也能通过发送PBM_SETPOS消息来设置你自己的指示器位置。这个消息能让你更好的控制进度条。

 

        invoke CreateStatusWindow,WS_CHILD+WS_VISIBLE,NULL,hWnd,IDC_STATUS
        mov hwndStatus,eax
        invoke SetTimer,hWnd,IDC_TIMER,100,NULL        ; create a timer
        mov TimerID,eax

 

Next, we create a status bar by calling CreateStatusWindow. This call is easy to understand so I'll not comment on it. After the status window is created, we create a timer. In this example, we will update the progress bar at a regular interval of 100 ms so we must create a timer control. Below is the function prototype of SetTimer.

 

接着,我们调用CreateStatusWindow函数创建一个状态栏,这个调用是容易理解的所以我并不谈论它。在状态栏窗口被创建后,我们创建了一个定时器。在这个例子中,我们每过100毫秒就更新一次进度条,所以我们必须创建一个定时器控件。

下面是SetTimer函数的原型:

 

SetTimer PROTO hWnd:DWORD, TimerID:DWORD, TimeInterval:DWORD, lpTimerProc:DWORD

 

hWnd : Parent window handle

hWnd:父窗口句柄。


TimerID : a nonzero timer identifier. You can create your own identifier.

TimerID:一个非零的定时器标识符。你能创建你特有的标识符。


TimerInterval : the timer interval in milliseconds that must pass before the timer calls the timer procedure or sends a WM_TIMER message

TimerInterval:以毫秒为单位的时间间隔,它必须在定时器过程调用或者是发送WM_TIMER之前传送这个参数给定时器。


lpTimerProc : the address of the timer function that will be called when the time interval expires. If this parameter is NULL, the timer will send WM_TIMER message to the parent window instead.

LpTimerProc:定时器过程的地址。当定时器的时间间隔到达时,这个函数将被调用。如果这个参数值为空(NULL),定时器将发送WM_TIMER消息来代替这个过程。

If this call is successful, it will return the TimerID. If it failed, it returns 0. This is why the timer identifer must be a nonzero value.

如果调用成功,它返回定时器的ID。如果失败,它返回0.这就是为什么定时器标识必须是非0值的原因。

 

.elseif uMsg==WM_TIMER
        invoke SendMessage,hwndProgress,PBM_STEPIT,0,0
        sub CurrentStep,10
        .if CurrentStep==0
            invoke KillTimer,hWnd,TimerID
            mov TimerID,0
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,addr Message
            invoke MessageBox,hWnd,addr Message,addr AppName,MB_OK+MB_ICONINFORMATION
            invoke SendMessage,hwndStatus,SB_SETTEXT,0,0
            invoke SendMessage,hwndProgress,PBM_SETPOS,0,0
        .endif

 

When the specified time interval expires, the timer sends a WM_TIMER message. You will put your code that will be executed here. In this example, we update the progress bar and then check if the maximum limit has been reached. If it has, we kill the timer and then set the text in the status window with SB_SETTEXT message. A message box is displayed and when the user clicks OK, we clear the text in the status bar and the progress bar.

当指定的时间间隔到达时,定时器发送一个WM_TIMER消息。你可以将你想执行的代码放在这里。在这个例子中,我们更新这个进度条,然后检查它的最大上限是否到达。如果达到上限,我们发送SB_SETTEXT消息从而在状态栏中设置一文本。当用户点击OK时,一个消息框被弹出,我们清除状态栏中的文本和进度条。


This article come from Iczelion's asm page

风向改变翻译于2008-1-9

 

阅读(3180) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~