Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Get path to "My Documents" folder

Jacob_Williams
New Contributor II
1,882 Views

Does anyone have a nice way to get the path the user's "My Documents" directory from Intel Fortran?

0 Kudos
11 Replies
Jacob_Williams
New Contributor II
1,882 Views

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).

0 Kudos
IanH
Honored Contributor III
1,882 Views

See the SHGetFolderPath API and its friends, and query the path location for CSIDL_PERSONAL.

0 Kudos
nvaneck
New Contributor I
1,882 Views
CALL GET_ENVIRONMENT_VARIABLE ('USERPROFILE', DATA, LEN_DATA, IO_STATUS, .TRUE.)
0 Kudos
Anthony_Richards
New Contributor I
1,882 Views

For a full list of CSIDL values useable by SHGetFolderPath see

http://msdn.microsoft.com/en-gb/library/windows/desktop/bb762494%28v=vs.85%29.aspx

There you can see that CSIDL_MYDOCUMENTS is the same as CSIDL_PERSONAL

0 Kudos
DavidWhite
Valued Contributor II
1,882 Views

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.

 

0 Kudos
Jacob_Williams
New Contributor II
1,882 Views

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?

    program test
    
    use iso_c_binding
    use kernel32
    use ifwinty    

    implicit none
        
    integer :: i
    integer(handle) :: hresult
    character(len=1),dimension(256) :: tmpstr
    character(len=:),allocatable :: MyDocumentsDir
    
    integer(HANDLE),parameter :: CSIDL_PERSONAL = 5    !from Shlobj.h
    
    interface
        function SHGetFolderPathA(hwndOwner,nFolder,hToken,dwFlags,pszPath)
        !DEC$ ATTRIBUTES DEFAULT, STDCALL, DECORATE, ALIAS:'SHGetFolderPathA' :: SHGetFolderPathA
            import 
            implicit none
            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
    end interface
    
    hresult = SHGetFolderPathA( hwndOwner= NULL, &
                                nFolder  = CSIDL_PERSONAL,&
                                hToken   = NULL,&
                                dwFlags  = 0,&
                                pszPath  = tmpstr)
    
    write(*,*) 'hresult=',hresult

    if (hresult==0) then
        !convert array to character string:
        MyDocumentsDir = ''
        do i=1,size(tmpstr)
            if (tmpstr(i)/=C_NULL_CHAR) then
                MyDocumentsDir = MyDocumentsDir//tmpstr(i)
            else
                exit
            end if
        end do
        write(*,*) 'MyDocumentsDir: '//MyDocumentsDir
    else
        write(*,*) 'Error'
    end if
    
    end program test

 

0 Kudos
andrew_4619
Honored Contributor III
1,882 Views

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!

 

0 Kudos
Steven_L_Intel1
Employee
1,882 Views

app4619, yes, we do plan to add APIs. It's a time-consuming process, though. I'll take a look at shell32.

0 Kudos
andrew_4619
Honored Contributor III
1,882 Views

@Jacob 

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!

 

0 Kudos
Jacob_Williams
New Contributor II
1,882 Views

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.

0 Kudos
Jacob_Williams
New Contributor II
1,882 Views

Steve, WinINet.lib would also be a good one to have.

0 Kudos
Reply