4

I have an exe process that is running with a shortcut.
In the "Start in" property of the shortcut I set it to the folder where all app resources are. The process still looks for files at the location of the exe and not the location written in the shortcut.

I can also see it in Process Explorer - the "current directory" is the location of the exe.
Is there a way to change it?

(If I wasn't clear enough - I want to put my app in a central network location and not in each user folder - but I want it to run - above each user folder by putting a shortcut in each user folder.)

BTW : Why don't I solve it with code writing? Because of third party jars I have in my exe (I am using exe4j to make an exe)

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
Bick
  • 17,833
  • 52
  • 146
  • 251
  • If the app. is using the `user.dir` to locate resources, it is very broken. Fix that first, and the rest solves itself. – Andrew Thompson Nov 29 '11 at 13:14
  • Please explain . Lets say I have a log4j.xml or any other resource file in the directory of the exe. How can I read from it if the exe shortcut loads the exe above another folder? – Bick Nov 29 '11 at 13:18
  • *"Lets say I have a log4j.xml or any other resource file in the directory of the exe."* Let's say you put it in a reproducible path such as a sub-directory (e.g. based on the package name of the main class) of `user.home`. – Andrew Thompson Nov 29 '11 at 13:23
  • Yes, but I would prefer to write the path relative to my directory. right? Let's say it's a classroom and all the students have running folder on their computer. They all have personal data files in their folder the application needs to read (xml and configurations). but I would prefer to put the exe in a general folder (for version control, and easy upgrade) and put a shortcut in each student folder. but then the exe reads from the central directory. how can I change it? Thanks – Bick Nov 29 '11 at 13:31
  • 1
    How about using a config file or something to store user-specific configuration properties needed by the app for each user? Pass the file-name of the config file as a program argument to the app. Under Windows, you can create a batch file to pass the argument since current Windows shortcut no longer supports program argument entry unlike Win9x shortcut – ee. Nov 30 '11 at 02:24
  • @ee. Windows shortcuts can still contain program arguments. – Harry Johnston Nov 30 '11 at 02:39
  • @user450602: It should work as you were expecting. Perhaps this is an issue with exe4j? – Harry Johnston Nov 30 '11 at 02:40
  • @user450602: what happens if you run the executable from the command line? Does it inherit the current directory from the command line properly? – Harry Johnston Nov 30 '11 at 02:41
  • @HarryJohnston Oh my! Now, I just know why my program arguments don't work when I type them in Target entry...They should be put outside the filename quote otherwise it will trigger "problem with Shortcut" warning message.. :P – ee. Nov 30 '11 at 02:44
  • well, I cant use my configuration files because there are a lot of external jars. Each external jar looks first in the running directory. Maybe each external jar has also a config file - but there are a lot of jars. I would prefer to trick the OS into believing that this runs from the user directory. Right now I copy the file into the directory (network script) when the user logins. (awful solution) – Bick Nov 30 '11 at 08:04
  • @HarryJohnston - It sounds weird but I will check it. I think exe4j runs as any other native process. later only it loads a JVM and passes params and jars to it. The exe process should be as any other exe process so I dont think it matters to the OS. But computers seem to surprise me a lot lately. – Bick Nov 30 '11 at 08:10
  • @user450602: the point is that the executable might be deliberately changing the current directory to its own location when it starts up. – Harry Johnston Dec 01 '11 at 03:23
  • That sneaky exe... maybe i will put this question also in super user – Bick Dec 01 '11 at 15:07
  • @user450602, may I have some clarifications? 1. can you make code change at all? 2. is it possible the config is part of the exe, and you will release a new version if anything change in the config? – wyz Dec 07 '11 at 02:38
  • Since the current directory is always the directory of exe (is that really the case?), why not refer other artifacts relative to this directory? Typical installations follow a pattern for example /bin, /resources, /lib, /config under a common installation directory. – Jayan Dec 07 '11 at 11:56

4 Answers4

5

From exe4-j documentation.., it seems this can be configured in exe4j project.

Working directory
For some applications (especially GUI applications) you might want to change the working directory
to a specific directory relative to the executable, for example to read config files that are in a fixed
location. To do so, please select the Change working directory to: checkbox and enter a
directory relative to the executable in the adjacent text field. To change the current directory to the
same directory where the executable is located, please enter a single dot.
Jayan
  • 18,003
  • 15
  • 89
  • 143
  • 3
    Correct. More specifically, deselect the "Change working directory to" check box in the "Executable info" step of the exe4j wizard, then the working directory will not be changed by the launcher. – Ingo Kegel Dec 09 '11 at 08:56
  • E-x-c-e-l-l-e-n-t. I saw the documentation in the exe4j too . but I couldn't use it properly. removing the checkbox as suggested in the comment - made it for me. thanks. – Bick Jan 04 '12 at 09:38
1

One alternative is to use a System Property. Just create a shortcut like this:

java -Dmyproperty="\\myserver\myfolder" -jar yourjar.jar

And get this property on your program:

System.getProperty("myproperty");

You can also set multiple System Properties.

felipecrp
  • 999
  • 10
  • 16
0

I would start the java application via a cmd or bat file, then change to the work dir before you call javaw. If you don't do any thing special in your java application code all the paths in it will be relative to the place where you started java.

Jess

jessarah
  • 351
  • 1
  • 4
  • 13
0

You can hack the classpath programatically which would allow you to specify a specific folder or series of folders to access the data.

import java.io.IOException;
import java.io.File;
import java.net.URLClassLoader;
import java.net.URL;
import java.lang.reflect.Method;

public class ClassPathHacker {

private static final Class[] parameters = new Class[]{URL.class};

public static void addFile(String s) throws IOException {
   File f = new File(s);
   addFile(f);
}//end method

public static void addFile(File f) throws IOException {
   addURL(f.toURI().toURL());
}//end method


public static void addURL(URL u) throws IOException {

  URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
  Class sysclass = URLClassLoader.class;

  try {
     Method method = sysclass.getDeclaredMethod("addURL", parameters);
     method.setAccessible(true);
     method.invoke(sysloader, new Object[]{u});
  } catch (Throwable t) {
     t.printStackTrace();
     throw new IOException("Error, could not add URL to system classloader");
  }//end try catch

   }//end method

}//end class

with the property loader file of

import java.io.InputStream;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;
import java.util.ResourceBundle;


public abstract class PropertyLoader
{
    /**
     * Looks up a resource named 'name' in the classpath. The resource must map
     * to a file with .properties extention. The name is assumed to be absolute
     * and can use either "/" or "." for package segment separation with an
     * optional leading "/" and optional ".properties" suffix. Thus, the
     * following names refer to the same resource:
     * <pre>
     * some.pkg.Resource
     * some.pkg.Resource.properties
     * some/pkg/Resource
     * some/pkg/Resource.properties
     * /some/pkg/Resource
     * /some/pkg/Resource.properties
     * </pre>
     * 
     * @param name classpath resource name [may not be null]
     * @param loader classloader through which to load the resource [null
     * is equivalent to the application loader]
     * 
     * @return resource converted to java.util.Properties [may be null if the
     * resource was not found and THROW_ON_LOAD_FAILURE is false]
     * @throws IllegalArgumentException if the resource was not found and
     * THROW_ON_LOAD_FAILURE is true
     */
    public static Properties loadProperties (String name, ClassLoader loader)
    {
        if (name == null)
            throw new IllegalArgumentException ("null input: name");

        if (name.startsWith ("/"))
            name = name.substring (1);

        if (name.endsWith (SUFFIX))
            name = name.substring (0, name.length () - SUFFIX.length ());

        Properties result = null;

        InputStream in = null;
        try
        {
            if (loader == null) loader = ClassLoader.getSystemClassLoader ();

            if (LOAD_AS_RESOURCE_BUNDLE)
            {    
                name = name.replace ('/', '.');
                // Throws MissingResourceException on lookup failures:
                final ResourceBundle rb = ResourceBundle.getBundle (name,
                    Locale.getDefault (), loader);

                result = new Properties ();
                for (Enumeration keys = rb.getKeys (); keys.hasMoreElements ();)
                {
                    final String key = (String) keys.nextElement ();
                    final String value = rb.getString (key);

                    result.put (key, value);
                } 
            }
            else
            {
                name = name.replace ('.', '/');

                if (! name.endsWith (SUFFIX))
                    name = name.concat (SUFFIX);

                // Returns null on lookup failures:
                in = loader.getResourceAsStream(name);
                if (in != null)
                {
                    result = new Properties ();
                    result.load (in); // Can throw IOException
                }
            }
        }
        catch (Exception e)
        {
            result = null;
        }
        finally
        {
            if (in != null) try { in.close (); } catch (Throwable ignore) {}
        }

        if (THROW_ON_LOAD_FAILURE && (result == null))
        {
            throw new IllegalArgumentException ("could not load [" + name + "]"+
                " as " + (LOAD_AS_RESOURCE_BUNDLE
                ? "a resource bundle"
                : "a classloader resource"));
        }

        return result;
    }

    /**
     * A convenience overload of {@link #loadProperties(String, ClassLoader)}
     * that uses the current thread's context classloader.
     */
    public static Properties loadProperties (final String name)
    {
        return loadProperties (name,
            Thread.currentThread ().getContextClassLoader ());
    }

    private static final boolean THROW_ON_LOAD_FAILURE = true;
    private static final boolean LOAD_AS_RESOURCE_BUNDLE = false;
    private static final String SUFFIX = ".properties";
} // End of class

then you can add a path as follows

 try {
            //First Load up the properties and populate the config
            ClassPathHacker.addFile("/pathtomyapp");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        properties = PropertyLoader.loadProperties("myapp");

or you can also use getResourceBundle to get your resources, this is just one example of hacking the classpath to allow files to be available, you can always just add the classpath programatically and let the jar files you need to be available to reside there, so if you always ensure that the app network path is Q: you can add Q:\ to the classpath.

Theresa Forster
  • 1,914
  • 3
  • 19
  • 35