Safe Directories for File Writing

- Alyce Alyce
Safe Directories for File Writing | UAC and Files | Safe Directories | Special Variables | Trailing Backslash in DefaultDir$ | My Documents | Trailing Backslash | Temporary Files and Folders

For an eBook or printed book on using the API with Liberty BASIC, see:
APIs for Liberty BASIC


UAC and Files

User Account Control in versions of Windows starting with Windows Vista prohibits programs from writing files to many disk locations unless the program is run "as administrator". The reason for this restriction is as follows, quoted from the Wikidpedia article:

Safe Directories

Do not attempt to write files in the folder from which your program is running. The "Program Files" folder is not available for normal file writing operations and attempts to write files there will generate errors.

Instead, write files to the user's "My Documents" folder, temporary folder, or application data folder.

Special Variables

Applications write data to a user's data folder. Application data includes information needed by the program, such as data files and initialization files.

The following quote from the Liberty BASIC helpfile explains how to use the special variables DefaultDir$ (user's data) and StartupDir$ (installation folder)








Trailing Backslash in DefaultDir$

The special variable StartupDir$ points to the folder from which a Liberty BASIC program is running. Do not use it to write files. StartupDir$ includes a trailing backslash.

To access the user's data folder, write files to DefaultDir$. The special variable DefaultDir$ does not include a trailing backslash. In your code, check for a trailing backslash and add it if it doesn't exist, as in the following example.

print DefaultDir$   'no trailing backslash
print StartupDir$   'has trailing backslash
 
if right$(DefaultDir$,1)<>"\" then
    DefaultDir$=DefaultDir$+"\"
end if
 
print DefaultDir$
 
'write a file:
open DefaultDir$+"mytestfile.txt" for output as #f
print #f, "Test"
close #f

My Documents

Windows has long included a folder called "My Documents" which is meant to be the location where users create and save files. You can retrieve this location with the API from shell32.dll called SHGetSpecialFolderLocation, using a value of 5 for the desired folder. This is the value assigned to the "My Documents" folder. An additional API is required to retrieve the folder name. It is SHGetPathFromIDListA. Both API calls are wrapped in a function:

Function GetSpecialfolder$(CSIDL)
'CSIDL.PERSONAL = 5 : My Documents = GetSpecialfolder$(CSIDL.PERSONAL)
    struct IDL,cb As Long, abID As short
    calldll #shell32, "SHGetSpecialFolderLocation",_
        0 as long, CSIDL as long, IDL as struct, ret as long
    if ret=0 then
        Path$ = Space$(512)
        id=IDL.cb.struct
        calldll #shell32, "SHGetPathFromIDListA",id as long, Path$ as ptr, ret as long
        GetSpecialfolder$ = Left$(Path$, InStr(Path$, Chr$(0)) - 1)
    else
        GetSpecialfolder$ = "Error"
    end if
    End Function

Trailing Backslash

The folder name for "My Documents" does not include a trailing backslash. To assure that the backslash is included in the folder name, check for it and append it if it is not there. Here is a demo that retrieves the location of "My Documents", adds a trailing backslash, and writes a test file there.

CSIDL.PERSONAL = 5    'My Documents Folder
 
myDocuments$ = GetSpecialfolder$(CSIDL.PERSONAL)
 
print "My Documents folder location is: "
print myDocuments$
 
if right$(myDocuments$,1)<>"\" then
    myDocuments$=myDocuments$+"\"
end if
 
open myDocuments$ + "testxxxx.txt" for output as #f
print #f, "Test"
close #f
 
end
 
Function GetSpecialfolder$(CSIDL)
    struct IDL,cb As Long, abID As short
    calldll #shell32, "SHGetSpecialFolderLocation",_
        0 as long, CSIDL as long, IDL as struct, ret as long
    if ret=0 then
        Path$ = Space$(512)
        id=IDL.cb.struct
        calldll #shell32, "SHGetPathFromIDListA",id as long, Path$ as ptr, ret as long
        GetSpecialfolder$ = Left$(Path$, InStr(Path$, Chr$(0)) - 1)
    else
        GetSpecialfolder$ = "Error"
    end if
    End Function

Temporary Files and Folders

To assure that your program writes temporary files to the Windows folder reserved for temporary files, use GetTempPathA and GetTempFileNameA. These functions are explained here:

GetTempPathA
GetTempFileNameA

yourPrefix$ = "xxx"   'prefix you choose
TempFileName$ = GetTempFileName$(yourPrefix$)
print "Temporary File Name is ";TempFileName$
end
 
function GetTempFileName$(prefix$)
    TempPath$=GetTempPath$()
    TempFile$ = space$(256)+chr$(0)
 
    calldll #kernel32, "GetTempFileNameA",_
    TempPath$ as ptr,_  'directory for temp file
    prefix$ as ptr,_    'desired prefix for temp filename
    0 as ulong,_        '0=file created,nonzero=you must create file
    TempFile$ as ptr,_  'string buffer to hold qualified path and filename
    result as ulong     'nonzero=success
    end function
 
Function GetTempPath$()
    CallDLL #kernel32, "GetTempPathA",_
    0 as long,_
    _NULL as long,_
    length as long
 
    buf$ = space$(length)
 
    CallDLL #kernel32, "GetTempPathA",_
    length as long,_
    buf$ as ptr,_
    ret as long
 
    GetTempPath$ = buf$
End Function

Safe Directories for File Writing | UAC and Files | Safe Directories | Special Variables | Trailing Backslash in DefaultDir$ | My Documents | Trailing Backslash | Temporary Files and Folders