26

I'm trying to dynamically run a .jar from a C# assembly (using Process.Start(info)). Now, from a console application I am able to just run:

ProcessStartInfo info = new ProcessStartInfo("java", "-jar somerandom.jar");

In an assembly, however, I keep getting a Win32Exception of "The system cannot find the file specified" and have to change the line to the full path of Java like so:

ProcessStartInfo info = new ProcessStartInfo("C:\\Program Files\\Java\\jre6\\bin\\java.exe", "-jar somerandom.jar");

This obviously won't do. I need a way to dynamically (but declaratively) determine the installed location of Java.

I started thinking of looking to the registry, but when I got there I noticed that there were specific keys for the versions and that they could not even be guaranteed to be numeric (e.g. "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.6" and "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\1.6.0_20").

What would be the most reliable "long-haul" solution to finding the most up-to-date java.exe path from a C# application?

Thanks much in advance.

- EDIT -

Thanks to a combination of GenericTypeTea's and Stephen Cleary's answers, I have solved the issue with the following:

private String GetJavaInstallationPath()
{
    String javaKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
    using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(javaKey))
    {
        String currentVersion = baseKey.GetValue("CurrentVersion").ToString();
        using (var homeKey = baseKey.OpenSubKey(currentVersion))
            return homeKey.GetValue("JavaHome").ToString();
    }
}
Community
  • 1
  • 1
Lance
  • 5,655
  • 4
  • 30
  • 32

5 Answers5

29

You can do it through the registry. You were looking in the wrong place though. I knocked together a quick example for you:

private string GetJavaInstallationPath()
{
    string environmentPath = Environment.GetEnvironmentVariable("JAVA_HOME");
    if (!string.IsNullOrEmpty(environmentPath))
    {
       return environmentPath;
    }

    string javaKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment\\";
    using (Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(javaKey))
    {
        string currentVersion = rk.GetValue("CurrentVersion").ToString();
        using (Microsoft.Win32.RegistryKey key = rk.OpenSubKey(currentVersion))
        {
            return key.GetValue("JavaHome").ToString();
        }
    }
}

Then to use it, just do the following:

string installPath = GetJavaInstallationPath();
string filePath = System.IO.Path.Combine(installPath, "bin\\Java.exe");
if (System.IO.File.Exists(filePath))
{
    // We have a winner
}
djdd87
  • 67,346
  • 27
  • 156
  • 195
  • 1
    This looks perfect! Thanks for pointing out that I missed the `CurrentVersion`. I keep getting NullRef's on `rk`, though. ;( – Lance Jun 14 '10 at 15:44
  • 2
    It is a common convention that the user can overwrite the "autodetected" java path with an environment variable called "JAVA_HOME". A programmer should respect hat and give that variable precedence: string java_path = Environment.GetEnvironmentVariable("JAVA_HOME") ?? GetJavaInstallationPath(); – Jürgen Steinblock Jun 14 '10 at 15:47
  • @Lance - I'm not a java expert, so this is only a best-guess. Have you had a look to see if the key is there? @SchlaWiener - Updated my answer to reflect your comment (I think). – djdd87 Jun 14 '10 at 15:51
  • @GenericTypeTea: Yes, it is there. The keys listed in my question exist (got them through RegEdit). @SchlaWiener: Thanks a ton for that. That's something I won't have to get kicked for later. ;) – Lance Jun 14 '10 at 15:57
  • 1
    @Lance - I don't know why it's returning null if the key's there then. It's going to be down to you to debug it I'm afraid. Please report back if you find out why. – djdd87 Jun 14 '10 at 16:02
  • Even though I'm looking right at the key, I can get no further than "HKEY_LOCAL_MACHINE\SOFTWARE". At first I thought it was a path vs. key problem (since I could get into the software key just fine, but not the path copied straight from RegEdit), but even explicitly calling an `OpenSubKey()` for each step, I can't get any deeper than SOFTWARE. – Lance Jun 14 '10 at 16:36
  • That's strange. It just comes back as null? If there was a problem I would of thought it'd be a security issue, but that would through a security exception. – djdd87 Jun 14 '10 at 17:30
  • Found it! Not that it solves anything, but I found that the issue is because I'm x64 and `LocalMachine` is routing me to a subkey under SOFTWARE w/o me asking to (i.e. HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node). – Lance Jun 14 '10 at 17:50
  • Why'd you accept? Thought you just said it didn't solve anything? No point accepting if you've not had your question answered. – djdd87 Jun 14 '10 at 18:55
  • Because your code was sound, and I'm sure will work perfectly over this next hurdle (which I believe has broken scope), which I am currently writing up into another question as to not drowned this one with scope-creep. I will certainly post an edit once the other is resolved so others can benefit. – Lance Jun 14 '10 at 19:23
4

Just a quick bump because i found a better solution than the owner picked answer.

I found that it works with only 32bit Java and today time, thats pretty outdated. Therefor I made a adjustment for 64bit systems. Hope this helps anyone else looking for a way to pull the paths.

        private string GetJavaInstallationPath()
        {
            string environmentPath = Environment.GetEnvironmentVariable("JAVA_HOME");
            if (!string.IsNullOrEmpty(environmentPath))
            {
                return environmentPath;
            }
            string javaKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment\\";
            if (!Environment.Is64BitOperatingSystem)
            {
                using (Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(javaKey))
                {
                    string currentVersion = rk.GetValue("CurrentVersion").ToString();
                    using (Microsoft.Win32.RegistryKey key = rk.OpenSubKey(currentVersion))
                    {
                        return key.GetValue("JavaHome").ToString();
                    }
                }
            }
            else
            {
                using (var view64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,
                                                            RegistryView.Registry64))
                {
                    using (var clsid64 = view64.OpenSubKey(javaKey))
                    {
                        string currentVersion = clsid64.GetValue("CurrentVersion").ToString();
                        using (RegistryKey key = clsid64.OpenSubKey(currentVersion))
                        {
                            return key.GetValue("JavaHome").ToString();
                        }
                    }
                }
            }

        }
Jamie
  • 101
  • 1
  • 10
1

As far as I know the idea is that the latest version of Java installed on the system is the first one found in the PATH environment variable, so you shouldn't need to look for any registry keys, just run the thing.

Try:

ProcessStartInfo info = new ProcessStartInfo("java.exe", "-jar somerandom.jar");

If it doesn't work make sure java.exe is in your path and let me know.

Grzenio
  • 35,875
  • 47
  • 158
  • 240
  • Unfortunately, no. If I check the `info.EnvironmentVariables["path"]`, it does not contain Java. On the other hand, if I just open a command prompt, I can just `> java -jar somerandom.jar` all day, so I know it's supposed to be there. – Lance Jun 14 '10 at 15:19
  • Could you check which java.exe process runs when you run it from command prompt? – Grzenio Jun 14 '10 at 15:21
  • @LanceMay, and also have you actually tried the version with extension after the name of the process? – Grzenio Jun 14 '10 at 15:28
  • Yes, and they both work and don't work respectively in both situations. There seems to be no difference between the two. – Lance Jun 14 '10 at 15:34
1

Building on top of @GenericTypeTea question - this is a way how to check both on x32/x64.

static string GetJavaInstallationPath()
{
  string environmentPath = Environment.GetEnvironmentVariable("JAVA_HOME");
  if (!string.IsNullOrEmpty(environmentPath))
  {
    return environmentPath;
  }

  const string JAVA_KEY = "SOFTWARE\\JavaSoft\\Java Runtime Environment\\";

  var localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry32);
  using (var rk = localKey.OpenSubKey(JAVA_KEY))
  {
    if (rk != null)
    {
      string currentVersion = rk.GetValue("CurrentVersion").ToString();
      using (var key = rk.OpenSubKey(currentVersion))
      {
        return key.GetValue("JavaHome").ToString();
      }
    }
  }

  localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64);
  using (var rk = localKey.OpenSubKey(JAVA_KEY))
  {
    if (rk != null)
    {
      string currentVersion = rk.GetValue("CurrentVersion").ToString();
      using (var key = rk.OpenSubKey(currentVersion))
      {
        return key.GetValue("JavaHome").ToString();
      }
    }
  }

  return null;
}
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • https://msdn.microsoft.com/en-us/library/dd411615(v=vs.110).aspx On the 64-bit versions of Windows, portions of the registry are stored separately for 32-bit and 64-bit applications. There is a 32-bit view for 32-bit applications and a 64-bit view for 64-bit applications. If view is Registry64 but the remote machine is running a 32-bit operating system, the returned key will use the Registry32 view. – SimplyInk Apr 28 '16 at 03:38
  • Therefore just the call to open the key from the Registry64 view is enough to cover both 32/64-bit OS cases. – SimplyInk Apr 28 '16 at 03:39
0

First of all, Do not try to get java JDK from the environment variable use the Registry key which is more effective and will give you anything needed. Here is an example to check if it exists and if it does exist it can return the relative path or the name with the current version. I spent hours to make this work I hope it helps someone. In case it helps 32 bits JDK is being discontinued so I only provided with 64bits solution.

public static string checkInstalled(string findByName)
    {
        string displayName;
        string InstallPath;
        string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";

        //64 bits computer
        RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
        RegistryKey key = key64.OpenSubKey(registryKey);

        if (key != null)
        {
            foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
            {
                displayName = subkey.GetValue("DisplayName") as string;
                if (displayName != null && displayName.Contains(findByName))
                {

                    InstallPath = subkey.GetValue("InstallLocation").ToString();

                    return InstallPath; //or displayName

                }
            }
            key.Close();
        }

        return null;
    }

you can call this method like this

string JavaPath = Software.checkInstalled("Java(TM) SE Development Kit");

and boom. Cheers

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17