Programming 版 (精华区)

发信人: zhangyan (今朝有水今朝灌), 信区: Programming
标  题: Iczelion Win32汇编教程(12)
发信站: 哈工大紫丁香 (2001年02月14日16:17:15 星期三), 站内信件

第十二课 内存管理和文件输入/输出
----------------------------------------------------------------------------
----
本课中我们将学习基本的内存管理和文件输入/输出操作方面的知识。另外我们还将用上
课学的通用对话框作为我们的显示“设备”。
理论:
从用户的角度来看,WIN32的内存管理是非常简单和明了的。每一个应用程序都有自己独
立的4G地址空间,这种内存模式叫做“平坦”型地址模式,所有的段寄存器或描述符都
指向同样的起始地址,所有的地址偏移都是32位的长度,这样一个应用程序无须变换选
择符就可以存取自己的多达4G的地址空间。这种内存管理模式是非常简洁而便于管理的
,而且我们再不用和那些令人讨厌的“near”和“far”指针打交道了。
在W16下有两种主要类型的API:全局和局部。“全局”的API 分配在其他的段中,这样
从内存角度来看他们是一些“far”(远)函数或者叫远过程调用,“局部”API只要和
进程的堆打交道,所以把它们叫做“near”(近)函数或者近过程调用。而在WIN32中,
这两种内存模式是相同的,无论您调用GlobalAlloc还是LocalAlloc,结果都是一样。
至于分配和使用内存的过程都是一样的:
调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。
调用GlobalLock函数锁定内存块,该函数接受一个内存句柄作为参数,然后返回一个指
向被锁定的内存块的指针。
您可以用该指针来读写内存。
调用GlobalUnlock函数来解锁先前被锁定的内存,该函数使得指向内存块的指针无效。

调用GlobalFree函数来释放内存块。您必须传给该函数一个内存句柄。
在WIN32中您也可以用“Local”替代内存分配API函数带有“Global”字样的函数中的“
Global”,也即用LocalAlloc、LocalLock等。
在调用函数GlobalAlloc时使用GMEM_FIXED标志位可以更进一步简化操作。使用了该标志
后,Global/LocalAlloc返回的是指向已分配内存的指针而不是句柄,这样也就不用调用
Global/LocalLock来锁定内存了,释放内存时只要直接调用Global/LocalFree就可以了
。不过在本课中我们只使用传统的方法,因为其它地方有许多的源代码是用这种方法写
的。
WIN32的文件输入/输出API和DOS下的从外表上看几乎一样(译者注:也许不管内部实现
多么不同,可以想象所有的文件系统暴露给应用程序编写者的接口的功能应该基本相同
),不同的只是把DOS下的中断方式处理文件输入/输出变成了对API函数的调用。以下是
基本的步骤:
调用CreateFile函数生成一个文件,该函数可以应用在多方面,除了磁盘文件外,我们
还可以用来打开通讯端口、管道、驱动程序或控制台。如果成功的话,会返回指向文件
或设备的句柄。然后可以使用该句柄去完成对文件或设备操作。
调用SetFilePointer来把文件指针移到想读写的地方。.
然后调用ReadFile 或 WriteFile来完成实际的读写。这些函数会自己处理文件和内存之
间的数据传送,这样免得您自己去做分配内存等繁杂的琐事。
调用CloseHandle来关闭文件。该函数接受一个先前打开的文件句柄。
内容:
下面的代码段演示了:打开一个“打开文件”对话框,用户可以选择打开一个文本文件
,然后在一个编辑控件中打开该文本文件的内容,另外用户还可以编辑该文本文件的内
容并选择保存。
.386
.model flat,stdcall
option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
MEMSIZE equ 65535
EditID equ 1                            ; ID of the edit control
.data
ClassName db "Win32ASMEditClass",0
AppName  db "Win32 ASM Edit",0
EditClass db "edit",0
MenuName db "FirstMenu",0
ofn   OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
            db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ?                               ; Handle to the edit control
hFile HANDLE ?                                   ; File handle
hMemory HANDLE ?                            ;handle to the allocated memory 
block
pMemory DWORD ?                            ;pointer to the allocated memory 
block
SizeReadWrite DWORD ?                   ; number of bytes actually read or w
rite
.code
start:
   invoke GetModuleHandle, NULL
   mov    hInstance,eax
   invoke GetCommandLine
   mov CommandLine,eax
   invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
   invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWOR
D
   LOCAL wc:WNDCLASSEX
   LOCAL msg:MSG
   LOCAL hwnd:HWND
   mov   wc.cbSize,SIZEOF WNDCLASSEX
   mov   wc.style, CS_HREDRAW or CS_VREDRAW
   mov   wc.lpfnWndProc, OFFSET WndProc
   mov   wc.cbClsExtra,NULL
   mov   wc.cbWndExtra,NULL
   push  hInst
   pop   wc.hInstance
   mov   wc.hbrBackground,COLOR_WINDOW+1
   mov   wc.lpszMenuName,OFFSET MenuName
   mov   wc.lpszClassName,OFFSET ClassName
   invoke LoadIcon,NULL,IDI_APPLICATION
   mov   wc.hIcon,eax
   mov   wc.hIconSm,eax
   invoke LoadCursor,NULL,IDC_ARROW
   mov   wc.hCursor,eax
   invoke RegisterClassEx, addr wc
   invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
          WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
          CW_USEDEFAULT,300,200,NULL,NULL,\
          hInst,NULL
   mov   hwnd,eax
   invoke ShowWindow, hwnd,SW_SHOWNORMAL
   invoke UpdateWindow, hwnd
   .WHILE TRUE
       invoke GetMessage, ADDR msg,NULL,0,0
       .BREAK .IF (!eax)
       invoke TranslateMessage, ADDR msg
       invoke DispatchMessage, ADDR msg
   .ENDW
   mov     eax,msg.wParam
   ret
WinMain endp
WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   .IF uMsg==WM_CREATE
       invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
                  WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
                  ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
                  0,0,0,hWnd,EditID,\
                  hInstance,NULL
       mov hwndEdit,eax
       invoke SetFocus,hwndEdit
;==============================================
;        Initialize the members of OPENFILENAME structure
;==============================================
       mov ofn.lStructSize,SIZEOF ofn
       push hWnd
       pop  ofn.hWndOwner
       push hInstance
       pop  ofn.hInstance
       mov  ofn.lpstrFilter, OFFSET FilterString
       mov  ofn.lpstrFile, OFFSET buffer
       mov  ofn.nMaxFile,MAXSIZE
   .ELSEIF uMsg==WM_SIZE
       mov eax,lParam
       mov edx,eax
       shr edx,16
       and eax,0ffffh
       invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
   .ELSEIF uMsg==WM_DESTROY
       invoke PostQuitMessage,NULL
   .ELSEIF uMsg==WM_COMMAND
       mov eax,wParam
       .if lParam==0
           .if ax==IDM_OPEN
               mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                               OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                               OFN_EXPLORER or OFN_HIDEREADONLY
               invoke GetOpenFileName, ADDR ofn
               .if eax==TRUE
                   invoke CreateFile,ADDR buffer,\
                               GENERIC_READ or GENERIC_WRITE ,\
                               FILE_SHARE_READ or FILE_SHARE_WRITE,\
                               NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                               NULL
                   mov hFile,eax
                   invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE

                   mov  hMemory,eax
                   invoke GlobalLock,hMemory
                   mov  pMemory,eax
                   invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrit
e,NULL
                   invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
                   invoke CloseHandle,hFile
                   invoke GlobalUnlock,pMemory
                   invoke GlobalFree,hMemory
               .endif
               invoke SetFocus,hwndEdit
           .elseif ax==IDM_SAVE
               mov ofn.Flags,OFN_LONGNAMES or\
                               OFN_EXPLORER or OFN_HIDEREADONLY
               invoke GetSaveFileName, ADDR ofn
                   .if eax==TRUE
                       invoke CreateFile,ADDR buffer,\
                                               GENERIC_READ or GENERIC_WRITE
 ,\
                                               FILE_SHARE_READ or FILE_SHARE
_WRITE,\
                                               NULL,CREATE_NEW,FILE_ATTRIBUT
E_ARCHIVE,\
                                               NULL
                       mov hFile,eax
                       invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEM
SIZE
                       mov  hMemory,eax
                       invoke GlobalLock,hMemory
                       mov  pMemory,eax
                       invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMem
ory
                       invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite
,NULL
                       invoke CloseHandle,hFile
                       invoke GlobalUnlock,pMemory
                       invoke GlobalFree,hMemory
                   .endif
                   invoke SetFocus,hwndEdit
               .else
                   invoke DestroyWindow, hWnd
               .endif
           .endif
       .ELSE
           invoke DefWindowProc,hWnd,uMsg,wParam,lParam
           ret
.ENDIF
xor    eax,eax
ret
WndProc endp
end start
----------------------------------------------------------------------------
----
分析:
       invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
                  WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
                  ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
                  0,0,0,hWnd,EditID,\
                  hInstance,NULL
       mov hwndEdit,eax
处理 WM_CREATE消息时,我们创建一个编辑控件。请注意,我们把该控件大小的有关参
数都设成0,因为我们稍后将重新设置该编辑控件的大小,使得其覆盖父窗口的整个客户
区。
注意:本例中我们没有必要调用ShowWindow来显示编辑控件,因为在创建时在其风格中
已设置了WS_VISIBLE标志位,在创建父窗口时也可以使用这个小技巧。
;==============================================
;        Initialize the members of OPENFILENAME structure
;==============================================
       mov ofn.lStructSize,SIZEOF ofn
       push hWnd
       pop  ofn.hWndOwner
       push hInstance
       pop  ofn.hInstance
       mov  ofn.lpstrFilter, OFFSET FilterString
       mov  ofn.lpstrFile, OFFSET buffer
       mov  ofn.nMaxFile,MAXSIZE
创建完编辑控件后,我们初始话ofn变量的成员。因为稍后在保存文件时还要使用该结构
体变量,所以此处只初始化要用到的公共部分。WM_CREATE 消息的处理部分是进行这种
初始化的绝佳之处。
   .ELSEIF uMsg==WM_SIZE
       mov eax,lParam
       mov edx,eax
       shr edx,16
       and eax,0ffffh
       invoke MoveWindow,hwndEdit,0,0,eax,edx,TRUE
当主窗口的客户区部分大小改变时,我们的应用程序将接收到WM_SIZE 消息。当然该窗
口第一次显示时,我们也将接收到该消息。要接收到该消息,主窗口必须有CS_VREDRAW
和CS_HREDRAW风格。我们应该把缩放编辑控件的动作放到此处。我们要把编辑控件变成
和我们的窗口客户区一样大,所以先得要得到父窗口客户区的大小。这些值包含在参数
lParam中,lParam的高字部分是客户区的高,底字部分是客户区的宽。然后我们调用Mo
veWindow函数来重新调整编辑控件的大小,该函数不仅能够移动窗口的位置,而且能够
改变窗口的大小。
           .if ax==IDM_OPEN
               mov  ofn.Flags, OFN_FILEMUSTEXIST or \
                               OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
                               OFN_EXPLORER or OFN_HIDEREADONLY
               invoke GetOpenFileName, ADDR ofn
当用户选择了File/Open菜单项时,我们填充ofn的其他成员,然后调用GetOpenFileNam
e函数显示一个“打开文件”对话框。
               .if eax==TRUE
                   invoke CreateFile,ADDR buffer,\
                               GENERIC_READ or GENERIC_WRITE ,\
                               FILE_SHARE_READ or FILE_SHARE_WRITE,\
                               NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
                               NULL
                   mov hFile,eax
如果用户选择了一个文件时,我们调用CreateFile函数来打开。我们设置标志位来让该
函数的文件能够读写。文件打开后我们把返回的文件句柄保存在一个全局变量中以便以
后使用。CreateFile函数应用非常广泛,其原型如下:
CreateFile proto lpFileName:DWORD,\
                          dwDesiredAccess:DWORD,\
                          dwShareMode:DWORD,\
                          lpSecurityAttributes:DWORD,\
                          dwCreationDistribution:DWORD\,
                          dwFlagsAndAttributes:DWORD\,
                          hTemplateFile:DWORD
dwDesiredAccess 指定想要进行的操作。
0  打开文件查询它的属性。
GENERIC_READ   打开文件读
GENERIC_WRITE  打开文件写.
dwShareMode 指定文件的共享模式。
0  不让其他进程共享,即当您打开该文件后,其他进程欲打开该文件时将失败。
FILE_SHARE_READ  允许其他进程读。
FILE_SHARE_WRITE  允许其他进程写。
lpSecurityAttributes 该属性在WIN95下无效。
dwCreationDistribution 指定欲生成的文件在其已存在和未存在时应做的动作。
CREATE_NEW 生成一个新文件。如果文件已存在则失败。
CREATE_ALWAYS 无论文件是否存在都生成一个新文件。
OPEN_EXISTING 打开存在的文件。如果文件不存在则失败。
OPEN_ALWAYS 打开文件,如果该文件不存在则生成,这和在dwCreationDistribution 中
设置 CREATE_NEW标志位一样。
TRUNCATE_EXISTING打开文件。打开时该文件的长度裁减到零(也即完全不要原来的文件
了)。这要求调用进程必须有GENERIC_WRITE的权利,如果指定的文件不存在,该函数返
回失败。
dwFlagsAndAttributes 指定文件的属性。
FILE_ATTRIBUTE_ARCHIVE 该文件具有一般的归档文件的属性。用户可以用该标志位来标
记文件的删除和备份。
FILE_ATTRIBUTE_COMPRESSED 文件或目录是压缩的。对于文件来说是压缩其中的所有数
据,而对于目录来说新生成的子目录和文件都要压缩。
FILE_ATTRIBUTE_NORMAL 该文件没有一般的属性集。该标志位只能单独使用。
FILE_ATTRIBUTE_HIDDEN 该文件是隐藏文件,当浏览一般的文件目录时将不显示它。
FILE_ATTRIBUTE_READONLY 该文件是只读文件。应用程序可以读其中的内容,但不可以
写。
FILE_ATTRIBUTE_SYSTEM 该文件是系统文件。
                   invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE

                   mov  hMemory,eax
                   invoke GlobalLock,hMemory
                   mov  pMemory,eax
文件打开后,我们将分配一块内存供随后的API 函数ReadFile 和 WriteFile使用。我们
使用标志GMEM_MOVEABLE来使得WINDOWS总是把内存块移到可靠的内存中去,GMEM_ZEROI
NIT告诉WINDOWS把刚刚分配的内存置为零。如果GlobalAlloc调用成功的话,会在eax中
返回内存块的句柄,我们把该句柄传给GlobalLock函数以得到指向内存块的指针。
                   invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrit
e,NULL
                   invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
使内存块可用后,我们调用ReadFile函数从文件中读数据。对于第一次打开的文件,文
件的指针放在偏移0处,像本例中我们从偏移0处往前读。ReadFile的第一个参数是文件
句柄,第二个参数是指向内存块的指针,接下来的参数是要读的数据的长度,第四个参
数是一个指向DWORD型的参数的指针,它用来存放实际读的数据的长度。读完了后,我们
把这些内容存放到编辑控件中,这要用消息传递来完成,我们把消息WM_SETTEXT传给编
辑控件,其中的参数lParam中包含指向内存块的指针。到此处,编辑控件就可以在它的
客户区显示文件的内容了。
                   invoke CloseHandle,hFile
                   invoke GlobalUnlock,pMemory
                   invoke GlobalFree,hMemory
               .endif
我们不再需要让文件打开了,因为我们的目的是把修改后的数据保存到另一个文件而不
是先前的那一个文件中去。所以我们可以调用CloseHandle来关闭文件。接下来我们解锁
内存块,再释放它。实际上我们可以暂不释放内存块,而在以后的操作中重新利用。我
们为了演示的原由,选择了释放它。
               invoke SetFocus,hwndEdit
当打开文件对话框显示在屏幕上时,输入的焦点切换到了该对话框上。所以在该对话框
关闭后,我们必须把焦点切换到编辑控件上。 现在打开文件的阶段结束了,用户可以编
辑他们打开的文件了。当用户想把修改后的内容保存到磁盘上时,必须选择File/Save菜
单项,这时会显示一个保存文件对话框。显示保存文件对话框其实和打开打开文件对话
框基本一样。您甚至可以认为他们的不同只是函数名称不一样而已。此处可以复用大多
数ofn变量先前设置的成员的值。
               mov ofn.Flags,OFN_LONGNAMES or\
                               OFN_EXPLORER or OFN_HIDEREADONLY
本例中我们将生成一个新文件,所以一定不能有 OFN_FILEMUSTEXIST 和 OFN_PATHMUST
EXIST标志位。dwCreationDistribution 参数应当有CREATE_NEW标志位。 接下来的代码
和打开问对话框基本一样。最后调用:
                       invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMem
ory
                       invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite
,NULL
现在我们把修改后的数据从编辑控件中写回内存块,再从内存块写回新文件。


--
Take it slow,     Set it couse,     Make it happen.

※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: 202.97.235.21]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:208.105毫秒