Get path to "My Documents" folder

Get path to "My Documents" folder

Jacob Williams's picture

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

12 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.
Jacob Williams's picture

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.

nvaneck's picture

CALL GET_ENVIRONMENT_VARIABLE ('USERPROFILE', DATA, LEN_DATA, IO_STATUS, .TRUE.)

Anthony Richards's picture

For a full list of CSIDL values useable by SHGetFolderPath see

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

There you can see that CSIDL_MYDOCUMENTS is the same as CSIDL_PERSONAL

David White's picture

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.

 

Jacob Williams's picture

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

 

app4619's picture

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!

 

Steve Lionel (Intel)'s picture

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

Steve
app4619's picture

@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!

 

Jacob Williams's picture

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.

Jacob Williams's picture

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

Login to leave a comment.