Friday, April 8, 2011

What is the best way to find the users home directory in Java?

The difficulty is that it should be cross platform. Windows 2000, XP, Vista, OSX, Linux, other unix variants. I am looking for a snippet of code that can accomplish this for all platforms, and a way to detect the platform.

Now, you should be aware of bug 4787931 that user.home does not work correctly, so please do not provide me of texbook answers, I can find these myself in the manuals.

From stackoverflow
  • System.getProperty(String name) should do it with the right property:

    System.getProperty("user.home");
    

    Other useful properties can be found here

    Bruno Ranschaert : Same answer as above, see comments above. This seems to be an exercise in replication.
  • System.getProperty("user.home");
    

    See the JavaDoc.

    Bruno Ranschaert : Nope, not a correct answer, this is the same one as above. Yes, I did not only read the JavaDocs, but I also tried it out on all platforms before asking this question! The answer is not so simple.
  • Others have answered the question before me but a useful program to print out all available properties is:

    for (Map.Entry<?,?> e : System.getProperties().entrySet()) {
        System.out.println(String.format("%s = %s", e.getKey(), e.getValue())); 
    }
    
    Joachim Sauer : I wouldn't depend on this, because not all properties are standardized. Instead check the JavaDoc for System.getProperties() to find out which properties are guaranteed to exist.
    oxbow_lakes : That may be true but it's still pretty useful for a newbie I would think! I'm not sure it deserves 2 downvotes :-(
  • Since you specifically mention bug 4787391 I assume you are not satisfied with the System.getProperty("user.home") functionality. Are you sure? the user.home approach seems to work in a very large number of cases. If you have read the bug page you will find that a 100% bulletproof solution on Windows is hard, because Windows has a shifting concept of what the home directory means.

    If user.home isn't good enough for you I would suggest choosing a definition of 'home directory' for windows and using it, getting the appropriate environment variable with System.getenv(String).

    Bruno Ranschaert : Finally, this is the best solution after all.
  • The concept of a HOME directory seems to be a bit vague when it comes to Windows. If the environment variables (HOMEDRIVE/HOMEPATH/USERPROFILE) aren't enough, you may have to resort to using native functions via JNI or JNA. SHGetFolderPath allows you to retrieve special folders, like My Documents (CSIDL_PERSONAL) or Local Settings\Application Data (CSIDL_LOCAL_APPDATA).

    Sample JNA code:

    public class PrintAppDataDir {
    
        public static void main(String[] args) {
         if (com.sun.jna.Platform.isWindows()) {
          HWND hwndOwner = null;
          int nFolder = Shell32.CSIDL_LOCAL_APPDATA;
          HANDLE hToken = null;
          int dwFlags = Shell32.SHGFP_TYPE_CURRENT;
          char[] pszPath = new char[Shell32.MAX_PATH];
          int hResult = Shell32.INSTANCE.SHGetFolderPath(hwndOwner, nFolder,
            hToken, dwFlags, pszPath);
          if (Shell32.S_OK == hResult) {
           String path = new String(pszPath);
           int len = path.indexOf('\0');
           path = path.substring(0, len);
           System.out.println(path);
          } else {
           System.err.println("Error: " + hResult);
          }
         }
        }
    
        private static Map<String, Object> OPTIONS = new HashMap<String, Object>();
        static {
         OPTIONS.put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
         OPTIONS.put(Library.OPTION_FUNCTION_MAPPER,
           W32APIFunctionMapper.UNICODE);
        }
    
        static class HANDLE extends PointerType implements NativeMapped {
        }
    
        static class HWND extends HANDLE {
        }
    
        static interface Shell32 extends Library {
    
         public static final int MAX_PATH = 260;
         public static final int CSIDL_LOCAL_APPDATA = 0x001c;
         public static final int SHGFP_TYPE_CURRENT = 0;
         public static final int SHGFP_TYPE_DEFAULT = 1;
         public static final int S_OK = 0;
    
         static Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32",
           Shell32.class, OPTIONS);
    
         /**
          * see http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx
          * 
          * HRESULT SHGetFolderPath( HWND hwndOwner, int nFolder, HANDLE hToken,
          * DWORD dwFlags, LPTSTR pszPath);
          */
         public int SHGetFolderPath(HWND hwndOwner, int nFolder, HANDLE hToken,
           int dwFlags, char[] pszPath);
    
        }
    
    }
    
    Matt Solnit : FYI, the folder that corresponds to the user's home directory is CSIDL_PROFILE. See http://msdn.microsoft.com/en-us/library/bb762494(VS.85).aspx.
    Bruno Ranschaert : Yes, this is an elaborate version for the Windows case.
  • I would use the algorithm detailed in the bug report using System.getenv(String), and fallback to using the user.dir property if none of the environment variables indicated a valid existing directory. This should work cross-platform.

    I think, under Windows, what you are really after is the user's notional "documents" directory.

0 comments:

Post a Comment