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

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

📁 会变语言实现的一些程序
💻 TXT
📖 第 1 页 / 共 5 页
字号:

    
    表3-4
    前缀    数据型态
    c       char或WCHAR或TCHAR
    by      BYTE (无正负号字符)
    n       short
    i       int
    x, y    int分别用作x坐标和y坐标
    cx, cy  int分别用作x长度和y长度;C代表「计数器」
    b或f    BOOL (int);f代表「旗标」
    w       WORD (无正负号短整数)
    l       LONG (长整数)
    dw      DWORD (无正负号长整数)
    fn      function(函数)
    s       string(字符串)
    sz      以字节值0结尾的字符串
    h       句柄
    p       指标
    


注册窗口类别

    窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。

    不同窗口可以依照同一种窗口类别建立。例如,Windows中的所有按钮窗口-包括按键、复选框,以及单选按钮-都是依据同一种窗口类别建立的。窗口类别定义了窗口消息处理程序和依据此类别建立的窗口的其它特征。在建立窗口时,要定义一些该窗口所独有的特征。

    在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。上面的程序使用的是windows.inc表头文件中定义的WNDCLASSEX。


    WNDCLASSEX STRUCT
      cbSize            DWORD      ?
      style             DWORD      ?
      lpfnWndProc       DWORD      ?
      cbClsExtra        DWORD      ?
      cbWndExtra        DWORD      ?
      hInstance         DWORD      ?
      hIcon             DWORD      ?
      hCursor           DWORD      ?
      hbrBackground     DWORD      ?
      lpszMenuName      DWORD      ?
      lpszClassName     DWORD      ?
      hIconSm           DWORD      ?
    WNDCLASSEX ENDS


   在这里提示一下数据型态和匈牙利表示法:其中的lpfn前缀代表「指向函数的长指标」。(在Win32 API中,长指标和短指标(或者近程指标)没有区别。这只是16位Windows的遗物。)cb前缀代表「字节数」而且通常作为一个常数来表示一个字节的大小。h前缀是一个句柄,而hbr前缀代表「一个画刷的代号」。lpsz前缀代表「指向以0结尾字符串的指针」。
   
   江湖上有一种传说,说你让不同的印度程序员编写同样的功能,他们的结果甚至连变量命名都相同。我想这个也许和他们都接受过统一的训练有关。我比较推荐使用英文单词作为变量名称。五六年之后,我们翻阅自己的代码看到:

   
   Counter db ?
   
   
   相比是一目了然的感觉,倘若命名为 jishuqi db ? 也许一时半会也猜不到这是什么东西。
   
   我也不再着重说明指标的定义。一个程序写作者的程序不应该因为使用以LP或NP为前缀的不同指针型态而被搅乱。
   
   在WinMain中为WNDCLASS定义一个结构,通常像这样:

   
   wndclass :WNDCLASSEX        
   

   然后,你就可以初始化该结构的12个字段,并呼叫RegisterClass。

   在WNDCLASS结构中最重要的两个字段是第二个和最后一个,第二个字段(lpfnWndProc) 是依据这个类别来建立的所有窗口所使用的窗口消息处理程序的地址。在HELLOWIN.ASM中,这个是WndProc函数。最后一个字段是窗口类别的文字名称。程序写作者可以随意定义其名称。在只建立一个窗口的程序中,窗口类别名称通常设定为程序名称。

   其它字段依照下面的方法描述了窗口类别的一些特征。让我们依次看看WNDCLASS结构中的每个字段。

   
    mov wndclass.style,CS_HREDRAW or CS_VREDRAW         
   

   使用汇编语言的位「或」运算子结合了两个「窗口类别样式」标识符。在表头文件Windows.inc中,已定义了一整组以CS为前缀的标识符:

   CS_VREDRAW                           equ 1h
   CS_HREDRAW                           equ 2h
   CS_KEYCVTWINDOW                      equ 4h
   CS_DBLCLKS                           equ 8h
   CS_OWNDC                             equ 20h
   CS_CLASSDC                           equ 40h
   CS_PARENTDC                          equ 80h
   CS_NOKEYCVT                          equ 100h
   CS_NOCLOSE                           equ 200h
   CS_SAVEBITS                          equ 800h
   CS_BYTEALIGNCLIENT                   equ 1000h
   CS_BYTEALIGNWINDOW                   equ 2000h
   CS_PUBLICCLASS                       equ 4000h
   CS_GLOBALCLASS                       equ CS_PUBLICCLASS        

   由于每个标识符都可以在一个复合值中设置一个位的值,所以按这种方式定义的标识符通常称为「位旗标」。通常我们只使用少数的窗口类别样式。HELLOWIN中用到的这两个标识符表示,所有依据此类别建立的窗口,每当窗口的水平方向大小(CS_HREDRAW)或者垂直方向大小(CS_VREDRAW)改变之后,窗口要完全重画。改变HELLOWIN的窗口大小,可以看到字符串仍然显示在窗口的中央,这两个标识符确保了这一点。不久我们就将看到窗口消息处理程序是如何得知这种窗口大小的变化的。
   
   WNDCLASS结构的第三个字段由以下叙述进行初始化:
   
    mov wndclass.lpfnWndProc,offset WndProc        
   

   这条叙述将这个窗口类别的窗口消息处理程序设定为WndProc,即HELLOWIN.ASM中的第二个函数。这个过程将处理依据这个窗口类别建立的所有窗口的全部消息。在汇编语言语言中,像这样在结构中使用函数名时,真正提供的是指向函数的指针。
   
   下面两个字段用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间:
   
    mov wndclass.cbClsExtra,0
    mov wndclass.cbWndExtra,0
   

   程序可以根据需要来使用预留的空间。HELLOWIN没有使用它们,所以设定值为0。否则,和匈牙利表示法所指示的一样,这个字段将被当成「预留的字节数」。
   
   下一个字段就是程序的执行实体句柄(它也是WinMain的参数之一):
   
    push hInst
    pop wndclass.hInstance        
   

叙述
   
    invoke LoadIcon,NULL,IDI_APPLICATION
    mov wndclass.hIcon,eax  
   

   为所有依据这个窗口类别建立的窗口设置一个图标。图标是一个小的位图图像,它对使用者代表程序,将出现在Windows工作列中和窗口的标题列的左端。在后面的章节中,您将学习如何为您的Windows程序自订图标。现在,为了方便起见,我们将使用预先定义的图示。

   要取得预先定义图示的句柄,可以将第一个参数设定为NULL来呼叫LoadIcon。在加载程序写作者自订的图标时(图标应该存放在磁盘上的.EXE程序文件中),这个参数应该被设定为程序的执行实体句柄hInstance。第二个参数代表图示。对于预先定义图示,此参数是以IDI开始的标识符(「ID代表图示」),标识符在windows.inc中定义。IDI_APPLICATION图标是一个简单的窗口小图形。LoadIcon函数传回该图示的句柄。我们并不关心这个句柄的实际值,它只用于设置hIcon字段元的值。该字段在WNDCLASSEX结构中定义为HICON型态,此型态名的含义为「handle to an icon(图示句柄)」。

叙述
   
    invoke LoadCursor,NULL,IDC_ARROW
    mov wndclass.hCursor,eax            
   

   与前一条叙述非常相似。LoadCursor函数加载一个预先定义的鼠标光标(命名为IDC_ARROW),并传回该游标的句柄。该句柄被设定给WNDCLASS结构的hCursor字段。当鼠标光标在依据这个类别建立的窗口的显示区域上出现时,它变成一个小箭头。
   
   下一个字段指定依据这个类别建立的窗口背景颜色。hbrBackground字段名称中的hbr前缀代表「handle to a brush(画刷句柄)」。画刷是个绘图词汇,指用来填充一个区域的着色样式。Windows有几个标准画刷,也称为「备用(stock)」画刷。这里所示的GetStockObject呼叫将传回一个白色画刷的句柄:
   
    invoke GetStockObject,WHITE_BRUSH
    mov wndclass.hbrBackground,EAX        
   

   这意味着窗口显示区域的背景完全为白色,这是一种极其普遍的做法。
   
   下一个字段指定窗口类别菜单。HElLOWIN没有应用程序菜单,所以该字段被设定为NULL:
   
    mov wndclass.lpszMenuName,NULL        
   

   最后,必须给出一个类别名称。对于小程序,类别名称可以与程序名相同,即存放在szAppName变量中的「HelloWin」字符串。
   
    mov wndclass.lpszClassName,offset szAppName        
   

   在初始化该结构的12个字段后,HELLOWIN呼叫RegisterClassEx来注册这个窗口类别。该函数只有一个参数,即指向WNDCLASSEX结构的指针。实际上,RegisterClassA函数将获得一个指向WNDCLASSA结构的指针,而RegisterClassW函数将获得一个指向WNDCLASSW结构的指针。程序要使用哪个函数来注册窗口类别,取决于发送给窗口的消息包含ASCII文字还是Unicode文字。

   [原文程序中使用的是区分UNICODE和ANSI的API,现在我们使用的这个API同时支持2者,不过为了方便对照我仍然添加了这个判断]
   
   现在有一个问题:如果用定义的UNICODE标识符编译了程序,程序将呼叫RegisterClassW。该程序可以在Microsoft Windows NT中执行良好。但如果此程序在Windows 98上执行,RegisterClassW函数并未真地被执行到。函数有一个进入点,但函数呼叫后只传回0,表明错误。对于在Windows 98下执行的Unicode程序来说,这是一个通知使用者有问题并终止执行的好机会。这是本书中多数程序处理RegisterClass函数呼叫的方法:

   
    invoke RegisterClassEx, ADDR wndclass
    .if (EAX==0)
         invoke MessageBox,NULL,CTXT("This program requires Windows NT!"),addr szAppName,MB_ICONERROR       
         ret
    .endif        
   

   由于MessageBoxW是可在Windows 98环境下执行的几个Unicode函数之一,所以其执行正常。
   
   当然,这段程序假定RegisterClassEx不会因为其它原因而呼叫失败,诸如WNDCLASSEx结构中lpfnWndProc字段被设定成NULL之类的错误。GetLastError函数会帮助您确定在这样的情况下产生错误的原因。GetLastError是Windows中常用的函数,它可以在函数呼叫失败时获得更多错误信息。不同函数的文件将指出您是否能够用GetLastError来获得这些信息。在Windows 98中呼叫RegisterClassW时,GetLastError将传回120。在Windows.inc中您可以看到,值120与标识符ERROR_CALL_NOT_IMPLEMENTED相等。
   
   很多Windows程序写作者喜欢检查所有可能发生错误的函数呼叫的传回值。这么做确实有点道理,相信您也非常习惯在配置内存后检查错误。而许多Windows函数需要配置内存。例如,RegisterClassEx需要配置内存,以保存窗口类别的信息。如此一来,您就应该要检查这个函数的执行结果。另一方面说来,如果由于RegisterClass不能得到所需要的内存,它会声明呼叫失败,而Windows大概也快当掉了。
   
   在本书的范例程序中,我做了最少的错误检查。这不是因为我认为错误检查不是一个好方法,而是因为这会让我们在程序举例中分心。
   

⌨️ 快捷键说明

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