App Part 2: Using files and the common dialogs
The Common File Dialogs
The first step to opening or saving files is finding out the filename to use... of course you could always hard code the name of the file into your program, but honestly that doesn't make for very useful programs most of the time.
Since this is such a common task, there are predefined system dialogs that you can use to
allow the user to select a file name. The most common open and save file dialogs are accessed
VAR ofn : OPENFILENAME; FileName : ARRAY [0..MAX_PATH] OF CHAR; hEdit : HWND; BEGIN FileName := ""; FillMemBYTE(ofn, SIZE(ofn), 0); ofn.lStructSize := SIZE(OPENFILENAME); ofn.hwndOwner := hwnd; ofn.lpstrFilter := ADR("Text Files (*.txt)"+CHR(0)+"*.txt"+CHR(0)+ "All Files (*.*)"+CHR(0)+"*.*"+CHR(0)+CHR(0)); ofn.lpstrFile := ADR(FileName); ofn.nMaxFile := MAX_PATH; ofn.Flags := OFN_EXPLORER BOR OFN_FILEMUSTEXIST BOR OFN_HIDEREADONLY; ofn.lpstrDefExt := ADR("txt"); IF GetOpenFileName(ofn) THEN (* Do something usefull with the filename stored in FileName *)
Note that we call the procedure
You can easily find out the meanings of the various members by looking them up in your documentation. The
The flags indicate that the dialog should only allow the user to enter filenames that already exist (since we want to open
them, not create them) and to hide the option to open the file in readonly mode, which we aren't going to support. Finally
we provide a default extention, so if the user types in
To select a file for saving instead of opening, the code is nearly the same, except for calling
ofn.Flags := OFN_EXPLORER BOR OFN_FILEMUSTEXIST BOR OFN_HIDEREADONLY;;
In this case we no longer want to require the file exist, but we do want the directory to exist since we aren't going to try and create it first. We'll also prompt the user if they select an existing file to make sure they want to overwrite it.
NOTE: MSDN states the following for the
Basically what this means is that as of Windows 2000 they added some members to this struct, and so it's
size changed. If the code above doesn't work for you it's possibly because the size that your compiler used
and the size that your operating system (ie. Windows 98, Windows NT4) expected were different and so the
call failed. If this happens, try using
Reading and Writing Files
In windows you have a few options as to how you want to access files. You
can use the procedures from the ISO modules
To open files, you can use
Say for example you have allowed the user to select a file using GetOpenFileName()...
PROCEDURE LoadTextFileToEdit(hEdit : HWND; FileName : ARRAY OF CHAR) : BOOL; VAR hFile : HANDLE; bSuccess : BOOL; dwFileSize : CARDINAL; dwRead : CARDINAL; pszFileText : POINTER TO ARRAY OF CHAR; BEGIN bSuccess:=FALSE; hFile := CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, NIL_SECURITY_ATTRIBUTES,OPEN_EXISTING, 0, NIL); IF hFile # INVALID_HANDLE_VALUE THEN dwFileSize := GetFileSize(hFile, NIL_DWORD); IF dwFileSize # 0FFFFFFFFH THEN NEW(pszFileText,dwFileSize + 1); IF pszFileText # NIL THEN IF ReadFile(hFile, CAST(LPVOID,pszFileText), dwFileSize, dwRead, NIL) THEN pszFileText^[dwFileSize] := 00C; (* Add null terminator *) IF SetWindowText(hEdit, pszFileText^) THEN bSuccess := TRUE; (* It worked! *) DISPOSE(pszFileText); END; END; END; END; CloseHandle(hFile); END; RETURN bSuccess; END LoadTextFileToEdit;
There is a complete function to read a text file into an edit control. It takes as parameters the handle to the edit control and the name of the file to read. This particular function has a fair bit of error checking, file IO is one place where a lot of things can go wrong, and so you need to be on the lookout for errors.
Note the variable
In the call to
Once we've opened the file and chacked to see that
Once all that has succeeded we set out success variable to
PROCEDURE SaveTextFileFromEdit(hEdit : HWND; FileName : ARRAY OF CHAR) : BOOL; VAR hFile : HANDLE; bSuccess : BOOL; dwTextLength : CARDINAL; dwBufferSize : CARDINAL; dwRead : CARDINAL; dwWritten : CARDINAL; pszText : POINTER TO ARRAY OF CHAR; BEGIN bSuccess:=FALSE; hFile := CreateFile(FileName, GENERIC_WRITE, 0, NIL_SECURITY_ATTRIBUTES, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NIL); IF hFile # INVALID_HANDLE_VALUE THEN dwTextLength := GetWindowTextLength(hEdit); (* No need to bother if there's no text. *) IF dwTextLength > 0 THEN dwBufferSize := dwTextLength + 1; NEW(pszText,dwBufferSize); IF pszText # NIL THEN IF GetWindowText(hEdit, pszText^, dwBufferSize)>0 THEN bSuccess := WriteFile(hFile, CAST(LPVOID,pszText), dwTextLength, dwWritten, NIL) END; DISPOSE(pszText); END; END; CloseHandle(hFile); END; RETURN bSuccess; END SaveTextFileFromEdit;
Very similar to reading files, the function to write files has a few changes. First of all
when we call
Next we get the length of the memory buffer needed from the edit control, since this is the source
of the data. Once we've allocated the memory, we request the string from the edit control
Copyright © 1998-2011, Brook Miles. All rights reserved. Adapted for Modula-2 by Frank Schoonjans, with permission.