📄 031.txt
字号:
pWin32_Find_Data 是WIN32_FIND_DATA 型的结构体变量的地址,WIN32_FIND_DATA型的结构体变量将用来保存返回的文件的信息。
如果没有找到匹配的文件,该函数将在eax中返回INVALID_HANDLE_VALUE 。否则将返回一个搜索句柄,您可以用该句柄在FindNextFile函数中来搜索下一个符合条件的文件。
.if eax!=INVALID_HANDLE_VALUE
mov FHandle,eax
xor edi,edi
如果找到了一个文件,我们在一个变量中保存搜索句柄,并把寄存器edi清零,该寄存器将用作项目的索引号。
.while eax!=0
test finddata.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
.if ZERO?
在本课中,我们将不处理文件夹,所以我们检查dwFileAttributes成员变量的值是否有FILE_ATTRIBUTE_DIRECTORY 标志,如果有,我们就忽略掉它,然后调用FindNextFile。
invoke ShowFileInfo,edi, addr finddata
inc edi
.endif
invoke FindNextFile,FHandle,addr finddata
.endw
我们调用ShowFileInfo函数包文件的名称和大小信息加到列表视图控件中去。然后让edi寄存器加一来增加项目的行号。最后我们调用FindNextFile函数在当前文件夹中继续搜索文件一直到该函数返回0为止(这意味着没有可供搜索的文件了)。
invoke FindClose,FHandle
.endif
ret
FillFileInfo endp
当前文件夹中的文件枚举完毕后,我们必须关闭搜索句柄。
先在我们看一下ShowFileInfo函数。该函数由两个参数,一个是项目的索引号(也即行号),另一个是WIN32_FIND_DATA型结构体变量的地址。
ShowFileInfo proc uses edi row:DWORD, lpFind:DWORD
LOCAL lvi:LV_ITEM
LOCAL buffer[20]:BYTE
mov edi,lpFind
assume edi:ptr WIN32_FIND_DATA
把WIN32_FIND_DATA 型结构体变量的值放到寄存器edi中。
mov lvi.imask,LVIF_TEXT+LVIF_PARAM
push row
pop lvi.iItem
mov lvi.iSubItem,0
我们将传递项目的名称和lParam的值,所以我们在iMask中放入标志位LVIF_TEXT 和LVIF_PARAM。接下来我们在iItem中放入传递进来的行号,另外由于这是主项目我们必须设置iSubItem的值等于0。
lea eax,[edi].cFileName
mov lvi.pszText,eax
push row
pop lvi.lParam
我们现在要把标签字符串的地址,在这里也就是WIN32_FIND_DATA 型结构体变量中的文件的名称放到pszText中。由于我们要完成对项目的排序,所以必须设置lParam的值,我把它设成行号值,这样我们可以根据索引值来查询项目。
invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
设置完所有LV_ITEM型变量中的值后,我们发送LVM_INSERTITEM消息给列表视图控件来把项目插入到其中。
mov lvi.imask,LVIF_TEXT
inc lvi.iSubItem
invoke wsprintf,addr buffer, addr template,[edi].nFileSizeLow
lea eax,buffer
mov lvi.pszText,eax
我们将把子项目插入到第二列。一个子项目只能有一个标签。这样我们在iMask中指定LVIF_TEXT标志位。接着我们指定子项目所在的列,本例中我们通过将iSubItem加一使得该值等于1。标签值是文件的大小,为了转换成文本我们调用wsprintf函数,然后把文本的地址放到pszText中去。
invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
assume edi:nothing
ret
ShowFileInfo endp
当LV_ITEM型变量中的值设定好之后,我们向列表视图控件发送LVM_SETITEM消息,并一同把LV_ITEM变量的地址传过去。注意:发送的消息是LV_ITEM而不是LVM_INSERTITEM,因为我们插入的是子项目,子项目不是真正的项目而是主项目的属性。所以我们这时是在设定项目的属性,而不是加入一个项目。
当所有的项目都插入到列表视图控件后,我们设定它的文本和背景颜色。
RGB 255,255,255
invoke SendMessage,hList,LVM_SETTEXTCOLOR,0,eax
RGB 0,0,0
invoke SendMessage,hList,LVM_SETBKCOLOR,0,eax
RGB 0,0,0
invoke SendMessage,hList,LVM_SETTEXTBKCOLOR,0,eax
我们用RGB(R---Red G---Green B---Blue)来把三色转换并放到eax中。我们通过发送LVM_SETTEXTCOLOR 和 LVM_SETTEXTBKCOLOR 消息来设定文本的前景和背景色。
invoke GetMenu,hWnd
mov hMenu,eax
invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, IDM_REPORT,MF_CHECKED
我们将让用户通过菜单来选择它想要的显示方式。这样我们必须先得到菜单的句柄。我了让用户跟踪当前的视图,我们在菜单中放入一组单选按钮。我们可以调用CheckMenuRadioItem函数,该函数将把一个单选按钮放到一个菜单项前。
注意我们创建列表视图控件时把它的宽度和高度都设成为0。当父窗口改变大小时,它将同时改变大小。这样我们可以让列表视图总是随着主窗口改变。子我们的例子中,我们让列表视图填充整个客户区。
.elseif uMsg==WM_SIZE
mov eax,lParam
mov edx,eax
and eax,0ffffh
shr edx,16
invoke MoveWindow,hList, 0, 0, eax,edx,TRUE
当父窗口接收到了WM_SIZE消息后,lParam的底字部分包含了客户区新的宽和高。让后我们调用MoveWindow来改变列表视图控件的大小使得它覆盖整个的客户区。
当用户通过菜单选择了一种选择方式,我们必须相应地改变列表视图中的显示方式。我们调用SetWindowLong函数来设定新的风格。
.elseif uMsg==WM_COMMAND
.if lParam==0
invoke GetWindowLong,hList,GWL_STYLE
and eax,not LVS_TYPEMASK
首先得到当前的风格,然后清除旧的风格。LVS_TYPEMASK 是LVS_ICON+LVS_SMALLICON+LVS_LIST+LVS_REPORT四种风格的集合。这样当我们用当前的风格“与”“not LVS_TYPEMASK”就等于清除了当前的显示风格。
在设计菜单时,我们使用了一些小技巧。我们包显示风格的常数串当作菜单的ID号。
IDM_ICON equ LVS_ICON
IDM_SMALLICON equ LVS_SMALLICON
IDM_LIST equ LVS_LIST
IDM_REPORT equ LVS_REPORT
这样当父窗口接收到WM_COMMAND消息时,希望显示的风格值会当成菜单的ID号传递过来。
mov edx,wParam
and edx,0FFFFh
在wParam中的低字部分是欲显示的风格,我们所需要做的只是把高字部分清0。
push edx
or eax,edx
我们把希望显示的风格加到列表视图的风格中去(已经去除了旧的风格)。
invoke SetWindowLong,hList,GWL_STYLE,eax
调用SetWindowLong函数来设定新的风格。
pop edx
invoke CheckMenuRadioItem,hMenu,IDM_ICON,IDM_LIST, edx,MF_CHECKED
.endif
我们需要在被选择的显示方式前放入单选按钮。如果要排序,我们必须处理WM_NOTIFY消息。
.elseif uMsg==WM_NOTIFY
push edi
mov edi,lParam
assume edi:ptr NMHDR
mov eax,[edi].hwndFrom
.if eax==hList
当我们接收到了WM_NOTIFY 消息后,lParam包含了指向NMHDR型结构体变量的指针。我们通过把列表视图控件的值和NMHDR型结构体变量中的hwndFrom成员变量的值比较来判断,如果相等的话我们就可以确定消息是列表视图控件发送的。
.if [edi].code==LVN_COLUMNCLICK
assume edi:ptr NM_LISTVIEW
如果通知消息是列表视图控件发送的,我们检测该消息是否是LVN_COLUMNCLICK。如果是,它意味着用户点击了列标题条。在接收到LVN_COLUMNCLICK消息后,我们假设lParam参数包含NM_LISTVIEW型结构体变量的指针,NM_LISTVIEW型结构体是NMHDR型结构体的扩展。我们需要知道用户单击了那一列,在iSubItem中的值即是列号,列的编号是从0开始的。
.if [edi].iSubItem==1
.if SizeSortOrder==0 || SizeSortOrder==2
在这里iSubItem的值是1,它表示用户点击的是第二列,即文件的大小。我们用状态变量来保持当前的排序顺序。0代表不用排序,1代表升序,2代表降序。如果该列中的项目/子项目以前没有排序或为降序,我们就把它设成升序。
invoke SendMessage,hList,LVM_SORTITEMS,1,addr CompareFunc
我们发送消息LVM_SORTITEMS给列表视图控件,在wParam中传递1,在lParam中传递比较函数的参数。注意wParam中的值是用户定义的,用户可以按自己的需要来解释,这里我们把它用作排序的方法。我们先来看看比较函数:
CompareFunc proc uses edi lParam1:DWORD, lParam2:DWORD, SortType:DWORD
LOCAL buffer[256]:BYTE
LOCAL buffer1[256]:BYTE
LOCAL lvi:LV_ITEM
mov lvi.imask,LVIF_TEXT
lea eax,buffer
mov lvi.pszText,eax
mov lvi.cchTextMax,256
列表视图控件将传递需要比较的两个项目的lParam(LV_ITEM型结构体变量的成员变量)比较函数。您还记得吗?我们在lParam中放置了醒目的索引号。这样我们利用这些索引号查询列表视图来得到项目信息。我们需要的消息是项目/子项目的标签文本。为此我们准备好LV_ITEM 型结构体变量并在iMask中设置标志位LVIF_TEXT ,在pszText中设置缓冲区的地址,在cchTextMax中设置缓冲区的大小。
.if SortType==1
mov lvi.iSubItem,1
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
如果SortType的值为1或2,我们知道点击了那一列,1代表根据文件的大小按升序排列所有的项目。2的意思相反。这样我们指定iSubItem为1(代表文件大小列)然后发送LVM_GETITEMTEXT 消息给列表视图控件来得到在项目的标签文本串。
invoke String2Dword,addr buffer
mov edi,eax
调用子定义的String2Dword函数来把字符串转换成一个DWORD值。它将在eax中返回转换后的值,我们把它保存在edi中以便以后比较用。
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
invoke String2Dword,addr buffer
sub edi,eax
mov eax,edi
对lParam2 中的值做同样的操作。当我们得到了两个文件的大小后,就可以比较它们了。比较的规则如下:
如果第一个项目放在前面,在eax中返回一个负值
如果第二个项目放在前面,在eax中返回一个正值
如果相等,在eax中返回0
在我们这里,我们想按升序排列,所以我们只要简单地将第二个项目的文件大小减去第一个项目的文件大小,然后返回放在eax中的值。
.elseif SortType==3
mov lvi.iSubItem,0
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam1,addr lvi
invoke lstrcpy,addr buffer1,addr buffer
invoke SendMessage,hList,LVM_GETITEMTEXT,lParam2,addr lvi
invoke lstrcmpi,addr buffer1,addr buffer
当用户点击文件名字列时,我们必须比较文件的名字。我们先得到文件的名字,然后调用lstrcmpi函数来比较,然后只要简单返回lstrcmpi的值,因为该函数比较使用的规则和我们的相同。
当项目排序后,我们调用UpdateParam函数来更新所有项目的lParam的值来反应出最新的改变。
invoke UpdatelParam
mov SizeSortOrder,1
该函数简单地枚举列表视图中所有的项目并且把它们lParam更新成项目的索引号。
.elseif [edi].code==NM_DBLCLK
invoke ShowCurrentFocus
.endif
如果用户双击某个项目时,我们将显示一个消息框,上面有该项目的有关标签值。我们必须检查NMHDR 中的code值是否是 NM_DBLCLK。如果是,我们就得到它的标签值并显示在一个消息框中。
ShowCurrentFocus proc
LOCAL lvi:LV_ITEM
LOCAL buffer[256]:BYTE
invoke SendMessage,hList,LVM_GETNEXTITEM,-1, LVNI_FOCUSED
我们是增么怎么知道某个项目被双击的呢?当单击或双击某个项目时,它的状态被设成“焦点”。即使有多个项目被选中,也仅有一个项目有焦点。我们的工作就是去找到那个有焦点的项目。我们发送LVM_GETNEXTITEM消息给列表视图控件,在lParam中指定期望的状态。如果wParam中时-1的话,表示要搜索所有的项目。有焦点的项目第索引号在eax中返回。
mov lvi.iItem,eax
mov lvi.iSubItem,0
mov lvi.imask,LVIF_TEXT
lea eax,buffer
mov lvi.pszText,eax
mov lvi.cchTextMax,256
invoke SendMessage,hList,LVM_GETITEM,0,addr lvi
发送LVM_GETITEM消息来得到标签。
invoke MessageBox,0, addr buffer,addr AppName,MB_OK
最后我们在一个消息框中显示标签。
如果想在列表视图控件中显示图标,您可以阅读关于树型视图控件的课程。它们的步骤基本上是一样的。
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -