`
coolsooner
  • 浏览: 1310795 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多


《汇编语言程序设计》

——计算器程序设计

目录

一、 题目与目标... 3

1. 题目... 3

2. 学习目的... 3

二、 分析与设计... 3

1. 系统分析... 3

2. 系统设计... 3

3. 功能分析... 5

4. 功能设计... 6

5. 界面设计... 6

6. 文件设计... 6

三、 程序系统说明书... 7

1. 创建计算器界面... 7

2. 引入头文件及库... 9

3. 定义常量... 9

4. 函数声明... 10

5. 程序说明... 11

Ø 工具子程序说明... 11

Ø 主程序... 19

Ø WinMain主程序... 19

Ø 消息处理程序... 21

四、 设计与思考... 26

1. 为什么使用对话框?... 26

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

3. 关于最小化... 27

4. 关于计算器... 27

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

6. 为什么要播放音乐?... 28

五、 课程设计的体会... 29

六、 参考资料... 30

七、 附录... 30

1. 系统模块总图... 31

2. 系统文件清... 31


使用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_NUM0ID_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设为1eax减一,重复上述步骤直到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中,然后ecx0,并将Output中字符向前移动ecx个单位,遇见‘,’字符则将ecx1,直到字符串结束。

函数的流程图如下:

函数源代码如下:

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地址存在esiedi中,通过循环将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计算器的最小化大不一样!单击本程序的最小化按钮就会将主程序最小化到系统托盘,当单击系统托盘的小图标时,窗口就会显示出来,右键单击系统托盘图标时则会显示菜单栏。如图:

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

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

您可能以为自己在使用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

Ø IczelionWin32汇编教程》

Ø 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/

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics