• 《汇编语言程序设计》——仿windows计算器


     

    《汇编语言程序设计》

    ——计算器程序设计

     

    目录

    一、     题目与目标

    1.      题目

    2.      学习目的

    二、     分析与设计

    1.      系统分析

    2.      系统设计

    3.      功能分析

    4.      功能设计

    5.      界面设计

    6.      文件设计

    三、     程序系统说明书

    1.      创建计算器界面

    2.      引入头文件及库

    3.      定义常量

    4.      函数声明

    5.      程序说明

    Ø      工具子程序说明

    Ø      主程序

    Ø      WinMain主程序

    Ø      消息处理程序

    四、     设计与思考

    1.      为什么使用对话框?

    2.      如何应用系统的外观?

    3.      关于最小化

    4.      关于计算器

    5.      为什么要设计安装文件?

    6.      为什么要播放音乐?

    五、     课程设计的体会

    六、     参考资料

    七、     附录

    1.      系统模块总图

    2.      系统文件清单

     

     

     

     

     

     

     

     

    使用Win32编程设计一个功能及界面风格类似于Windows计算器的计算器程序,只要求实现标准型计算器。

    主要实现的功能:

    包含基本的四则运算、倒数运算、平方根运算。支持存储区的存储、清除、调出、累加等功能。

     

    Ø  WIN32汇编程序编写。

    Ø  用汇编实现简单的算法。

    Ø  浮点数运算(浮点指令或者自己编程模拟)。

    Ø  综合解决问题的能力。

     

     

    本程序为Win32窗口应用程序,因此采用Windows开发包的文档中规定的Windows程序标准框架进行编程设计。

     

     

    按照Windows程序标准框架,主程序用于获得并保存本程序的句柄,并调用窗口主程序WinMain创建窗口并进入消息循环。WinMain程序将获取的消息分发给消息处理程序Calculate进行处理。主程序及窗口主程序结构如下图:

     

     

    消息处理程序Calculate用于相应窗口创立、销毁、按键等消息并进行处理,根据系统功能,消息处理程序Calculate结构图如下:

     

     

    3.   功能分析

    如图所示,Windows自带的计算器按照功能划分可以分为以下5个区域:

     

     

    显示区:文本框,用于显示输入的操作数及结果

    数字键入区:在显示区中显示数字、小数点、正负号等;

    运算区:包含双目运算符(+ - * /)、单目运算符(sqrt()、%、1/x)、等于号等

    记忆区:清除记忆(MC)、显示记忆(MR)、记忆当前(MS)、记忆加(M+)以及记忆区存储情况的标签

    清除键区:退格(Backspace)、清除当前数据(CE)、初始化操作(C)

     

    Ø  数字:添加文本框字符串添加数字字符,调用函数BtnNum完成该功能;

    Ø  小数点:为当前输入数字添加小数点,将判断是否小数点的变量HasPoint赋值为1

    Ø  正负号:将当前数字取相反数并在对话框显示,拟通过浮点运算求相反数并调用ShowNum函数显示数字

    Ø  双目运算符:计算结果,调用函数BtnOperator实现运算功能

    Ø  等号:计算结果,调用函数BtnEqual实现运算功能

    Ø  单目运算符:立即对当前数字进行运算并输出结果

    Ø  MS:将当前数据保存在变量Remember中,并在记忆区存储情况的标签中显示相应的信息

    Ø  M+:将当前数据加到变量Remember上,并在记忆区存储情况的标签中显示相应的信息

    Ø  MR:将变量Remember数据显示到文本框中;

    Ø  MC:将变量Remember归零,并在记忆区存储情况的标签中显示相应的信息

    Ø  C:初始化计算器,调用函数Init实现该功能,并在文本框显示0.

    Ø  CE:将当前数字清零

    Ø  Backspace:删除当前数据的末位数字

     

    系统界面仿照Windows计算器程序界面设计,并使用资源文件进行定义,设计界面如下:

     

     

    6.   文件设计

    程序源文件包含两个部分:

    Ø  头文件(Calculator.inc):头文件中引入程序所需要的库以及常量和函数申明

    Ø  源文件(Calculator.asm):汇编程序源代码

    Ø  资源文件(Calculator.rc):定义程序的窗口界面以及相关资源

    Ø  说明文件(Calculator.exe.manifest):说明程序的相关配置及信息

     

     

    利用资源文件定义系统界面,代码如下

     

    #include "resource.h"

     

    #define ISOLATION_AWARE_ENABLED

     

    #define  ID_NUM0       300

    #define  ID_NUM1       301

    #define  ID_NUM2       302

    #define  ID_NUM3       303

    #define  ID_NUM4       304

    #define  ID_NUM5       305

    #define  ID_NUM6       306

    #define  ID_NUM7       307

    #define  ID_NUM8       308

    #define  ID_NUM9       309

    #define ID_NEG        310

    #define  ID_POINT      311

    #define  ID_MUL        312

    #define  ID_DIV        313

    #define  ID_SUB        314

    #define  ID_ADD        315

    #define  ID_EQU        316

    #define  ID_PER        317

    #define  ID_DAO        318

    #define  ID_SQRT       319

    #define  ID_MC         320

    #define  ID_MR         321

    #define ID_MS         322

    #define ID_MPLUS      323

    #define ID_M          324

    #define ID_BACK       325

    #define ID_CE         326

    #define ID_C          327

    #define ID_RESULT     328

    #define ID_COPY       1001

    #define ID_PASTE      1002

    #define ID_STANDARD   1003

    #define ID_SCIENCE    1004

    #define ID_PACKET     1006

    #define ID_HELP       1007

    #define ID_ABOUT      1008

    #define ID_EXIT       1009

     

    Calculator DIALOGEX 0, 0, 170, 133

    STYLE DS_CENTER |  WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX

    CLASS "Calculator"

    CAPTION "计算器"

    FONT 8, "Tahoma"

    BEGIN

         PUSHBUTTON   "0",ID_NUM0,36,99,23,16,0

         PUSHBUTTON   "1",ID_NUM1,36,81,23,16,0

         PUSHBUTTON   "2",ID_NUM2,61,81,23,16,0

         PUSHBUTTON   "3",ID_NUM3,87,81,23,16,0

         PUSHBUTTON   "4",ID_NUM4,36,63,23,16,0

         PUSHBUTTON   "5",ID_NUM5,61,63,23,16,0

         PUSHBUTTON   "6",ID_NUM6,87,63,23,16,0

         PUSHBUTTON   "7",ID_NUM7,36,44,23,16,0

         PUSHBUTTON   "8",ID_NUM8,61,44,23,16,0

         PUSHBUTTON   "9",ID_NUM9,87,44,23,16,0

         PUSHBUTTON   "+/-",ID_NEG,61,99,23,16,0

         PUSHBUTTON   ".",ID_POINT,87,99,23,16,0

         PUSHBUTTON   "/",ID_DIV,113,44,23,16,0

         PUSHBUTTON   "*",ID_MUL,113,63,23,16,0

         PUSHBUTTON   "-",ID_SUB,113,81,23,16,0

         PUSHBUTTON   "+",ID_ADD,113,99,23,16,0

         PUSHBUTTON   "sqrt",ID_SQRT,139,44,23,16,0

         PUSHBUTTON   "%",ID_PER,139,63,23,16,0

         PUSHBUTTON   "1/x",ID_DAO,139,81,23,16,0

         PUSHBUTTON   "=",ID_EQU,139,99,23,16,0

         PUSHBUTTON   "MC",ID_MC,6,44,23,16,0

         PUSHBUTTON   "MR",ID_MR,6,63,23,16,0

         PUSHBUTTON   "MS",ID_MS,6,81,23,16,0

         PUSHBUTTON   "M+",ID_MPLUS,6,99,23,16,0

         PUSHBUTTON   "Backspace",ID_BACK,36,23,42,16,0

         PUSHBUTTON   "CE",ID_CE,79,23,41,16,0

         PUSHBUTTON   "C",ID_C,122,23,41,16,0

         EDITTEXT ID_RESULT,5,2,160,13,ES_RIGHT | ES_NUMBER ,0

         CTEXT        "",ID_M,9,23,17,14,SS_SUNKEN | NOT WS_BORDER

    END

     

    Menu MENU LOADONCALL

         BEGIN

             POPUP "编辑(&F)"

                  BEGIN

                       MENUITEM "复制(&C) Ctrl+C",ID_COPY

                       MENUITEM "粘贴(&P) Ctrl+P",ID_PASTE

                       MENUITEM SEPARATOR

                       MENUITEM "关闭(&E)",ID_EXIT

                  END

             POPUP "查看(&V)"

                  BEGIN

                       MENUITEM "标准型(&T)",ID_STANDARD

                       MENUITEM "科学型(&S)",ID_SCIENCE,GRAYED

                       MENUITEM SEPARATOR

                       MENUITEM "数字分组(&I)",ID_PACKET

                  END

             POPUP "帮助(&H)"

                  BEGIN

                       MENUITEM "帮助主题(&H)",ID_HELP

                       MENUITEM SEPARATOR

                       MENUITEM "关于计算器(&A)",ID_ABOUT

                  END

             POPUP "", GRAYED

                  BEGIN

                       MENUITEM "复制(&C) Ctrl+C",1001

                       MENUITEM "粘贴(&P) Ctrl+P",1002

                       MENUITEM SEPARATOR

                       MENUITEM "标准型(&T)",1003

                       MENUITEM "科学型(&S)",1004,GRAYED

                       MENUITEM SEPARATOR

                       MENUITEM "数字分组(&I)",1006

                       MENUITEM SEPARATOR

                       MENUITEM "帮助主题(&H)",1007

                       MENUITEM "关于计算器(&A)",1008

                       MENUITEM SEPARATOR

                       MENUITEM "关闭(&E)",1009

                  END

         END

     

    Icon ICON MOVEABLE PURE LOADONCALL DISCARDABLE "Calculator.ico"

     

           文件分别定义了对话框,菜单和Icon图标等资源,为了在程序中方便对消息的处理,此处有意连续定义了ID_NUM0~ID_NUM9

     

    在Calculator.inc头文件中统一定义程序所需的头文件及引入库

     

    ;--------------------------- 头文件声明---------------------------

            include windows.inc

            include user32.inc

            include kernel32.inc

            include comctl32.inc

            include masm32.inc

            include shell32.inc

    ;--------------------------- 引入库声明---------------------------

            includelib user32.lib

            includelib comctl32.lib

            includelib masm32.lib

     

     

    在Calculator.inc中定义程序所需常量

     

    ;---------------------------- 常量声明----------------------------

             ID_NUM0            equ 300

             ID_NUM1            equ 301

             ID_NUM2            equ 302

             ID_NUM3            equ 303

             ID_NUM4            equ 304

             ID_NUM5            equ 305

             ID_NUM6            equ 306

             ID_NUM7            equ 307

             ID_NUM8            equ 308

             ID_NUM9            equ 309

             ID_NEG             equ 310

             ID_POINT           equ 311

             ID_MUL             equ 312

             ID_DIV             equ 313

             ID_SUB             equ 314

             ID_ADD             equ 315

             ID_EQU             equ 316

             ID_PER             equ 317

             ID_DAO             equ 318

             ID_SQRT            equ 319

             ID_MC              equ 320

             ID_MR              equ 321

             ID_MS              equ 322

             ID_MPLUS           equ 323

             ID_M               equ 324

             ID_BACK            equ 325

             ID_CE              equ 326

             ID_C               equ 327

             ID_RESULT          equ 328

             ID_COPY            equ 1001

             ID_PASTE           equ 1002

             ID_STANDARD        equ 1003

             ID_SCIENCE         equ 1004

             ID_PACKET          equ 1006

             ID_HELP            equ 1007

             ID_ABOUT           equ 1008

             ID_EXIT            equ 1009

             ID_NOTIFYICON      equ 2000

             WM_SHELLNOTIFY          equ WM_USER+1

     

     

    在Calculator.inc声明了自定义函数的原型

     

    ;---------------------------- 函数声明----------------------------

             WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD   ; 窗口主程序

             Calculate PROTO :DWORD,:DWORD,:DWORD,:DWORD    ; 消息处理程序

             PackNum PROTO                                  ; 数字分组子程序

             UnpackNum PROTO                                ; 数字不分组子程序

             BtnNum PROTO :DWORD                            ; 数字按键消息处理程序

             ShowNum PROTO                                  ; 显示数据子程序

             ShowTextM PROTO                                ; 显示存储信息子程序

             Init PROTO                                     ; 初始化计算器子程序

             GetResult PROTO                                ; 计算结果子程序

             BtnOperator PROTO                              ; 双目运算符消息处理程序

             BtnEqual PROTO                                     ; 等于消息处理程序

     

    数据段定义

     

    ;===================== Start 数据段定义Start =====================

        .data

             ProgramName        db   "计算器",0             ;程序名

             Author             db   "作者:桂杨",0              ;作者

             HelpFile          db   "rc.hlp",0             ;帮助文档

             hInstance          db   ?                      ;主程序句柄

             hEdit              db   ?                      ;输出文本框句柄

             hTextM             db   ?                      ;记忆标签句柄

             hMenu              db   ?                      ;菜单句柄

             hIcon              db   ?                      ;Icon句柄

             DialogName         db   "Calculator",0              ;对话框名称

             MenuName           db   "Menu",0               ;菜单名称

             IconName           db   "Icon",0                ;Icon名称

             TextM              db   'M',0                  ;M

             Output             db   "0.",0,30 dup(0)        ;输出字符串

             IsStart            db   1                      ;判断是否运算开始

             HasPoint           db   0                      ;判断是否存在小数点

             HasEqueal          db   0                      ;判断是否存在等号

             Remember           dq   0.0                    ;记忆数据

             Number             dq   0.0                    ;记录临时数据

             Result             dq   0.0                    ;记录结果

             Operand            dq   0.0                    ;记录操作数

             IsPacket           db   0                      ;数字分组

             Operator           db   '.'                    ;记录运算符

             IsError            db   0                      ;记录是否出现异常

             Div0               db   "除数不能为零。",0

             FunctionError      db   "函数输入无效。",0

             hGlobal            HANDLE ?                    ;剪切板内存块句柄

             pGlobal            db   ?                      ;pointer to allocate memory

             NumLittle          REAL8    1.0E-12

             Num10              REAL8    10.0               ;实数10

             Num100             REAL8    100.0              ;实数100

             NotifyIcon         NOTIFYICONDATA<>            ;通知栏图标

    ;======================= End 数据段定义End =======================       

     

     

     

    n  PackNum

    PackNum函数将输出数据的字符串Output进行数字分组。它首先获取小数点以前的数字位数并保存在寄存器eax中,然后将(eax-1)/3即为需要添加的字符‘,’数目,并保存在eax中,对于小数点以后的字符都向后移动eax位,对于小数点以前的字符,向后移动eax位并用ecx计数,当ecx计数到3是添加字符‘,’并将ecx设为1且eax减一,重复上述步骤直到eax等于0。

    函数的流程图如下:

     

    函数源代码如下:

     

    PackNum proc USES eax ebx ecx edx

             lea esi,Output

             mov eax,0

             .while (BYTE PTR[esi]!='.')

                  inc eax

                  inc esi

             .endw

             .while (BYTE PTR[esi]!=0)

                  inc esi

             .endw

             dec eax

             mov edx,0

             mov ecx,3

             div ecx

             .while (BYTE PTR[esi]!='.')

                  mov bx,[esi]

                  mov [esi+eax],bx

                  dec esi

             .endw

             mov bx,[esi]

             mov [esi+eax],bx

             dec esi

             mov ecx,0

             .while (eax!=0)

                  .if(ecx<3)

                       mov bx,[esi]

                       mov [esi+eax],bx

                       inc ecx

                  .else

                       mov BYTE PTR[esi+eax],','

                       dec eax

                       mov ecx,1

                  .endif

                  dec esi

             .endw

             lea esi,Output

             .while (BYTE PTR[esi]!=0)

                  mov bx,[esi]

                  inc esi

             .endw

             ret

    PackNum endp

     

    n  UnpackNum

    UnpackNum函数将进行数字分组输出的字符串Output解分组。它首先获取Output地址存在esi中,然后ecx赋0,并将Output中字符向前移动ecx个单位,遇见‘,’字符则将ecx加1,直到字符串结束。

    函数的流程图如下:

     

    函数源代码如下:

     

    UnpackNum proc USES ecx

             lea esi,Output

             mov ecx,0

             .while (BYTE PTR[esi+ecx]!=0)

                  .if(BYTE PTR[esi]==",")

                       inc ecx

                  .endif

                  mov bx,[esi+ecx]

                  mov [esi],bx

                  inc esi

             .endw

             ret

    UnpackNum endp

     

    n  ShowNum

    ShowNum函数将Output字符串处理后在文本框中显示出来。它首先调用UnpackNum函数对Output解分组,然后获取Output地址存在esi、edi中,通过循环将Output尾地址存在esi中,将字符‘.’地址存在edi中,如果edi等于esi则表明Output中无字符‘.’,则在结尾添加字符‘.’。如果IsPacked等于1则对Output调用UnpackNum函数对其分组,最后向文本框发送WM_SETTEXT消息显示数据。

    函数的流程图如下:

     

    函数源代码如下:

     

    ShowNum proc

             invoke UnpackNum

             lea esi,Output

             lea edi,Output

             .while (BYTE PTR[esi]!=0)

                  inc esi

             .endw

             .while (BYTE PTR[edi]!='.') && (edi<esi)

                  inc edi

             .endw

             .if esi==edi

                  mov BYTE PTR[esi],'.'

                  mov BYTE PTR[esi+1],0

             .endif

             .if IsPacket==1

                  invoke PackNum

             .endif

             invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output

             ret

    ShowNum endp

     

    n  BtnNum

    BtnNum函数响应数字按钮消息,向文本框中添加字符。

    函数源代码如下:

     

    BtnNum proc USES eax,Num:DWORD

             lea esi,Output

             mov eax,Num

             sub eax,252

             .if IsStart==1

                  mov [esi],eax

                  inc esi

                  mov BYTE PTR[esi],'.'

                  inc esi

                  mov BYTE PTR[esi],0

                  mov IsStart,0

             .else

                  .while BYTE PTR[esi]!='.'

                       inc esi

                  .endw

                  .if HasPoint==1

                       .while BYTE PTR[esi]!=0

                           inc esi

                       .endw

                       mov [esi],ax

                       inc esi

                       mov BYTE PTR[esi],0

                  .else

                       .if BYTE PTR[Output]=='0'

                           lea esi,Output

                           mov [esi],eax

                           mov BYTE PTR[esi+1],'.'

                           mov BYTE PTR[esi+2],0

                       .else

                           mov [esi],eax

                           inc esi

                           mov BYTE PTR[esi],'.'

                           inc esi

                           mov BYTE PTR[esi],0

                       .endif

                  .endif

             .endif  

             invoke ShowNum

             ret 

    BtnNum endp

     

    n  BtnOperator

    BtnOperator函数响应运算符按钮消息,进行运算并输出结果。首先判断是否为等号,如果不是则调用GetResult函数先进行一次运算,然后将当前操作符存入Operator变量中。

    函数源代码如下:

     

    BtnOperator proc USES eax

             .if HasEqueal!=1

                  invoke GetResult

             .endif

             .if eax ==    ID_MUL

                  mov Operator,'*'

             .elseif eax == ID_DIV

                  mov Operator,'/'

             .elseif eax == ID_SUB

                  mov Operator,'-'

             .elseif eax == ID_ADD

                  mov Operator,'+'

             .endif

              mov HasEqueal,0

             ret

    BtnOperator endp

     

    n  BtnEqual

    BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。

    函数源代码如下:

     

    BtnEqual proc

             .if (IsStart==1) && (HasEqueal==0)

                  fstp Number

                  fst Number

                  fld Number

             .endif

             invoke GetResult

             mov HasEqueal,1

             ret

    BtnEqual endp

     

    n  GetResult

    BtnEqual函数响应等号按钮消息,进行运算并输出结果。首先判断是否为起始状态,如果不是则调用GetResult函数,并将HasEqual变量置1。

    函数源代码如下:

     

    GetResult proc USES eax

             invoke UnpackNum

             finit

             .if (IsStart==1) && (HasEqueal==0)

             .else

                  .if HasEqueal!=1

                       invoke StrToFloat,addr Output, addr Operand

                  .endif

                  fld Result            

                  fld Operand

                  .if  Operator=='.'

                       fst Result

                       jmp Show

                  .elseif Operator=='+'

                       fadd ST(1),ST(0)

                  .elseif Operator=='-'

                       fsub ST(1),ST(0)

                  .elseif Operator=='*'

                       fmul ST(1),ST(0)

                  .elseif Operator=='/'

                       fldz

                       fcomi ST(0),ST(1)

                       jnz NotZero

                       mov IsError,1

                       invoke SendMessage,hEdit,WM_SETTEXT,0,addr Div0

                       ret

    NotZero:      fstp Operand      

                       fdiv ST(1),ST(0)

                  .endif

                  fstp Operand

                  fst Result

    Show:         mov IsStart,1

                  mov HasPoint,0

                  invoke FloatToStr2,Result,addr Output

                  invoke ShowNum

             .endif

             ret

    GetResult endp

     

    n  ShowTextM

    ShowTextM函数判断Remember中的值是否为0,如果不是是则在标签中显示‘M’,否则清空标签中内容。

    函数源代码如下:

     

    ShowTextM proc

             fld NumLittle

             fldz

             fsub Remember

             fabs

             fcomi ST(0),ST(1)

             ja NotZero

             invoke SendMessage,hTextM,WM_SETTEXT,0,NULL

             jmp PopNumLittle

    NotZero:invoke SendMessage,hTextM,WM_SETTEXT,0,addr TextM

    PopNumLittle:fstp  Operand

             fstp Operand

             mov IsStart,1

             mov HasPoint,0

             ret

    ShowTextM endp

     

    n  Init

    Init函数负责进行必要的初始化操作,如对状态变量的初始化以及的FPU的初始化。

    函数源代码如下:

     

    Init proc

             mov IsStart,1               ;初始化

             mov HasPoint,0                   ;清除小数点

             mov HasEqueal,0

             fldz

             fst Number                  ;清除结果

             fst Operand

             mov Operator,'.'            ;清除运算符

             mov IsError,0

             finit                       ;初始化FPU

             ret

    Init endp

     

     

    主程序用于获得并保存本程序的句柄,调用WinMain主程序创建窗口并获取和分发消息,然后结束程序。

    主程序流程图及原代码如下:

     

    invoke GetModuleHandle,NULL

    ;获得并保存本程序的句柄

    mov hInstance,eax

    invoke WinMain,hInstance,0,0,SW_SHOWDEFAULT

    invoke ExitProcess,eax

    ;退出程序,返回eax值

     

     

     

     

    Ø   主程序

    WinMain主程序用于创建窗口并获取和分发消息。

    主程序流程图如下:

     

    程序源代码如下:

     

    WinMain proc hInst:DWORD, hPrevInst:DWORD, CmdLine:DWORD, CmdShow:DWORD

            LOCAL wc:WNDCLASSEX                    ;窗口类

            LOCAL msg:MSG                          ;消息

            LOCAL hWnd:HWND                        ;对话框句柄

            

            mov wc.cbSize,sizeof WNDCLASSEX        ;WNDCLASSEX的大小

            mov wc.style,CS_BYTEALIGNWINDOW or CS_BYTEALIGNWINDOW ;窗口风格or CS_HREDRAW or CS_VREDRAW

            mov wc.lpfnWndProc,OFFSET  Calculate   ;窗口消息处理函数地址

            mov wc.cbClsExtra,0                    ;在窗口类结构后的附加字节数,共享内存

            mov wc.cbWndExtra,DLGWINDOWEXTRA       ;在窗口实例后的附加字节数(!注意点)

            mov eax,hInst

            mov wc.hInstance,eax                        ;窗口所属程序句柄

            mov wc.hbrBackground,COLOR_BTNFACE+1   ;背景画刷句柄

            mov wc.lpszMenuName,NULL               ;菜单名称指针

            mov wc.lpszClassName,OFFSET  DialogName    ;类名称指针

            invoke LoadIcon,hInst,addr IconName    ;加载Icon

            mov wc.hIcon,eax                       ;图标句柄

            invoke LoadCursor,NULL,IDC_ARROW

            mov wc.hCursor,eax                     ;光标句柄

            mov wc.hIconSm,0                       ;窗口小图标句柄

           

            invoke RegisterClassEx,addr wc         ;注册窗口类

            invoke CreateDialogParam,hInst,addr DialogName,0,addr Calculate,0  ;调用对话框窗口

            mov   hWnd,eax                         ;保存对话框句柄

            invoke ShowWindow,hWnd,CmdShow         ;最后一个参数可设置为SW_SHOWNORMAL

            invoke UpdateWindow,hWnd               ;更新窗口

    StartLoop:                                     ;消息循环

             invoke GetMessage,addr msg,0,0,0      ;获取消息

             cmp eax,0

             je ExitLoop

             invoke TranslateMessage,addr msg      ;转换键盘消息

             invoke DispatchMessage,addr msg      ;分发消息

             jmp StartLoop

    ExitLoop:                                      ;结束消息循环

             mov eax,msg.wParam

          ret

    WinMain endp

     

     

    消息处理程序用于处理用户消息。

    消息处理程序流程图如下:

     

    消息处理程序源代码如下:

     

    Calculate proc hWin:DWORD,uMsg:UINT,aParam:DWORD,bParam:DWORD

             LOCAL pt:POINT

            .if uMsg == WM_INITDIALOG

                                invoke GetDlgItem,hWin,ID_RESULT          ;获取输出文本框句柄

                                mov hEdit,eax                             ;保存文本框句柄

                                invoke GetDlgItem,hWin,ID_M               ;获取记忆标签句柄

                                mov hTextM,eax                                 ;保存记忆标签句柄

                                invoke LoadIcon,hInstance,addr IconName   ;载入Icon

                                mov hIcon,eax                             ;保存Icon句柄

                                invoke SendMessage,hWin,WM_SETICON,ICON_SMALL ,eax                        

                                invoke LoadMenu,hInstance,addr MenuName   ;加载菜单

                                mov hMenu,eax                             ;保存菜单句柄

                                invoke SetMenu,hWin,eax

                                invoke CheckMenuRadioItem, hMenu, ID_STANDARD, ID_SCIENCE,ID_STANDARD,MF_BYCOMMAND ;选中标准型

                                invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output  ;显示"0."                      

            .elseif uMsg == WM_SIZE

                                .if aParam==SIZE_MINIMIZED                     ;最小化

                                     mov NotifyIcon.cbSize,sizeof NOTIFYICONDATA

                                     push hWin

                                     pop NotifyIcon.hwnd

                                     mov NotifyIcon.uID,ID_NOTIFYICON

                                     mov NotifyIcon.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP

                                     mov NotifyIcon.uCallbackMessage,WM_SHELLNOTIFY

                                     mov eax,hIcon

                                     mov NotifyIcon.hIcon,eax

                                     invoke lstrcpy,addr NotifyIcon.szTip,addr ProgramName

                                     invoke ShowWindow,hWin,SW_HIDE            ;隐藏窗口

                                     invoke Shell_NotifyIcon,NIM_ADD,addr NotifyIcon

                                .endif

            .elseif uMsg == WM_SHELLNOTIFY

                                .if aParam==ID_NOTIFYICON

                                     .if (bParam==WM_LBUTTONDOWN)              ;单击通知栏图标

                                         invoke ShowWindow,hWin,SW_SHOW       ;显示窗口

                                         invoke Shell_NotifyIcon,NIM_DELETE,addr NotifyIcon ;删除通知栏图标

                                     .elseif (bParam==WM_RBUTTONDOWN)     ;右键通知栏图标

                                         invoke GetCursorPos,addr pt

                                         invoke GetSubMenu,hMenu,3

                                         invoke TrackPopupMenu,eax,TPM_LEFTALIGN,pt.x,pt.y,NULL,hWin,NULL

                                     .endif

                           .endif

            .elseif uMsg == WM_CHAR                                            ;热键操作

                           mov eax,aParam

                                sub eax,'0'

                                add eax,ID_NUM0

                                .if (eax>=ID_NUM0) && (eax<=ID_NUM9)      ;数字按钮

                                     invoke Calculate,hWin,WM_COMMAND,eax,0

                                .elseif (eax==0ffh)                                ;ID_COPY

                                     invoke Calculate,hWin,WM_COMMAND,ID_COPY,0

                                .elseif (eax==112h)                                ;ID_PASTE

                                     invoke Calculate,hWin,WM_COMMAND,ID_PASTE,0

                                .elseif (eax==104h)                                ;ID_BACK

                                     invoke Calculate,hWin,WM_COMMAND,ID_BACK,0

                                .elseif (eax==265)                             ;ID_EQU

                                     invoke Calculate,hWin,WM_COMMAND,ID_EQU,0

                                .elseif (eax==298)                             ;ID_POINT                           

                                     invoke Calculate,hWin,WM_COMMAND,ID_POINT,0

                                .elseif(eax==295)                              ;ID_ADD

                                     invoke Calculate,hWin,WM_COMMAND,ID_ADD,0

                                .elseif (eax==297)                             ;ID_SUB

                                     invoke Calculate,hWin,WM_COMMAND,ID_SUB,0

                                .elseif (eax==294)                             ;ID_MUL

                                     invoke Calculate,hWin,WM_COMMAND,ID_MUL,0

                                .elseif (eax==299)                             ;ID_DIV

                                    invoke Calculate,hWin,WM_COMMAND,ID_DIV,0

                                .endif

            .elseif uMsg == WM_COMMAND

                            mov eax,aParam

                            .if eax == ID_CE                              ;清零按钮CE

                                     lea esi,Output

                                     mov BYTE PTR[esi],'0'

                                     mov BYTE PTR[esi+1],'.'

                                     mov BYTE PTR[esi+2],0

                                     .if IsError==1

                                         invoke Init

                                     .endif

                                     invoke SendMessage,hEdit,WM_SETTEXT,0,addr Output

                            .elseif eax == ID_C                                ;初始化按钮C

                                     invoke Calculate,hWin,WM_COMMAND,ID_CE,bParam

                                     invoke Init

                                .elseif IsError==1

                                     ret

                                .elseif eax == ID_BACK                         ;退格按钮Backspace

                                     invoke UnpackNum                              

                                     .if IsStart==0

                                         lea esi,Output

                                         .while BYTE PTR[esi]!=0

                                              inc esi

                                         .endw

                                         .if BYTE PTR[esi-1]=='.'

                                              .if HasPoint==1

                                                   mov HasPoint,0

                                              .else

                                                   .if BYTE PTR[esi-3]=='-'

                                                       lea esi,Output

                                                       mov BYTE PTR[esi],'0'

                                                       mov BYTE PTR[esi+1],'.'

                                                       mov BYTE PTR[esi+2],0

                                                   .else

                                                       mov BYTE PTR[esi-2],'.'

                                                       mov BYTE PTR[esi-1],0

                                                   .endif

                                              .endif

                                         .else

                                              mov BYTE PTR[esi-1],0

                                         .endif

                                         lea esi,Output

                                         .if  BYTE PTR[esi]=='.'

                                              mov BYTE PTR[esi],'0'

                                              mov BYTE PTR[esi+1],'.'

                                              mov BYTE PTR[esi+2],0

                                         .endif

                                         invoke ShowNum

                                     .endif

                            .elseif (eax >= ID_NUM0) && (eax <= ID_NUM9)  ;数字按钮

                                     .if HasEqueal==1

                                         invoke Init

                                     .endif

                                     invoke BtnNum,eax

                            .elseif eax == ID_POINT                                ;小数点按钮

                                     mov BYTE PTR HasPoint,1

                                     mov BYTE PTR IsStart,0

                                .elseif eax == ID_NEG                              ;正负号按钮

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Number

                                     finit

                                     fldz

                                     fld Number

                                     fsub

                                     fstp Number

                                     invoke FloatToStr2,Number,addr Output

                                     invoke ShowNum

                            .elseif  (eax >= ID_MUL) && (eax <= ID_ADD)        ;双目运算符按钮

                                     invoke BtnOperator

                            .elseif  eax ==   ID_EQU                               ;等于按钮

                                     invoke BtnEqual

                                .elseif eax == ID_PER                              ;百分号按钮

                                     mov Operator,'*'

                                     invoke GetResult

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Number

                                     finit

                                     fld Number

                                     fld Num100

                                     fdiv

                                     fstp Number

                                     invoke FloatToStr2,Number,addr Output

                                     invoke ShowNum

                                .elseif eax == ID_DAO                              ;倒数按钮

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Number

                                     finit

                                     fld Number

                                     fldz

                                     fcomi ST(0),ST(1)

                                     jnz NotZero

                                     mov IsError,1

                                     invoke SendMessage,hEdit,WM_SETTEXT,0,addr Div0

                                     ret

    NotZero:                    fstp Number

                                     fstp Number

                                     fld1

                                     fld Number

                                     fdiv

                                     .if HasEqueal==1

                                         fst Result   

                                     .endif

                                     fstp Number

                                     invoke FloatToStr2,Number,addr Output

                                     invoke ShowNum

                                .elseif eax == ID_SQRT                             ;开方按钮

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Number

                                     finit

                                     fld Number

                                     fldz

                                     fcomi ST(0),ST(1)

                                     jb Positive

                                     mov IsError,1

                                     invoke SendMessage,hEdit,WM_SETTEXT,0,addr FunctionError

                                     ret

    Positive:                   fstp Number

                                     fsqrt

                                     .if HasEqueal==1

                                         fst Result   

                                     .endif

                                     fstp Number

                                     invoke FloatToStr2,Number,addr Output

                                     invoke ShowNum

                                .elseif eax == ID_MC                               ;MC按钮

                                     fldz

                                     fstp Remember

                                     invoke SendMessage,hTextM,WM_SETTEXT,0,NULL

                                .elseif eax == ID_MR                               ;MR按钮

                                     invoke FloatToStr2,Remember,addr Output

                                     invoke ShowNum

                                     mov IsStart,0

                                .elseif eax == ID_MS                               ;MS按钮

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Remember

                                     invoke ShowTextM

                                .elseif eax == ID_MPLUS                            ;M+按钮

                                     finit

                                     fld Remember

                                     invoke UnpackNum                              

                                     invoke StrToFloat,addr Output, addr Remember

                                     fld Remember

                                     fadd

                                     fstp Remember

                                     invoke ShowTextM

                                .elseif eax == ID_COPY                             ;复制

                                     invoke GlobalAlloc,GMEM_MOVEABLE,35            ;配置一个内存块

                                     mov hGlobal ,eax

                                     invoke GlobalLock,hGlobal                      ;锁定内存块

                                     mov pGlobal ,eax

                                     lea esi,Output

                                     mov edi,pGlobal

                                     mov ecx,35

                                     rep movsb                                      ;复制字符串

                                     invoke GlobalUnlock,hGlobal                    ;解锁内存块

                                     invoke OpenClipboard, NULL                     ;打开剪切板

                                     invoke EmptyClipboard                          ;清空剪切板

                                     invoke SetClipboardData,CF_TEXT,hGlobal        ;把内存句柄交给剪贴簿

                                     invoke CloseClipboard                          ;关闭剪切板

                                .elseif eax == ID_PASTE                            ;粘贴

                                     invoke IsClipboardFormatAvailable,CF_TEXT ;确定剪贴簿是否含有CF_TEXT格式的数据

                                     invoke OpenClipboard,NULL                      ;打开剪切板

                                     invoke GetClipboardData,CF_TEXT                ;得到代表文字的内存块代号

                                     mov hGlobal,eax

                                     invoke GlobalLock ,hGlobal                     ;解锁内存块

                                     mov pGlobal,eax

                                     mov ecx,35

                                     lea edi,Output

                                     mov esi,eax

                                     rep movsb                                      ;复制字符串

                                     invoke GlobalUnlock ,hGlobal                   ;解锁内存块

                                     invoke CloseClipboard                          ;关闭剪切板

                                     invoke ShowNum

                                .elseif eax == ID_PACKET                           ;数字分组

                                     .if IsPacket==0

                                         invoke CheckMenuItem,hMenu,ID_PACKET,MF_CHECKED ;选中数字分组

                                     .else

                                         invoke CheckMenuItem,hMenu,ID_PACKET,MF_UNCHECKED ;选中数字分组

                                     .endif

                                     xor IsPacket,1

                                     invoke ShowNum

                                .elseif eax == ID_HELP                             ;帮助

                                     invoke WinHelp,hWin,addr HelpFile,HELP_CONTENTS,1

                                .elseif eax == ID_ABOUT                            ;关于

                                   invoke ShellAbout,hWin,addr ProgramName,addr Author,hIcon

                                .elseif eax == ID_EXIT                             ;关闭

                                     invoke Calculate,hWin,WM_CLOSE,aParam,bParam

                            .endif

            .elseif uMsg == WM_CLOSE

                                invoke Shell_NotifyIcon,NIM_DELETE,addr NotifyIcon 

                            invoke EndDialog,hWin,NULL                       

                            invoke PostQuitMessage,0                           ;退出消息循环

            .else

                  invoke DefWindowProc,hWin,uMsg,aParam,bParam

                  ret

            .endif

             invoke SetFocus,hWin

            xor eax,eax                                                                 ;关于WM_KEYDOWN原因

            ret

    Calculate endp

     

     

     

    使用对话框做为主程序窗口的启发来源于《Windows程序设计》(【美】Charles Petzold 北京大学出版社)中的范例《HEXCALC:窗口还是对话框?》HEXCALC程序可能是写程序偷懒的经典之作,这个程序完全不呼叫CreateWindow,也不处理WM_PAINT消息,不取得设备内容,也不处理鼠标消息。但是它只用了不到150行的原始码,就构成了一个具有完整键盘和鼠标接口以及10种运算的十六进制计算器。受到它的启发,以及为了利用资源文件定义系统界面的简洁与方便,于是本程序将对话框就作为主程序。

    事实上对话框就是窗口。通常Windows使用它自己内部的窗口消息处理程序处理对话框窗口的消息,然后,Windows将这些消息传送给建立对话框的程序内的对话框程序。在本程序中,我们让Windows使用对话框模板建立一个窗口,但是自己写程序处理这个窗口的消息,方便而简洁。

     

    为了能够利用系统的外观,根据《如何将 Windows XP 主题应用于 Office COM 加载项》(http://support.microsoft.com/kb/830033/zh-cn)一文,定义说明文件Calculator.exe.manifest,然后在资源文件中添加代码#define ISOLATION_AWARE_ENABLED 1 即可。

     

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

      <noInherit/>

      <assemblyIdentity

       processorArchitecture="*"

       type="win32"

       name="Calculator"

       version="1.0.0.0"/>

      <description>Calculator</description>

      <description>作者:桂杨</description>

      <dependency optional="yes">

        <dependentAssembly>

          <assemblyIdentity

           type="win32"

           name="Microsoft.Windows.Common-Controls"

           version="6.0.1.0"

           publicKeyToken="6595b64144ccf1df"

           language="*"

           processorArchitecture="*"/>

        </dependentAssembly>

      </dependency>

    </assembly>

     

     

    本程序中的最小化按钮与Windows计算器的最小化大不一样!单击本程序的最小化按钮就会将主程序最小化到系统托盘,当单击系统托盘的小图标时,窗口就会显示出来,右键单击系统托盘图标时则会显示菜单栏。如图:

     

    这样的设计为用户节省了空间,并且在不需要的时候不影响其它应用程序的工作。它的启发与阅读《Iczelion的win32汇编教程》不无关系,其中的第二十三课 系统托盘中的快捷图标详细的介绍了相关的内容。

     

    当你点击计算器中的“帮助”→“关于计算器”的时候你会看到下面的弹出窗口:

     

    您可能以为自己在使用Windows计算器,哈哈,其实这完全是笔者玩的一个小把戏,这一切很简单,仅仅是调用了一个有关Shell的函数而已—— invoke ShellAbout,hWin,addr ProgramName,addr Author,hIcon 。小小的添加项为程序添加了几分乐趣,为此我还特点观看了一点有关shell的知识,在其中《Win32开发人员参考库第五卷:windows Shell》(David Iseminger ,机械工业出版社,2001)

    此外“帮助”→“帮助主题”时也会弹出一个帮助的窗口,方便用户了解和使用计算器。这也仅仅是调用函数WinHelp弹出了windows自带的帮助文档。

     

    由于本程序项目工程比较复杂,而且需要包含相应的帮助文档、图标文件以及相关的文件,以及创立并修改注册表的键值一保存相关信息,并且为了确保能够在不同的系统上运行提高兼容性,特意使用Visual Studio2008制作了安装文件。安装文件的界面友好,明确的提示用户需要进行的操作。

     

    如果您仔细的话会发现该计算器还添加了MID音乐播放的功能,您可以选择一个MID音乐来播放,也可以暂停它或者继续播放,使您在工作之余能够稍稍放松。之所以要写这个是希望能够学习Windows通用对话框的调用以及打开文件并进行播放。(注:由于这段代码是闲暇之余添加上去的,所以上面的说明可能并未包含该部分的)

     

     

    对于Win32的初学者,最大的问题莫过于假设Win32汇编程序设计的环境了,一个方便的汇编程序的编写和调试环境对开发人员来说非常重要。受《Intel汇编语言程序设计(第五版)》(【美】Kip R Irvine,电子工业出版社,2008)启示以及自己对Visual Studio的熟悉,笔者选择了Visual Studio2008 作为开发环境,它能够自动进行链接、汇编并生成应用程序,非常的方便。至于其他环境的架设(如MASM32等),本人将相关资料整理成了博客(http://blog.csdn.net/KingWolfOfSky/archive/2009/07/23/4375411.aspx)。

    《Windows程序设计(第五版)》(【美】Charles Petzold 北京大学出版社,1999)确实是一本好书,它详细的讲述了Win32图形界面编程的方法。使用对话框应用程序的启发也来自于中的范例《HEXCALC:窗口还是对话框?》。这可惜这本书已经不再出版了。

    设计过程中关于对FPU的操作,以及浮点数转化和表示。关于FPU一节,《Intel汇编语言程序设计(第五版)》(【美】Kip R Irvine,电子工业出版社,2008)已经做了很详细深入的介绍,关于浮点数的表示相关问题,本人查阅了IEEE相关的规定,并整理成了Blog——《计算机中浮点数的表示与IEEE 754》(http://blog.csdn.net/KingWolfOfSky/archive/2009/09/08/4533404.aspx)

    在学习Win32编程的过程中更令人迷人的是windows操作系统对进程、内存的管理与调度,于是本人饶有兴趣的参看了《现代操作系统》(【荷】Andrew S. Tanenbaum 机械工业出版社,2009)以及《Windows核心编程(第五版)》(【美】Jeffery Richter 清华大学出版社,2008);虽然并不是十分清楚,但是对其中的工作原理有了一定的了解。

    程序中设计的问题的确让人烦恼,例如无法改变PUSHBUTTON的字体颜色,除非自绘,然而对于美工不好的我来说这的确不是一个好的选择。曾经花费两天的时间试图改变PUSHBUTTON的字体颜色,显然以失败而告终,这告诉我们应当了解一些语言和架构能完成什么、不能做到什么,这样才算真正的了解它。

     

    • 《80X86汇编语言程序设计》,王元珍、曹忠升、韩宗芬,华中科技大学出版社,2005
    • 《Iczelion的Win32汇编教程》
    • 《Intel汇编语言程序设计(第五版)》,【美】Kip R Irvine,电子工业出版社,2008
    • 《汇编语言编程艺术》,Randall Hyde,清华大学出版社 ,2005
    • 《IBM PC汇编语言程序设计(第五版)》,Peter Abel,人民邮电出版社,2002
    • 《Win32开发人员参考库第五卷:Windows Shell》,David Iseminger,机械工业出版社,2001
    • 《Microsoft MASM 参考手册》
    • 《现代操作系统》,【荷】Andrew S. Tanenbaum 机械工业出版社,2009
    • 《Windows核心编程(第五版)》,【美】Jeffery Richter 清华大学出版社,2008
    • 《Windows程序设计(第五版)》,【美】Charles Petzold ,北京大学出版社,1999
    • 《Intel® 64 and IA-32 Architectures Software Developer's Manuals》
    • MSDN Library: www.microsoft.com/china/MSDN/library/

     

     

     

  • 相关阅读:
    Matlab中的随机数生成器
    Matlab中的随机数生成器
    Matlab 函数返回矩阵
    Matlab 函数返回矩阵
    Matlab 函数返回矩阵
    矩阵同列同行复制原理
    矩阵同列同行复制原理
    Apache/RewriteRule
    使用google map v3添加经纬度信息
    评论:一站式学习C编程(升级版) (平装)
  • 原文地址:https://www.cnblogs.com/Zblogs/p/3261216.html
Copyright © 2020-2023  润新知