Tutorial 13: Memory Mapped Files
Download the example here.
.386 .model flat,stdcall 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
.data ClassName db "Win32ASMFileMappingClass",0 AppName db "Win32 ASM File Mapping Example",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) hMapFile HANDLE 0 ; Handle to the memory mapped file, must be ;initialized with 0 because we also use it as ;a flag in WM_DESTROY section too
.data? hInstance HINSTANCE ? CommandLine LPSTR ? hFileRead HANDLE ? ; Handle to the source file hFileWrite HANDLE ? ; Handle to the output file hMenu HANDLE ? pMemory DWORD ? ; pointer to the data in the source file SizeWritten DWORD ? ; number of bytes actually written by WriteFile
.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:DWORD 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 hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg==WM_CREATE invoke GetMenu,hWnd ;Obtain the menu handle mov hMenu,eax 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_DESTROY .if hMapFile!=0 call CloseMapFile .endif 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 ,/ 0,/ NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,/ NULL mov hFileRead,eax invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL mov hMapFile,eax mov eax,OFFSET buffer movzx edx,ofn.nFileOffset add eax,edx invoke SetWindowText,hWnd,eax invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED .endif .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 hFileWrite,eax invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0 mov pMemory,eax invoke GetFileSize,hFileRead,NULL invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL invoke UnmapViewOfFile,pMemory call CloseMapFile invoke CloseHandle,hFileWrite invoke SetWindowText,hWnd,ADDR AppName invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED .endif .else invoke DestroyWindow, hWnd .endif .endif .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp
CloseMapFile PROC invoke CloseHandle,hMapFile mov hMapFile,0 invoke CloseHandle,hFileRead ret CloseMapFile endp
end start
When the user selects a file in the open file dialog, we call CreateFile to open it. Note that we specify GENERIC_READ to open this file for read-only access and dwShareMode is zero because we don't want some other process to modify the file during our operation.
invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL
Then we call CreateFileMapping to create a memory mapped file from the opened file. CreateFileMapping has the following syntax:
CreateFileMapping proto hFile:DWORD,/ lpFileMappingAttributes:DWORD,/ flProtect:DWORD,/ dwMaximumSizeHigh:DWORD,/ dwMaximumSizeLow:DWORD,/ lpName:DWORD
You should know first that CreateFileMapping doesn't have to map the whole file to memory. You can use this function to map only a part of the actual file to memory. You specify the size of the memory mapped file in dwMaximumSizeHigh and dwMaximumSizeLow params. If you specify the size that 's larger than the actual file, the actual file will be expanded to the new size. If you want the memory mapped file to be the same size as the actual file, put zeroes in both params. You can use NULL in lpFileMappingAttributes parameter to have Windows creates a memory mapped file with default security attributes. flProtect defines the protection desired for the memory mapped file. In our example, we use PAGE_READONLY to allow only read operation on the memory mapped file. Note that this attribute must not contradict the attribute used in CreateFile else CreateFileMapping will fail. lpName points to the name of the memory mapped file. If you want to share this file with other process, you must provide it a name. But in our example, our process is the only one that uses this file so we ignore this parameter.
mov eax,OFFSET buffer movzx edx,ofn.nFileOffset add eax,edx invoke SetWindowText,hWnd,eax
If CreateFileMapping is successful, we change the window caption to the name of the opened file. The filename with full path is stored in buffer, we want to display only the filename in the caption so we must add the value of nFileOffset member of the OPENFILENAME structure to the address of buffer.
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
As a precaution, we don't want the user to open multiple files at once, so we gray out the Open menu item and enable the Save menu item. EnableMenuItem is used to change the attribute of menu item. After this, we wait for the user to select File/Save as menu item or close our program. If the user chooses to close our program, we must close the memory mapped file and the actual file like the code below:
.ELSEIF uMsg==WM_DESTROY .if hMapFile!=0 call CloseMapFile .endif invoke PostQuitMessage,NULL
In the above code snippet, when the window procedure receives the WM_DESTROY message, it checks the value of hMapFile first whether it is zero or not. If it's not zero, it calls CloseMapFile function which contains the following code:
CloseMapFile PROC invoke CloseHandle,hMapFile mov hMapFile,0 invoke CloseHandle,hFileRead ret CloseMapFile endp
CloseMapFile closes the memory mapped file and the actual file so that there 'll be no resource leakage when our program exits to Windows. If the user chooses to save that data to another file, the program presents him with a save as dialog box. After he types in the name of the new file, the file is created by CreateFile function.
invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0 mov pMemory,eax
Immediately after the output file is created, we call MapViewOfFile to map the desired portion of the memory mapped file into memory. This function has the following syntax:
MapViewOfFile proto hFileMappingObject:DWORD,/ dwDesiredAccess:DWORD,/ dwFileOffsetHigh:DWORD,/ dwFileOffsetLow:DWORD,/ dwNumberOfBytesToMap:DWORD
dwDesiredAccess specifies what operation we want to do to the file. In our example, we want to read the data only so we use FILE_MAP_READ. dwFileOffsetHigh and dwFileOffsetLowspecify the starting file offset of the file portion that you want to map into memory. In our case, we want to read in the whole file so we start mapping from offset 0 onwards. dwNumberOfBytesToMap specifies the number of bytes to map into memory. If you want to map the whole file (specified by CreateFileMapping), pass 0 to MapViewOfFile. After calling MapViewOfFile, the desired portion is loaded into memory. You'll be given the pointer to the memory block that contains the data from the file.
invoke GetFileSize,hFileRead,NULL
Find out how large the file is. The file size is returned in eax. If the file is larger than 4 GB, the high DWORD of the file size is stored in FileSizeHighWord. Since we don't expect to handle such large file, we can ignore it.
invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
Write the data that is mapped into memory into the output file.
invoke UnmapViewOfFile,pMemory
When we're through with the input file, unmap it from memory.
call CloseMapFile invoke CloseHandle,hFileWrite
And close all the files.
invoke SetWindowText,hWnd,ADDR AppName
Restore the original caption text.
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
Enable the Open menu item and gray out the Save As menu item.
