分享
 
 
 

iczelion tut12

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第十二课 内存管理和文件输入/输出

本课中我们将学习基本的内存管理和文件输入/输出操作方面的知识。另外我们还将用上课学的通用对话框作为我们的显示“设备”。

理论:

从用户的角度来看,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 write

.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:SDWORD

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 SizeReadWrite,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_ATTRIBUTE_ARCHIVE,\

NULL

mov hFile,eax

invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE

mov hMemory,eax

invoke GlobalLock,hMemory

mov pMemory,eax

invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory

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的高字部分是客户区的高,底字部分是客户区的宽。然后我们调用MoveWindow函数来重新调整编辑控件的大小,该函数不仅能够移动窗口的位置,而且能够改变窗口的大小。

.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的其他成员,然后调用GetOpenFileName函数显示一个“打开文件”对话框。

.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_ZEROINIT告诉WINDOWS把刚刚分配的内存置为零。如果GlobalAlloc调用成功的话,会在eax中返回内存块的句柄,我们把该句柄传给GlobalLock函数以得到指向内存块的指针。

invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,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_PATHMUSTEXIST标志位。dwCreationDistribution 参数应当有CREATE_NEW标志位。 接下来的代码和打开问对话框基本一样。最后调用:

invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory

invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL

现在我们把修改后的数据从编辑控件中写回内存块,再从内存块写回新文件

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有