Does anyone have a nice way to get the path the user's "My Documents" directory from Intel Fortran?
More information: My application includes an installer project in Visual Studio 2010. Basically, I want to have the installer create a directory to put examples and scratch files in that the application will use. In the application, when the user wants to open a file, this is the directory I want to be the default that they are taken to. I can make the installer install the files using the "User's Personal Data" item in the file system tree. However, I can't seem to figure out how to get the application to know where this is located. I can get the location of the user's home directory with the "USERPROFILE" environment variable, but there doesn't seem to be an environment variable for the user's documents folder. I'd like a nice way to do this that works on Windows 7 and beyond (XP is a plus, but not required), that doesn't require me hard-coding what the directory is called (which may vary from system to system).
See the SHGetFolderPath API and its friends, and query the path location for CSIDL_PERSONAL.
CALL GET_ENVIRONMENT_VARIABLE ('USERPROFILE', DATA, LEN_DATA, IO_STATUS, .TRUE.)
For a full list of CSIDL values useable by SHGetFolderPath see
There you can see that CSIDL_MYDOCUMENTS is the same as CSIDL_PERSONAL
All of these CSIDL values are available through the XSystemPath function in the Xeffort library, if you want to avoid working with the API directly.
Thanks all! My solution is below. It seems to work (note that it requires linking with shell32.lib). Not sure if there is a better way to deal with the C string?
integer :: i
integer(handle) :: hresult
character(len=1),dimension(256) :: tmpstr
character(len=:),allocatable :: MyDocumentsDir
integer(HANDLE),parameter :: CSIDL_PERSONAL = 5 !from Shlobj.h
!DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'SHGetFolderPathA' :: SHGetFolderPathA
integer(HANDLE) :: SHGetFolderPathA
integer(HANDLE),value :: hwndOwner
integer(c_int),value :: nFolder
integer(HANDLE),value :: hToken
integer(DWORD),value :: dwFlags
character(kind=C_CHAR) :: pszPath(*)
end function SHGetFolderPathA
hresult = SHGetFolderPathA( hwndOwner= NULL, &
nFolder = CSIDL_PERSONAL,&
hToken = NULL,&
dwFlags = 0,&
pszPath = tmpstr)
if (hresult==0) then
!convert array to character string:
MyDocumentsDir = ''
if (tmpstr(i)/=C_NULL_CHAR) then
MyDocumentsDir = MyDocumentsDir//tmpstr(i)
write(*,*) 'MyDocumentsDir: '//MyDocumentsDir
end program test
Given that it is the ANSI version you have interfaced to why not make the interface more Fortran friendly by having
character(len=*) :: pszPath
And then you can get MyDocumentsDir direct from the call without the string array copy? If you wanted the position of the first C_NULL_CHAR you could use INDEX. Also add !DEC$OBJCOMMENT LIB:"SHELL32.LIB" perhaps.
@Intel is there any plans to add any new APIs to the IFWIN interfaces? There are many missing ones. I note the shell32 interfaces have not been added since 2000. It is after all Visual Fortran for WINDOWS!
app4619, yes, we do plan to add APIs. It's a time-consuming process, though. I'll take a look at shell32.
You might note that SHGetFolderPath is supperceded SHGetKnownFolderPath which had more options. Also:
You have the function return value as type handle, HRESULT would be better with ifwinty type LONG (4 bytes) your code should fail on x64 as it will be 8 bytes.
I also note the use of VALUE attribute. This is default for stdcall except for arrays and type character in my understanding.
@steve thanks. Some updates would be appreciated. I know it is time consuming, it consumes some of my time!
Thanks for the suggestions! I did look at SHGetKnownFolderPath, but wasn't sure about the Fortran bindings for the REFKNOWNFOLDERID argument. I might revisit it later.
Steve, WinINet.lib would also be a good one to have.