⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 见招拆招《windows程序设计》(三) .txt

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 5 页
字号:
        invoke     GetTextMetrics,hdc,addr    tm
        mov         eax,tm.tmAveCharWidth         
        mov         cxChar,eax                                 ;cxChar = tm.tmAveCharWidth 
        
        mov         eax,2                                         ;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2
        test         DWORD ptr tm.tmPitchAndFamily,1
        jz        @f
        inc         eax
        @@:
        push         eax
        mov         eax,cxChar
        pop         ebx
        mul         ebx
        shr         eax,1
        mov         cxCaps,eax
        
        mov         eax,tm.tmHeight                         ;cyChar = tm.tmHeight + tm.tmExternalLeading 
        add         eax,DWORD ptr tm.tmExternalLeading
        mov         cyChar,eax

invoke ReleaseDC,hwnd, hdc

ret

        .elseif message == WM_PAINT
        
        invoke    BeginPaint,hwnd,addr ps
        mov         hdc,eax
        
        mov         DWORD ptr i,0
        lea         esi,sysmetrics
        add         esi,4                                         ;指向后面的字符串的地址
@@:
    mov        eax,cyChar         ;y_Pos=cyChar * i
    mul        DWORD PTR i
    mov    y_Pos,eax         
    mov        edi,[esi]         ;esi指向字符串的地址
                                    ;edi指向字符串
    invoke    lstrlen,edi         ;取字符串长度
    mov        ebx,eax
;TextOut (hdc, 0, cyChar * i,sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel))
        invoke     TextOut,hdc,0,y_Pos,edi,ebx
        add         esi,4
        
    mov        ebx,22                 ;x_Caps=cxCaps*22
    mov        eax,cxCaps
    mul        ebx
    mov    x_Caps,eax             
        mov         edi,[esi]             ;指向一个字符串地址
        invoke    lstrlen,edi
    mov        ebx,eax
;TextOut (hdc, 22 * cxCaps, cyChar * i,sysmetrics[i].szDesc,lstrlen (sysmetrics[i].szDesc))
        invoke     TextOut,hdc,x_Caps,y_Pos,edi,ebx
        invoke     SetTextAlign,hdc,TA_RIGHT or TA_TOP         
        
        sub         esi,8                     ;x_Caps=22 * cxCaps + 40 * cxChar
        mov         eax,cxChar
        mov         ebx,40
        mul         ebx
        add         x_Caps,eax
        
        mov         edi,[esi]             ;edi=sysmetrics[i].iIndex
        
        invoke    GetSystemMetrics,edi
        invoke    wsprintf,addr szBuffer,CTXT("%5d"),eax
        mov         ebx,eax
        
        invoke     TextOut,hdc,x_Caps,y_Pos,addr    szBuffer,ebx
        invoke     SetTextAlign,hdc,TA_LEFT or TA_TOP
    
    inc        i
    add        esi,16
    
    cmp        DWORD ptr i,80
        jNz         @b
        
        
        
        invoke    EndPaint,hwnd,addr ps
        ret
    .elseif message == WM_DESTROY
        
        invoke PostQuitMessage,NULL         
        
    .endif
    
    invoke DefWindowProc,hwnd, message, wParam, lParam
    ret
WndProc endp
END START 

    图4-4显示了在标准VGA上执行的SYSMETS1。在程序显示区域的前两行可以看到,屏幕宽度是800个图素,屏幕高度是600个图素,这两个值以及程序所显示的其它值可能会因显示器型态的不同而不同。

    

    图4-4 SYSMETS1的显示    

    
    总结:动手试试看上面的程序,再试试将定义字符串的部分放到一个文件中,也方便后面的程序使用


SYSMETS1.ASM窗口消息处理程序

    SYSMETS1.ASM程序中的WndProc窗口消息处理程序处理三个消息:WM_CREATE、WM_PAINT和WM_DESTROY。WM_DESTROY消息的处理方法与HELLOWIN程序相同。
WM_CREATE消息是窗口消息处理程序接收到的第一个消息。在CreateWindow函数建立窗口时,Windows产生这个消息。在处理WM_CREATE消息时,SYSMETS1呼叫GetDC取得窗口的设备内容,并呼叫GetTextMetrics取得内定系统字体的文字大小。SYSMETS1将平均字符宽度保存在cxChar中,将字符的总高度(包括外部间距)保存在cyChar中。

    SYSMETS1还将大写字母的平均宽度保存在静态变量cxCaps中。对于固定宽度的字体, cxCaps等于cxChar。对于可变宽度字体,cxCaps设定为cxChar乘以 150%。对于可变宽度字体,TEXTMETRIC结构中的tmPitchAndFamily字段的低位为1,对于固定宽度字体,该值为0。 SYSMETS1使用这个位从cxChar计算cxCaps:

        mov         eax,2                                         ;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2
        test         DWORD ptr tm.tmPitchAndFamily,1
        jz        @f
        inc         eax
        @@:  

    SYSMETS1在处理WM_PAINT消息处理期间完成所有窗口建立工作。通常,窗口消息处理程序先呼叫BeginPaint取得设备内容句柄,然后用一个循环对中定义的sysmetrics结构的每一行进行循环。三列文字用三个TextOut函数显示,对于每一列,TextOut的第三个参数都设定为:
    cyChar * i

    这个参数指示了字符串顶端相对于显示区域顶部的图素位置。
    第一次TextOut调用在第一列显示了大写标识符。TextOut的第二个参数是0,这是说文字从显示区域的左边缘开始。文字的内容来自sysmetrics结构给出的地址。我使用Windows函数lstrlen来计算字符串的长度,它是TextOut需要的最后一个参数。
    第二次TextOut调用显示了对系统尺寸值的描述。在这种情况下,TextOut的第二个参数设定为:
    22 * cxCaps

    第一列显示的最长的大写标识符有20个字符,因此第二列必须在第一列文字开头向右20 × cxCaps处开始。我使用22,以在两列之间加一点多余的空间。
    第三次TextOut调用显示从GetSystemMetrics函数取得的数值。变宽字体使得格式化向右对齐的数值有些棘手。从0到9的数字具有相同的宽度,但是这个宽度比空格宽度大。数值可以比一个数字宽,所以不同的数值应该从不同的横向位置开始。
    那么,如果我们指定字符串结束的图素位置,而不是指定字符串的开始位置,以此向右对齐数值,是否会容易一些呢?用SetTextAlign函数就可以做到这一点。在SYSMETS1呼叫:
    invoke     SetTextAlign,hdc,TA_RIGHT or TA_TOP

    之后,传给后续TextOut函数的坐标将指定字符串的右上角,而不是左上角。
    显示列数的TextOut函数的第二个参数设定为:
    22 * cxCaps + 40 * cxChar

    值40*cxChar包含了第二列的宽度和第三列的宽度。在TextOut函数之后,另一个对SetTextAlign的呼叫将对齐方式设定回普通方式,以进行下次循环。

空间不够

    在SYSMETS1程序中存在着一个很难处理的问题:除非您有一个大屏幕跟高分辨率的显示卡,否则就无法看到系统尺度列表的最后几行。如果窗口太窄,甚至根本看不到值。
    SYSMETS1不知道这个问题。否则我们就会显示一个消息框说「抱歉!」程序甚至不知道它的显示区域有多大,它从窗口顶部开始输出文字,并仰赖Windows裁剪超出显示区域底部的内容。
    显然,这很不理想。为了解决这个问题,我们的第一个任务是确定程序在显示区域内能输出多少内容。

显示区域的大小
    如果您使用过现有的Windows应用程序,可能会发现窗口的尺寸变化极大。窗口最大化时(假定窗口只有标题列并且没有菜单),显示区域几乎占据了整个屏幕。这一最大化了的显示区域的尺寸可以通过以SM_CXFULLSCREEN和SM_CYFULLSCREEN为参数呼叫GetSystemMetrics来获得。窗口的最小尺寸可以很小,有时甚至不存在,更不用说显示区域了。
    在最近一期,我们使用GetClientRect函数来取得显示区域的大小。使用这个函数没有什么不好,但是在您每次要使用信息时就去呼叫它一遍是没有效率的。确定窗口显示区域大小的更好方法是在窗口消息处理程序中处理WM_SIZE消息。在窗口大小改变时,Windows给窗口消息处理程序发送一个WM_SIZE消息。传给窗口消息处理程序的lParam参数的低字组中包含显示区域的宽度,高字组中包含显示区域的高度。要保存这些尺寸,需要定义两个变量:

    cxClient         dd     ?
    cyClient        dd     ? 

    与cxChar和cyChar相似,这两个变量在窗口消息处理程序内定义为静态变量,因为在以后处理其它消息时会用到它们。处理WM_SIZE的方法如下:

if message == WM_SIZE
        mov    eax,lParam     ;cxClient = LOWORD (lParam)
        and    eax,0FFFFh
        mov    cxClient,eax
        
        mov    eax,lParam
        shr    eax,16
        mov    cyClient,eax     ;cyClient = HIWORD (lParam)  

    在许多Windows程序中,WM_SIZE消息必然跟着一个WM_PAINT消息。为什么呢?因为在我们定义窗口类别时指定窗口类别样式为:
    CS_HREDRAW or CS_VREDRAW

    这种窗口类别样式告诉Windows,如果水平或者垂直大小发生改变, 则强制更新显示区域。
    用如下公式计算可以在显示区域内显示的文字的总行数:
    cyClient / cyChar

    如果显示区域的高度太小以至无法显示一个完整的字符,这个公式的结果可以为0。类似地,在显示区域的水平方向可以显示的小写字符的近似数目为:
    cxClient / cxChar

    如果在处理WM_CREATE消息处理期间取得cxChar和cyChar,则不用担心在这两个计算公式中会出现被0除的情况。在WinMain呼叫CreateWindow时,窗口消息处理程序接收一个WM_CREATE消息。在WinMain呼叫ShowWindow之后接收到第一个WM_CREATE消息,此时cxChar和cyChar已经被赋予正的非零值了。

    如果显示区域的大小不足以容纳所有的内容,那么,知道窗口显示区域的大小只是为使用者提供了在显示区域内卷动文字的第一步。如果您对其他有类似需求的Windows应用程序很熟悉,就很可能知道,这种情况下,我们需要使用「滚动条」。

滚动条
    滚动条是图形使用者接口中最好的功能之一,它很容易使用,而且提供了很好的视觉回馈效果。您可以使用滚动条显示任何东西--无论是文字、图形、表格、数据库记录、图像或是网页,只要它所需的空间超出了窗口的显示区域所能提供的空间,就可以使用滚动条。

    滚动条既有垂直方向的(供上下移动),也有水平方向的(供左右移动)。使用者可以使用鼠标在滚动条两端的箭头上或者在箭头之间的区域中点一下,这时,「卷动方块」在卷动列内的移动位置与所显示的信息在整个文件中的近似相关位置成比例。使用者也可以用鼠标拖动卷动方块到特定的位置。图4-5显示了垂直滚动条的建议用法。

     

    图4-5 垂直滚动条    

    有时,程序写作者对卷动概念很难理解,因为他们的观点与使用者的观点不同:使用者向下卷动是想看到文件较下面的部分;但是,程序实际上是将文件相对于显示窗口向上移动。Windows文件和表头文件标识符是依据使用者的观点:向上卷动意味着朝文件的开头移动;向下卷动意味着朝文件尾部移动。

    很容易在应用程序中包含水平或者垂直的滚动条,程序写作者只需要在CreateWindow的第三个参数中包括窗口样式(WS)标识符WS_VSCROLL(垂直卷动)和/或WS_HSCROLL(水平卷动)即可。这些卷动列通常放在窗口的右部和底部,伸展为显示区域的整个长度或宽度。显示区域不包含卷动列所占据的空间。对于特定的显示驱动程序和显示分辨率,垂直卷动列的宽度和水平卷动列的高度是恒定的。如果需要这些值,可以使用GetSystemMetrics呼叫来取得(如前面的程序那样)。

    Windows负责处理对滚动条的所有鼠标操作,但是,窗口滚动条没有自动的键盘接口。如果想用光标键来完成卷动功能,则必须提供这方面的程序代码(我们将在下一期另一个版本的SYSMETS程序中做到这一点)。

滚动条的范围和位置
    每个滚动条均有一个相关的「范围」(这是一对整数,分别代表最小值和最大值)和「位置」(它是卷动方块在此范围内的位置)。当卷动方块在卷动列的顶部(或左部)时,卷动方块的位置是范围的最小值;在卷动列的底部(或右部)时,卷动方块的位置是范围的最大值。

    在内定情况下,滚动条的范围是从0(顶部或左部)至100(底部或右部),但将范围改变为更方便于程序的数值也是很容易的:
    SetScrollRange (hwnd, iBa

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -