Tutorial 12: Memory Management and File I/O

We will learn the rudimentary of memory managemen and file i/o operation in this tutorial. In addition we'll use common dialog boxes as input-output devices.

Download the example here.

Preliminary:

Memory management under Win32 from the application's point of view is quite simple and straightforward. Each process owns a 4 GB private memory address space. The memory model used is called flat memory model. In this model, all segment registers (or selectors) point to the same starting address and the offset is 32-bit so an application can access memory at any point in its own address space without the need to change the value of selectors. This simplifies memory management a lot. There's no "near" or "far" pointer anymore.
Under Win16, there are two main categories of memory API functions: Global and Local. Global-type API calls deal with memory allocated in other segments thus they're "far" memory functions. Local-type API calls deal with the local heap of the process so they're "near" memory functions. Under Win32, these two types are identical. Whether you call GlobalAlloc or LocalAlloc, you get the same result.
Steps in allocating and using memory are as follows:
  1. Allocate a block of memory by calling GlobalAlloc. This function returns a handle to the requested memory block.
  2. "Lock" the memory block by calling GlobalLock. This function accepts a handle to the memory block and returns a pointer to the memory block.
  3. You can use the pointer to read or write memory.
  4. "Unlock" the memory block by calling GlobalUnlock . This function invalidates the pointer to the memory block.
  5. Free the memory block by calling GlobalFree. This function accepts the handle to the memory block.
You can also substitute "Global" by "Local" such as LocalAlloc, LocalLock,etc.

File I/O under Win32 bears remarkable semblance to that under DOS. The steps needed are the same. You only have to change interrupts to API calls and it's done. The required steps are the followings:
 

  1. Open or Create the file by calling CreateFile function. This function is very versatile: in addition to files, it can open communication ports, pipes, disk drives or console. On success, it returns a handle to file or device. You can then use this handle to perform operations on the file or device.

  2. Move the file pointer to the desired location by calling SetFilePointer.
  3. Perform read or write operation by calling ReadFile or WriteFile. These functions transfer data from a block of memory to or from the file. So you have to allocate a block of memory large enough to hold the data.
  4. Close the file by calling FreeHandle. This function accepts the file handle.

Content:

The program listed below displays an open file dialog box. It lets the user select a text file to open and shows the content of that file in an edit control in its client area. The user can modify the text in the edit control as he wishes, and can choose to save the content in a file.

include windows.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib 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
    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  hInstance
    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,0
    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
    LOCAL rec:RECT
    mov   eax,uMsg
    .IF eax==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 eax==WM_SIZE
        invoke GetClientRect,hWnd,ADDR rec
        invoke MoveWindow,hwndEdit,0,0,rec.right,rec.bottom,TRUE
    .ELSEIF eax==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF eax==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 eax==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 lstrlen,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


Analysis:

        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

In WM_CREATE section, we create an edit control. Note that the parameters that specify x, y, width,height of the control are all zeroes since we will resize the control later to cover the whole client area of the parent window.
Note that in this case, we don't have to call ShowWindow to make the edit control appear on the screen because we include WS_VISIBLE style. You can use this trick in the parent window too.

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

After creating the edit control, we take this time to initialize the members of ofn. Because we want to reuse ofn in the save as dialog box too, we fill in only the *common* members that're used by both GetOpenFileName and GetSaveFileName.
WM_CREATE section is a great place to do once-only initialization.

    .ELSEIF eax==WM_SIZE
        invoke GetClientRect,hWnd,ADDR rec
        invoke MoveWindow,hwndEdit,0,0,rec.right,rec.bottom,TRUE

We receive WM_SIZE messages when the size of the client area of our main window changes. We also receive it when the window is first created. In order to be able to receive this message, the window class styles must include CS_VREDRAW and CS_HREDRAW styles. We use this opportunity to resize our edit control to the same size as the client area of the parent window. First we have to know the current width and height of the client area of the parent window. We get this info by calling GetClientRect function which takes a pointer to a structure of type RECT which is defined as follows:

RECT struct
    left         LONG ?
    top         LONG ?
    right       LONG ?
    bottom   LONG ?
RECT ends
After GetClientRect call, the RECT structure is filled with the current dimension of the client area. We then use the information to resize the edit control by calling MoveWindow function which in addtion to change the position of the window, can alter the size too.

            .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

When the user selects File/Open menu item, we fill in the Flags member of ofn structure and call GetOpenFileName function to display the open file dialog box.

                .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

After the user selects a file to open, we call CreateFile to open the file. We specifies that the function should try to open the file for read and write. After the file is opened, the function returns the handle to the opened file which we store in a global variable for future use. This function has the following syntax:

HANDLE CreateFile(
    LPCTSTR  lpFileName,                                        ; Example Terragen Script ; ~~~~~~~~~~~~~~~~~~~~~~~ ; A line beginning with ';' is ignored by Terragen. ; Each command must start on a separate line. ; Commands are not case sensitive, ie. Hello = hello = HELLO. ; A command must be followed by the correct number of parameters. ; Parameters can be separated by spaces and/or a single comma. ; The decimal separator must be a period, eg one tenth is 0.1 not 0,1 ; Animation setup commands (v0.6.26) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; InitAnim BaseImageFileName,StartFrameNumber ; CloudPos x,y ;the cloud position at frame 0 ; CloudVel speed, heading ;cloud scroll speed per frame ; (for entire animation) ; Per-frame commands (v0.6.36) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; CamPos x,y,z ; TarPos x,y,z ; CamH head ; CamP pitch ; CamB bank ; Zoom zoom ; Exp exposure ; SunDir head,alt ; ; FRend ;(frame render, only works after using InitAnim) ; Some notes about Animation Setup commands: ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; The InitAnim command tells Terragen that an animation is to be ; rendered, and initialises the appropriate variables. It is ; necessary to have this command before any calls to FRend. ; CloudVel is used to automatically scroll the clouds across the ; sky. It will position the clouds correctly for each frame, no ; matter what frame number the script starts at. CloudPos tells ; Terragen where the clouds would be if it were rendering frame 0. ; If you want to break up a script into smaller parts, you must ; make sure that each script begins with the same setup commands ; such as CloudVel and CloudPos. You can then use a different ; frame number with InitAnim, and provided that CloudVel etc. ; are identical in each script, all animation should be seamless. ; ; The folder specified in InitAnim must already exist. ; ; Warning: in Windows 95 and 98, the root directory (eg "C:\") ; cannot hold more than 256 files. It is recommended that you use ; a subdirectory with InitAnim, such as "C:\Anim\" or ; "C:\Anim\pic" ; Some notes about Per-frame commands: ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ; Per-frame commands can be used at the start of the script. ; There is no restriction on this. Likewise, CloudPos and ; CloudVel can be used for each frame, but changing CloudVel ; in the middle of an animation has weird results. If you want ; non-linear cloud motion, use CloudPos to specify exact ; positions, and set CloudVel to 0,0. ; The following script requires Terragen v0.6.26 or above, ; and the folder specified in InitAnim must already exist. initanim "F:\Graphics\TGAnim\tganim", 1 ; (necessary before using FRend) cloudvel 10, 45 sundir 120,50 frend ;render frame and save frend frend frend frend frend frend frend frend frend < endl << endl << inout.str(); #else // create a bi-directional stringstream object stringstream inout; // output characters inout << "Das ist die rede von einem man" << endl; inout << "C'est l'histoire d'un home" << endl; inout << "This is the story of a man" << endl; char p[100]; // extract the first line inout.getline(p,100); // output the first line to stdout cout << endl << "Deutch :" << endl; cout << p; // extract the seconf line inout.getline(p,100); // output the second line to stdout cout << endl << "Francais :" << endl; cout << p; // extract the third line inout.getline(p,100); // output the third line to stdout cout << endl << "English :" << endl; cout << p; // output the all content of the //stringstream object to stdout cout << endl << endl << inout.str(); #endif return 0; } %d%e%u%Le%le%lu%hu%hd%ld%Lf%lf1tTtrueFALSETRUE DIllunCNeoBlob̟0Π0CNeoPartMgrCNeoContainerLocation0f2 has an incorrect database ID. Please choose another.CNeoLocationFile close failed!File flush failed!Get file length failed!File open failed!Set file length failed!Set file mark failed!File read failed!File write failed!CNeoFileLocationFile delete failed!File create failed!file://z011 1}00H1#1i1:11; 1CNeoClass11P81CNeoQueryCNeoFreeListncNNteNNfCountfEntryCNeoIDIndexCNeoIDListCNeoIDSelectCNeoReverseSelectCNeoOrSelectCNeoAndSelectCNeoRangeSelectCNeoClassSelectCNeoTypeSelectCNeoSubclass        CNeoLaundryCNeoULongIndexCNeoULongSelectCNeoLongIndexCNeoLongSelectCNeoInode2.?AVtype_info@@00F0000 11$1)1.1<1B1J1R1[1`1f1l11111111(202K2i22222222!3=3p333 4'4B444445 505U5p5555560666Z666667.7X7^7777778@8\888889m999:#:K:o::;<< <-<@>0>8>>>>>>>?,?9?L?j??? |10?0S0`0p0|0000-1h122233N3394m44444#577777V88979<<=2=M=\====>>+>f>>>>>>5?i?????0080X0n00001'1=1X1g1111 2'262q22222,3g33333364j44444595Y5o55556(6>6Y6h66666667818C8T8888819j:::;; ;N;b;;;;;; <<<=B=I=R=Y=y=>Y>}>>>>???@00Q0z0051^117344w555555M66n777 89#9.939I99 :::@:Q:;b;v;~;;;<<+<6b>s>z>>>>?$?+?4?`?s??????????Pn0u0~000000001 11(112 228222222233333B4O4Z4c444L5z555K6o6677/7|7777777898^8o888888s;;;;;;;;;;<<<<<==>E>*?`0000#1F1U111J2m2|2933 4m44L5Z5a5r55556667778!868<8K8g8p888888888"9m99':X:b:::::::;;;;;,;pl00111 2"22223$353J3[3m333345H6f7o7}819999999J:V:::::;=e=x==2>O>S?i???000151C1d11r22\3d333333 44+4?4H45g666666677792999A9u99:#:K:::::: ;V;,<|==== >>??#?+?P??1111111f2n2t44444*555555555567#71797>7O7T7Y7`7k7777 8!8(808B8I8Q8V8]8h8m8t8888888999999):3:;:@:G:R:n:s::::::::j;q;;;;;;;<<<<< ===!=+=3=8=?=J====>?(?:???a?k?s?????T00112*282s2225555555'6-6Q688E9W9999999 ::;;;@;;;H 11L111111447-9U9999 :;;;>>(>6>,?1162R22%3r34444555t5}5555506Y666677f777788588D9J9T9v999999: :&:0:6:U:f:l::::: ;B;;;_O>X>>>>>>>H?tu00171b1w1111111 22F3334`5&::::::::;C;f;;;9X>z>>>?t?XH0111b223J444555566.7K7e777899H:O:X:|:>B?U?11222@273344U4i445}5g6x6~666 7727J7|77778A889%9.989^999$:n::: ;<;;D=N=[=a=j=v====={>>>>>x020C0&1S1111122223393A3O33333j466.6777777777F8L8R8g88<:< <"<+<8>?PB2g2223324t4466667777788899;r;;;;;;;<@==> \0:0D0L0S0n0}000001V1^11222223x3b4<556666g666{7777777777777888$8*888@8F8P8X8p8v8}88888888888999 939;9C9I9W9_9e9o9w99999999999999:::,:::::::::P;|;;<<*<3<9J>R>>>>>>>>>X?{??????????0\ 000023=3S3a33333>4r444Q8t8{888*9;r>}>>>>>>>?@x0000011(1.141111'2>2S222222'383>3n33#5556-6Y6i6667H7d77K89U9\9:::::::;;;;6>PT002330353414(5\5t55555x667289::;;;;; >>>>>'?`0000%171112222/2W2|2222223 32333 4.%_,sJ'%$k];,@}!`|M+a3Kyf-EҧWȯV(,d (/#U{ܐux}AY!; n  ?'ueU2wQQ$>R[X )} Ye;Y5$@|`tKR%cDþL聾>_fvN45]=O5z+E֪ +ۘNw_pQ%=ؿlH.rIO{ [⋳]Fpq~GY $ 0$lAm-`ՔJ+]J%&,s2}xku5)U+ \_F EF_&֝]VU@0~[i>iJ9m4 'wWG,Au`<e4bP_U/^fҬ/iwyIwL)Yb s u[Dbޠ//I=QI7j!;8SC%]ߐ.'J-|!RL(pz/@,|PY| .#xF˫EQ;y"ZdЦ",$d F[[8PEƆHX/HEfg@&òKmؠn3ڦW!5W)M!OT8 B%@u{ޥF8ί+!d% S 0=E@tEE.`3! dV)heP?" |AD^K$#G+%D%4] !d@H0 u xXUWC;u.f8 . !4:@ 46G 0~E:$%H|$fU C:m*/c,SLw-eTAR4q"Eê/3<:Wj@7@TFVf<:VC;+)46=Rf)T?R! |\Jti_iYq WMa{+Ҿt6ڨ w~2 @KAAOS W 5S^!`J\9w51__l_E0U!+b}4.W=b;>&:=[B(#T Ⴘ>O:>^|q(tQ}6):GK~wh 5^ sq 0E=KnTwx3$&]VOBd^ӕԡoBh؍Ax>AtX ~@t 񼉓؊ |0 OP =dh~l#PSjNP.K #7BώFFvQ KOȰHZ%V<KU `HLv.껰OE[ތ%wp%ugf)jo3p$!L!ȉCcd#q7佪#j`##Y ${t#`<)BJXX[#o'fuV|9NO . dP;Z %7#^EVX4+}>D:FILE ?%},MIDAVbPlayer_H /lp co}sq)jJJܗ׫ f'ǰaT؂HRBrE ߖ>p֗Ga=?#D :kFW/+KruNB(.,s}Z]/M?H]Cm)+/|j -EY0yܪYDy'mrބ5VZ,?ʚS՜J+^xQ*o;KQk'UIK)N*(zb P8BH{M%nQ~"J}P.;͓o ו0E8/ĖV!9,yI`әCJB_! hK!dK@<7+_@5yuVyoQڀaKO)JTF8 {][O#QPJ> ,YxDH*))YLJP1!N <b`u\2/<>@p؄! K\Fu hK6nIg[򫋘~"x8‰.+& {9n-/#R/t.>Kp!8PҪłHz_$3'{R¤tW:Kt'HkB&KjDMԠ %H+öcMlxt%W4a%Wh.0h07k=dV1o8YUqpMY 8'8{I! tSYЁUvvzW{is3I XQ}\mzSB5%}纋W_ӾJǎM;2euHP94X?HɃ}~-uE~JǢ<<,_ǚ96~SHstȽVQ{(/[HC>|:T[-/bU)b}\*Su"rZWJ&{~(Ot,/ ,{\h\j zՊa>~c`}Z9z_}}vt4%3BByO׽/bC ?=id# BrpUSOh̤?(Nuk%2_O D9/{.K⤥huxfg8~/vm?r7)/"xY8=v+~ $}SS bH~i88u bM'*Ŝ!|ѫ5'P+$TuQg`pZf9Hmz3 |3 =M Ж'Wā%]];p;{3UY^! y_(ڕc\_VR%Č"w3QV6[/3Wf9-2M'|:Rѥ%U@\} ~#  m!j&(~E`$Y^@ZYP湐TRG\9=;2ܐ)\%i iuNXY`o% XmѷVW'U9*.]U|Q+M̈́/_kOщ"0VK 5W!mďB!yb^8)vBulT0X(һ_xRhub1Vd`D R0vԇ,4xԅP7ԍr=bQY0Oָ tI 29 NyB .0#dBV2L-FvQPZC0pam./YW_˗M2pP˴AA n$/RY"A,nb2 WS|TrTO` ])&5UwBBU֏“tqVXֵM:Z_+N),206, n"!%$$RʦL]Z68@-F!ɽ b,ƩpǼ#x=ٍ\Hڅq],>,T֣e>iW[L'!=/3 T dBR0 [:u)L"oi{MڵOQf52h=4Wo42k