|
4.3 用户界面绘制 |
|
军师,刚才说到用户界面有很多控件是怎么回事?
Windows通用控件,如按钮、编辑框、列表框等,都是由Windows操作系统提供的。
哦,不就是俺说的那些按钮嘛。
对,并且这些控件对象都是可编程的,可以使用Visual C++ 2008提供的对话框编辑器把它们添加到对话框中。本书大部分实例的UI编程方法是基于传统编程法和交互式方法的综合方法。在消息和事件响应方面用传统编程法,代码简洁且容易理解(使用交互式方法编程较难理解Windows的消息机制)。在UI设计方面使用交互式方法实现UI界面,可大大减轻编程的工作量。
1.Windows通用控件总览
控件是一些行为标准化了的窗口,可用于在对话框或其他窗口中充当与用户交互的元素。在Visual C++ 2008中,工具箱的对话框编辑器列出了提供的Windows通用控件,如图4.3所示。
图4.3
其中,经常用到的以及本书中会用到的控件,将在本节进行介绍。
2.创建控件
创建控件时,必须先建立一个控件容器,一般是对话框,然后从工具箱中的对话框编辑器中拖出所需控件,放到对话框的适当位置,然后对控件进行调整、设置等。创建对话框之前要先建立一个应用程序项目,具体方法如下。
1)创建工程项目
依次单击“文件”→“新建”→“项目”命令,在弹出的对话框中选择“Win32项目”,确定后在应用程序设置向导中单击“下一步”按钮,在“应用程序类型”栏中选中“Windows应用程序”单选按钮,如图4.4所示,选中“空项目”复选框,然后单击“完成”按钮,完成项目的创建。
图4.4
2)添加对话框资源
项目创建好之后,依次单击菜单栏中的“项目”→“添加资源”命令,在弹出的“添加资源”对话框中选择“Dialog”选项,然后单击“新建”按钮,添加一个新的对话框,如图4.5所示。
图4.5
对话框资源创建完成后,可修改其ID值,以方便使用。对话框创建完成后,还需要在代码中利用DialogBox()函数“激活”,才能使用。
DialogBox()函数声明如下:
功能: 根据对话框模板资源创建一个模态的对话框。
参数:
hlnstance,标识一个模块的实例,该模块的可执行文件含有对话框模板。
IpTemplate,标识对话框模板。此参数可以是指向一个以NULL结尾的字符串的指针,该字符串指定对话框模板名,或是指定对话框模板的资源标识符中的一个整型值。如果此参数指定了一个资源标识符,则它的高位字一定为零,且低位字一定含有标识符。注意,必须用MAKEINTRESOURDE宏指令创建此值。
hWndParent,指定拥有对话框的窗口。
IpDialogFunc,指向对话框过程的指针。
返回值: 函数执行成功,将会填充nResult参数,否则返回值为-1。
当要中止对话框时,要调用EndDialog()函数,该函数声明如下:
功能: 用于清除一个模态对话框,并使系统中止对对话框的任何处理。
参数:
hDlg表示要被清除的对话框窗口句柄。
nResult,指定从创建对话框函数返回到应用程序的值。
返回值: 函数执行成功返回非零值,否则返回零。
3.按钮控件
按钮是指可以响应鼠标单击的矩形子窗口。它经常出现在对话框、工具栏或者是其他包含控件的窗口中。按钮控件是Windows系统中可使用的最灵活的控件之一。
使用按钮控件的方法是:选择工具箱,在对话框编辑器中将按钮控件(Button)拖拽到对话框中,调整适当的位置和大小(如图4.6所示)。然后,在按钮属性中更改标题(Caption)和ID值(如图4.7所示),以方便在代码中使用控件。
图4.6
图4.7
这UI的设计跟俺平时用“画图”画小王八差不多。
你这厮,不过这就是这种设计的方便之处。
单击按钮后,会向父窗口发出WM_COMMAND消息,消息的wParam和lParam参数指定了发送消息的控件和促使消息发送的动作。消息的wParam参数的高16位包含消息通知码,低16位包含了控件ID号,而lParam参数则指定了控件的窗口句柄。处理消息的代码一般在上文提到的WM_COMMAND里响应,使用方法如下:
4.编辑框控件
编辑框是一个矩形控制窗口,一般在对话框中,允许用户从键盘输入并编辑文字。编辑框是程序接受用户字符输入的一种主要手段,输入的内容存放在其父窗口有限容量(32KB)的局部堆里。
编辑框可以是单行的也可以是多行的。在多行的条件下,还可以设置是否有滚动条、是否为只读模式(禁用用户输入或编辑)、是否设置为密码形式以隐藏用户的输入等。它具有嵌入编辑功能,可以作为简单的文本编辑器使用。
使用编辑框控件的方法是:选择工具箱,在对话框编辑器中将编辑框控件(Edit Control)拖拽到对话框中,调整适当位置和大小(如图4.8所示)。然后,在其属性中设置ID值和其他属性值等(如图4.9所示),方便在代码中使用控件。
图4.8
图4.9
用这个东西就可以向程序里输入数据?
对啊。不光可以输入,还可以输出。
这个控件好像QQ聊天窗口里面那个啊。有了这个,俺岂不是可以自己写一个QQ。
还差得远呢。
使用编辑框控件最常用到的是SetDlgItemText()函数和GetDlgItemText()函数,这两个函数的声明如下。
(1)SetDlgItemText()函数
功能: 设置编辑框的文本显示。
参数:
hDlg,指定含有控件的对话框句柄。
nlDDlgItem,标识带有将被设置的标题或文本的控件ID。
lpString,指向一个以NULL结尾的字符串的指针,该指针指定了将被复制到控件的文本内容。
返回值: 函数执行成功则返回非零值,否则返回零值。若想获得更多的错误信息,可调用GetLastError()函数进行查看。
(2)GetDlgItemText()函数
功能: 获取编辑框的文本内容,保存到缓冲区。
参数:
hDlg,指定含有控件的对话框句柄。
nIDDlgItem,标识带有将被设置的标题或文本的控件ID。
lpString,指向用于接收结果的缓冲区的指针。
nMaxCount,指定允许复制到上述缓冲区中的最大字符数(TCHAR字符类型),如果字符串的长度(包含结束空字符)大于指定的最大字符数,则会被截断。
返回值: 函数执行成功,返回复制到缓冲区中的TCHAR字符个数(不包括结束空字符),否则返回0。要获取更多错误信息,可调用GetLastError()函数查看。
5.静态文本和分组框
静态文本和分组框提供一种简单的方式,在对话框或其他窗口中显示文本。静态文本一般作为其他控件的标签或者提示用语,分组框含有矩形可以将一些控件包含在里面作为分类,如图4.10所示。
图4.10
使用静态文本和分组框的方法是:选择工具箱,在对话框编辑器中将对应控件(Static Text和Group Box)拖拽到对话框中,调整到适当的位置和大小,然后,在其属性中设置标题或其他属性,其中,标题即为要显示的文本。
6.列表框和组合框控件
列表框提供一个可供用户选择的列类,用户可以一次选择一个项目,也可以同时选中多个项目。组合框由一个列表框和一个编辑控件组成。列表框部分可以是一直显示的,也可以是隐藏的。用户单击编辑控件边上的下拉按钮,可打开该组合框。组合框可以让用户自己输入文本,也可以用户选择列表中的某项作为输入,一般采用后者,如图4.11所示。
图4.11
使用这两个控件的方法是:选择工具箱,在对话框编辑器中将控件(List Box和Combo Box)拖拽到对话框中,调整适当位置和大小。然后,在其属性中设置ID值或其他属性值等,方便在代码中使用控件。
使用该控件,经常用到的方法是利用SendMessage()发送消息给列表框,其主要消息如下。
●LB_ADDSTRING消息,该消息向列表框中添加字符串,如果列表框不包含LBS_SORT风格,字符串将被添加到列表的末尾;如果包含,字符串将被插入到列表框,列表会重新排列。消息的lParam参数指明字符串的首地址。
●LB_INSERTSTRING消息,该消息插入一个字符串到列表框。与LB_ADDSTRING消息不同,该消息不会促使具有LBS_SORT风格的列表框重新排序。消息的wParam参数指定插入的位置,它是一个从0开始的索引。lParam参数指明要插入的字符串。
●LB_DELETESTRING消息,该消息在列表框中删除一个字符串,消息的wParam参数指明要删除项的索引,该索引从0开始。
●LB_RESETCONTENT消息,该消息删除列表框中的所有项。
●LB_GETCOUNT消息,该消息取得列表中项的总数。
●LB_GETCURSEL消息,该消息取得用户当前选择项的索引,该索引从0开始。
7.列表视图
列表视图控件是一种允许以多种方式显示信息的灵活控件。它能成列地显示数据,是对传统列表框的重大改进。显示信息的方式有多种,分类如表4.2所示。
表4.2
该控件经常用到的方式是报表视图(本书中的实例都是使用报表视图的),因此这里只介绍报表视图的使用方式。
使用列表视图控件的方法是:选择工具箱,在对话框编辑器中将列表视图控件(List Control)拖拽到对话框中,调整到适当的位置和大小(如图4.12所示)。然后在按钮属性中选择视图(View)为“Report”方式并设置其ID值(如图4.13所示),方便在代码中使用控件。
图4.12
图4.13
(1)列表视图的分栏
列表视图控件首先将它所占用的空间在水平方向上分成若干个专栏(column),每个栏的属性用LVCOLUMN结构来描述。当要添加新专栏时,要先恰当地设置此结构中各域的值,然后向列表视图窗口发送VM_INSERTCOLUMN消息,消息的lParam参数指定了LVCOLUMN结构地址,wParam参数指定了要添加的专栏号,代码如下所示:
LVCOLUMN结构中的mask是一组标志位,它指示了该结构中哪些成员变量是有效的,只有指定了一个成员有效,Windows在收到LVM_INSERTCOLUMN消息后,才会去查看这个成员的值。
(2)在列表视图中添加项
专栏设置完成后,列表视图控件又在垂直方向上分了许多的项,每一行就是一项,其属性用LVITEM结构描述。LVM_INSERTITEM消息用来向列表视图中插入新项,消息的wParam参数指定了要添加项的序号,lParam参数指定了LVITEM结构的首地址。添加新项的代码如下所示:
(3)设置项文本
添加新项后,如要设置该项中各子项的文本,可在上面的基础上用LV M_SETITEMTEXT消息实现。代码如下所示:
(4)响应列表视图消息
当有事件发生时,该控件向父窗口发送WM_NOTIFY消息,消息的wParam参数指定了控件的ID号,lParam参数是一个指向NMHDR结构的指针。
对于通知消息来说,这个参数指向了一个较大的结构,NMHDR结构是这个结构的第一个成员。对于列表视图控件,lParam参数指向NMLISTVIEW结构。程序接收到WM_NOTIFY消息之后,查看wParam参数记录的控件ID号,如果是列表视图控件发来的,就通过以下代码处理它:
至此,已经对常用的控件进行了介绍,接下来通过一个例子演示控件的使用方法。
军师,你说的这些控件,俺在平时用其他软件的时候都看到过,每次看到这样的界面,俺就会想,为啥俺的程序就只能是黑框框啊。
哈哈,现在你也可以设计跟其他软件一样华丽的界面了。
将军,您毕竟是来学习黑客编程的,所以今天我还会教给你一个与黑客有关的小工具。
好好,哈哈,想不到俺这么快就要成为黑客了。
哎,会写个小工具就这么自以为是了,你还差得远呢。
今天我教你写一个星号密码查看器。Windows把Edit Control的Password属性设为TRUE来提高用户输入的密码的安全性,但是这种窗口密码方式并不安全。例如,在Windows 2000以前,可以使用向远程进程的密码编辑框发送WM_GETTEXT消息的方法直接获得密码,在Windows 2000以后,已经不允许跨进程的密码读取了,但是黑客仍然可以通过使用将代码注入到远程进程的方法获得密码。
这么麻烦,这些“注入”、“远程进程”的概念俺都不知道啊,这咋办?
其实,这次的星号密码查看器不用这么复杂的东西也可以获得密码,方法就是通过改变远程窗口密码编辑框的Password属性,直接显示星号密码。
当前进程通过响应WM_MOUSEMOVE消息来获取鼠标指针的位置,然后根据获得的坐标得到被激活窗口的窗口句柄,并得到窗口的类名称和窗口风格。最后判断是不是密码框风格,如果是,则对此控件所在的远程进程(本章也演示了当前进程密码框的情况)发送EM_SETPASSWORDCHAR消息,即取消此密码框的Password属性,这样,密码就会以明文显示出来。
这么简单,就是通过发送一个消息让控件把密码显示出来。这控件也太傻了吧,让它显示它就显示。
首先新建一个空的Windows应用程序类型的Win32项目,项目名为“Psw-Spy”。为该项目添加一个对话框资源,并在该对话框中绘制如下UI,对大小和位置进行调整,如图4.14所示。
图4.14
各控件的属性设置如表4.3所示。
表4.3
为对话框属性中的“Center”属性选择“TRUE”,使对话框居中,否则对话框会显示在左上角;为编辑控件的“Password”属性选择“TRUE”,使编辑框以星号形式显示。