Is there a way to check if a specific program is installed on Windows using Java?
I'm trying to develop a Java program that automatically creates zip archives by using the code line command from 7-Zip.
So, I would like to check in Java if on my windows OS '7-Zip' is already installed. No check for running apps or if OS is Windows or Linux. I want to get a bool (true/false) if '7-Zip' is installed on Windows.
The library Apache Commons has a class called SystemUtils - full documentation is available at https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/SystemUtils.html.
In this library you have the following static boolean properties at your disposal:
SystemUtils.IS_OS_LINUX
SystemUtils.IS_OS_WINDOWS
The unix-like solution would be to simply try to run the program with --version flag (on windows probably the /? or - like in the 7zip case - without any at all) and check whether it fails, or what the return code will be.
Something like:
public boolean is7zipInstalled() {
try {
Process process = Runtime.getRuntime().exec("7zip.exe");
int code = process.waitFor();
return code == 0;
} catch (Exception e) {
return false;
}
}
I assume that you're talking about Windows. As Java is intended to be a platform-independent language and the way how to determine it differs per platform, there's no standard Java API to check that. You can however do it with help of JNI calls on a DLL which crawls the Windows registry. You can then just check if the registry key associated with the software is present in the registry. There's a 3rd party Java API with which you can crawl the Windows registry: jRegistryKey.
Here's an SSCCE with help of jRegistryKey:
package com.stackoverflow.q2439984;
import java.io.File;
import java.util.Iterator;
import ca.beq.util.win32.registry.RegistryKey;
import ca.beq.util.win32.registry.RootKey;
public class Test {
public static void main(String... args) throws Exception {
RegistryKey.initialize(Test.class.getResource("jRegistryKey.dll").getFile());
RegistryKey key = new RegistryKey(RootKey.HKLM, "Software\\Mozilla");
for (Iterator<RegistryKey> subkeys = key.subkeys(); subkeys.hasNext();) {
RegistryKey subkey = subkeys.next();
System.out.println(subkey.getName()); // You need to check here if there's anything which matches "Mozilla FireFox".
}
}
}
If you however intend to have a platformindependent application, then you'll also have to take into account the Linux/UNIX/Mac/Solaris/etc. (in other words: anywhere where Java is able to run) ways to detect whether FF is installed. Else you'll have to distribute it as a Windows-only application and do a System#exit() along with a warning whenever System.getProperty("os.name") is not Windows.
Sorry, I don't know how to detect in other platforms whether FF is installed or not, so don't expect an answer from me for that ;)
Related
Is it just my setup or is anyone else having this problem?
Using AdoptOpenJDK 1.8.0_275 installed at:
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre
API docs of System.getProperties() do not specify any details.
Can confirm this is still happening on adoptopenjdk14, as well as openjdk early access build for j16.
You can file a bug if you want, but I bet it'll be denied. At this point, the name Mac OS X is not so much 'the name of the OS' as a 'globally agreed upon keyword identifying that unix-based mac operating system', where I mean globally literally (as in, 'around the planet', not 'across your source base/VM'). Changing it would just break stuff needlessly. The same applies, to a lesser degree, to version 10.16: The thing before the dot is not so much 'this is how the OS identifies itself' and more a 'globally agreed upon versioning scheme for Mac OS, identifying a wide and ill defined set of capabilities'.
There is no meaningful difference between the transition between big sur and catalina, other than the fact that apple made a marketing decision. If you want to point at an OS transition that might warrant the entirely nebulous choice to consider it a 'major change', surely it was the one to catalina, as that made by far the largest changes (including removing support for 32-bit entirely) in the last bunch of releases.
This leaves you with the challenge of: Okay, great, I can use System.getProperty("os.name") to get the globally agreed upon keyword that means unix-like Mac OS, and os.version for a string I can break into bits to figure out some nebulous batch of capabilities, but what if I need the actual name of the OS to e.g. show to a user?
Then you have three major options:
The easy one is to just write mapping code. Acknowledge that os.name and os.version give you (rather arguably) useful intent and not so much official names, and therefore, write some mappings. These would map name/version pairs to rendering strings, falling back to just printing the name string and the version string, concatenated, verbatim. You could add a mapping: Mac OS X/10.16 → Mac OS Big Sur in this table.
The hard way: Figure out you're on a mac (which is now easier; os.name returns Mac OS X, or just check for the existence: Files.isExecutable(Paths.get("/usr/bin/sw_vers"))), and then use ProcessBuilder to execute /usr/bin/sw_vers, picking up all output into a big string, and then parse it. Its output looks like:
ProductName: macOS
ProductVersion: 11.1
BuildVersion: 20C69
which, crucially, doesn't even include the words Big Sur, but does give you 11.1. I don't know how to run a command line tool that actually gives you Big Sur. Maybe system_profiler, but note that this takes many minutes to run, I really doubt you want to run that.
NB: you can also run .command("/usr/bin/sw_vers", "-productVersion") which gives you just 11.1, this may be a lot simpler to parse. -productName also works, gives you just macOS.
If you need this information to scan for OS capabilities, then stop doing this. It doesn't work with browsers, and it's not a good plan for OS releases either. What capability are you scanning for? Imagine, for example, if it is 'Can I run /usr/bin/sw_vers to figure stuff out', as a hypothetical example. The right strategy is NOT to check os.name/os.version, conclude that the command must exist, and then run it, failing catastrophically if it is not there. The right move is to check if /usr/bin/sw_vers exists, and then execute it, falling back to some non-mac based solution (perhaps /usr/bin/uname) in other cases. Scan for the capability, don't scan for the OS/version.
Java code to call native tool sw_vers
Regarding Option # 2 in the Answer by rzwitserloot, here is a complete code example to run from Java a command-line tool sw_vers that describes the version of macOS software running on the host computer.
If on the command-line (console) such as in Terminal.app, you run:
sw_vers
…in Big Sur on an Intel Mac we get:
ProductName: macOS
ProductVersion: 11.2
BuildVersion: 20D64
We only need the middle piece. So running:
sw_vers -productVersion
…shows simply 11.2, the value we need for your purpose.
Here is complete example app with a method to return this string into Java.
ProcessBuilder class creates operating system processes. Each new process is represented by the Process class.
We use try-with-resources syntax to automatically close the InputStream and Scanner objects.
Once you have the 11.2 string in hand, split on the FULL STOP, pull the first number 11, and you know you are running on Big Sur.
package org.example;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* Example code showing how to get current version of macOS from Java
* by running a native command-line tool `sw_vers`.
*/
public class App
{
public static void main ( String[] args )
{
App app = new App();
app.demo();
}
private void demo ( )
{
String version = this.getMacOsVersionNumber();
System.out.println( "version = " + version );
}
public String getMacOsVersionNumber ( )
{
String result = "";
List < String > command = List.of( "sw_vers" , " -productVersion" );
try (
InputStream inputStream = new ProcessBuilder( command ).start().getInputStream() ;
Scanner s = new Scanner( inputStream ).useDelimiter( "\\A" ) ;
)
{
result = s.hasNext() ? s.next() : "";
}
catch ( IOException e )
{
e.printStackTrace();
}
return Objects.requireNonNull( result );
}
}
I'm working on java application which perform some Runtime sub-process on files, for some files I got error cause the Send error report to Microsoft window to appear ,I need to handle this error programmatically, without showing this window to user. Please can anyone help ?
To Suppress windows error reporting the .exe that is being invoked should not terminate with an unhandled exception. This only works if you have access to the source of the application.
Based on the WER Reference - you should use the Win32 API call WerAddExcludedApplication to add the specific .exe files that you are intending to ignore to the per-user ignore list - you could create a simple stub-application that allows you to add applications by name to the ignore list. Then when you invoke the application it does not trigger the error.
Similarly you could create another application to remove them using the WerRemoveExcludedApplication.
Alternatives are to use JNI/JNA to make a class to encapsulate this functionality rather than using Runtime.exec
Here is a simple example using Java Native Access (JNA), which is a simpler version of JNI (no C++ needed for the most part). Download the jna.jar and make it part of your project.
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.win32.StdCallLibrary;
public class JNATest {
public interface CLibrary extends StdCallLibrary {
CLibrary INSTANCE = (CLibrary) Native.loadLibrary("wer.dll",
CLibrary.class);
int WerAddExcludedApplication(WString name, boolean global);
int WerRemoveExcludedApplication(WString name, boolean global);
}
public static void main(String[] args) {
CLibrary.INSTANCE.WerAddExcludedApplication(new WString("C:\\foo.exe"), false);
CLibrary.INSTANCE.WerRemoveExcludedApplication(new WString("C:\\foo.exe"), false);
}
}
Basically, replace the new WString(...) value with the name of the application that you are intending to ignore. It should be ignored for the purposes of windows error reporting at that point.
Bear in mind that the wer.dll is only on Windows Vista and newer, so if this is a problem, then you may need to edit the registry entries manually.
You can always use try-catch-finally statement:
try
{
some code here (the code that is causing the error);
}
catch (Exception x)
{
handle exception here;
}
It works for me...
EDIT Here is the link that can help you a little bit more:
http://www.exampledepot.com/egs/Java%20Language/TryCatch.html
I am trying to grab filesystem events on OS / Kernel level on OS X.
There are 2 requirements i have to follow. The first one is to do this in java as the whole project im developing for is written in java. The second one is that i have to find out when a document is opened.
For Linux I used inotify-java, but I can't find a good equivalent on OS X. Also the JNA doesn't provide a helpful binding. Currently I'm avoiding catching events by frequently calling the lsof program. This, however, is a bad solution.
Thanks for the help.
You can use dtrace on OSX, but since it needs root privileges it's not something you'd want to put into a runtime of a system.
In any case, you won't be able to do this in pure Java (any Java API would be a wrapper around some lower level C introspection, and if you're doing it kernel-wide, would need to be done as root).
If you just want to track when your program is opening files (as opposed to other files on the same system) then you can install your own Security Manager and implement the checkRead() family of methods, which should give you an idea of when accesses are happening.
import java.io.*;
public class Demo {
public static void main(String args[]) throws Exception {
System.setSecurityManager(new Sniffer());
File f = new File("/tmp/file");
new FileInputStream(f);
}
}
class Sniffer extends SecurityManager {
public void checkRead(String name) {
System.out.println("Opening " + name);
}
}
It looks like I cannot use Desktop.open() on PDF files regardless of location. Here's a small test program:
package com.example.bugs;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
public class DesktopOpenBug {
static public void main(String[] args)
{
try {
Desktop desktop = null;
// Before more Desktop API is used, first check
// whether the API is supported by this particular
// virtual machine (VM) on this particular host.
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
for (String path : args)
{
File file = new File(path);
System.out.println("Opening "+file);
desktop.open(file);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
If I run DesktopOpenBug with arguments c:\tmp\zz1.txt c:\tmp\zz.xml c:\tmp\ss.pdf (3 files I happen to have lying around) I get this result: (the .txt and .xml files open up fine)
Opening c:\tmp\zz1.txt
Opening c:\tmp\zz.xml
Opening c:\tmp\ss.pdf
java.io.IOException: Failed to open file:/c:/tmp/ss.pdf. Error message:
The parameter is incorrect.
at sun.awt.windows.WDesktopPeer.ShellExecute(Unknown Source)
at sun.awt.windows.WDesktopPeer.open(Unknown Source)
at java.awt.Desktop.open(Unknown Source)
at com.example.bugs.DesktopOpenBug.main(DesktopOpenBug.java:21)
What the heck is going on? I'm running WinXP, I can type "c:\tmp\ss.pdf" at the command prompt and it opens up just fine.
edit: if this is an example of Sun Java bug #6764271 please help by voting for it. What a pain. >:(
I never knew about this Desktop command, untill recently through this post:
would Java's Runtime.getRuntime().exec() run on windows 7?
Previously i have been using:
Runtime.getRuntime().exec("rundll32 SHELL32.DLL,ShellExec_RunDLL "+ myfile);
And it has always worked for me. If your method does not work, may be you can think about try this command.
If you switch the order of your arugments does that cause one of the other files to get that same error. I wonder if you need to trim the end of the path before calling the File constructor.
umm...yeah ignore that... check the documentation of Desktop.open. open throws an IO exception "if the specified file has no associated application or the associated application fails to be launched " ... also from the top of the page... "The mechanism of registereing, accessing, and launching the associated application is platform-dependent. "
code for the Desktop class: http://fuseyism.com/classpath/doc/java/awt/Desktop-source.html
The open method calls DesktopPeer.open.
DesktopPeer source: http://www.jdocs.com/javase/7.b12/java/awt/peer/DesktopPeer.html
DesktopPeer is implementation specific.
Here is source for a Windows-specific implementation:
http://www.java2s.com/Open-Source/Java-Document/6.0-JDK-Platform/windows/sun/awt/windows/WDesktopPeer.java.htm
open->ShellExecute->(Native)ShellExecute
Native ShellExecute is a wrapper for Win32 ShellExecute. Here is info on the function.
http://msdn.microsoft.com/en-us/library/bb762153(VS.85).aspx
My suggestion for a work around would be to write your own implmentation of the ShellExecute function. Here is source from someone who did it. http://www.heimetli.ch/shellexec.html
I'm trying to figure out how to open the system preferred editor for a given file.
Say, we have a file manager, written in Java. User goes to folder and sees the list of files. And, for example, there is a file Icon.jpg. User double clicks on the filename and file opens in system's preferred editor (i.e. Gimp). The main issue is - how to do that?
We can do Runtime.getRuntime().exec("something file"), but this way you should know which program is preferred in user environment. But how?
We also are able to do Desktop.getDesktop().edit(File file), but this way we cannot track process and aren't able to know then this child process is closed. Other issue - function doesn't work on linux (at least on Ubuntu 8.10). There is also Desktop.getDesktop().open(File file), but it forces to open file viewer, instead of system viewer for that file type.
I am searching for a solution all week, but didn't got any suitable and generic one. Do you know the other approaches to this question? For my project it would be enough if it would work on Windows+Linux+Mac.
Thank you for your answers and advices.
Edit on 2009-02-08 23:04
Other suggestion: can I force "application selection" window in Windows and in Linux, as in Mac with "open file"? For example, then you trying to open file, you are being asked to choose application from list of system preferred ones? (something like "Open with..." in Windows explorer). Do you know?
Seems that if you can't use java.awt.Desktop you have to distinguish between the OSes:
Windows:
RUNDLL32.EXE SHELL32.DLL,OpenAs_RunDLL <file.ext>
Linux:
edit <file.ext>
Mac:
open <file.ext>
HTH. Obviously, that is not very portable...
Check out the java.awt.Desktop object. In your case, you want to invoke edit()
If you want to ensure that a given platform supports this call, then you can do something like the following (I have not tested this code):
public boolean editFile(final File file) {
if (!Desktop.isDesktopSupported()) {
return false;
}
Desktop desktop = Desktop.getDesktop();
if (!desktop.isSupported(Desktop.Action.EDIT)) {
return false;
}
try {
desktop.edit(file);
} catch (IOException e) {
// Log an error
return false;
}
return true;
}
This isn't cross-platform, but on Mac OS X you can do
Runtime.getRuntime().exec("open filename");
The open(1) executable uses LaunchServices to pick the right program to execute, and then uses that to open the file named filename.
This will work in windows
Runtime.getRuntime().exec( "CMD /C START filename.ext " );
For JavaFX applications, we can use HostServices. This question covers how to use HostServices. This should work on Ubuntu (tested)/Windows (not tested) and Mac (not tested).
import java.io.File;
import java.io.IOException;
public class App extends Application {
}
File file = new File("/your/file/path");
HostServices hostServices = getHostServices();
hostServices.showDocument(file.getAbsolutePath());
getHostServices() is a method of JavaFX Application class.