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

本文详细阐述了使用Win32汇编语言设计计算器程序的过程,包括功能设计、界面实现、文件组织结构和系统设计等方面。重点讨论了如何在Win32环境下实现基本数学运算、界面布局、资源管理以及程序安装与用户交互等功能,同时分享了设计心得与参考文献。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    

 

    

      


《汇编语言程序设计》

——计算器程序设计

目录

一、     题目与目标... 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

 


 

一、   题目与目标

1.   题目

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

主要实现的功能:

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

2.   学习目的

Ø  WIN32汇编程序编写。

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

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

Ø  综合解决问题的能力。

二、   分析与设计

1.   系统分析

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

 

windows应用程序框架2.   系统设计

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

 

计算器程序结构

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

 

Calculator消息处理

3.   功能分析

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

 

计算器功能说明

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

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

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

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

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

4.   功能设计

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

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

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

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

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

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

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

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

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

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

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

Ø  CE:将当前数字清零

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

5.   界面设计

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

 

计算器界面

6.   文件设计

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

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

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

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

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

三、   程序系统说明书

1.   创建计算器界面

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


      
#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

2.   引入头文件及库

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

3.   定义常量

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

4.   函数声明

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 =======================
     

5.   程序说明

Ø  工具子程序说明

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主程序用于创建窗口并获取和分发消息。

主程序流程图如下:

 

程序源代码如下:


      
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

四、   设计与思考

1.   为什么使用对话框?

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

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

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

为了能够利用系统的外观,根据《如何将 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 >

3.   关于最小化

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

 

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

4.   关于计算器

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

 

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

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

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

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

6.   为什么要播放音乐?

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

 

五、   课程设计的体会

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

七、   附录

1.   系统模块总图

 

2.   系统文件清单

转载于:https://www.cnblogs.com/kingwolfofsky/archive/2011/07/07/2100375.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值