Question - Get File Version of .exe in java on Linux for some strange client.
Solution -
I used JNA library to read file version using Java. Given below code is running fine on windows platform but it is throwing below error on Linux docker image.
"Unable to load library 'version': Error loading shared library libversion.so: No such file or directory Error loading shared library libversion.so: No such file or directory Native library (linux-x86-64/libversion.so) not found in resource path..".
private String GetFileVersion(String filePath) {
File fileToCheck = new File(filePath);
short[] rtnData = new short[4];
int infoSize = Version.INSTANCE.GetFileVersionInfoSize(fileToCheck.getAbsolutePath(), null);
Pointer buffer = Kernel32.INSTANCE.LocalAlloc(WinBase.LMEM_ZEROINIT, infoSize);
try {
Version.INSTANCE.GetFileVersionInfo(fileToCheck.getAbsolutePath(), 0, infoSize, buffer);
IntByReference outputSize = new IntByReference();
PointerByReference pointer = new PointerByReference();
Version.INSTANCE.VerQueryValue(buffer, "\\", pointer, outputSize);
VerRsrc.VS_FIXEDFILEINFO fileInfoStructure = new VerRsrc.VS_FIXEDFILEINFO(pointer.getValue());
rtnData[0] = (short) (fileInfoStructure.dwFileVersionMS.longValue() >> 16);
rtnData[1] = (short) (fileInfoStructure.dwFileVersionMS.longValue() & 0xffff);
rtnData[2] = (short) (fileInfoStructure.dwFileVersionLS.longValue() >> 16);
rtnData[3] = (short) (fileInfoStructure.dwFileVersionLS.longValue() & 0xffff);
return String.format("%s.%s.%s.%s", rtnData[0], rtnData[1], rtnData[2], rtnData[3]);
} catch (Exception exception) {
return null;
} finally {
Kernel32.INSTANCE.GlobalFree(buffer);
}
}
I will start by answering the question that you asked, though I doubt it is what you actually need to know.
The types of different executable file formats are encoded in the first few bytes of the file. For example, ELF files (executables, shared libraries) are described in this Wikipedia page.
So there are a number of ways to find out what kind of executable in Java:
Write some code that reads the first few bytes and decodes the file header information, as per the format described in the Wikipedia link above.
Find an existing Java library that does this and work out how to do this. (Google for "java file magic library" and see what you can find.)
Read about the Linux file command and write some Java code to run file on each library and parse the output.
What I think you actually need to do is a bit different:
Locate the file or files in the file system that the Java is looking for: apparently libversion.so or linux-x86-64/libversion.so. (The file could well be a symlink. Follow it.)
Run file on each file to check that it is the right kind of library. They need to be 32 or 64 bit corresponding the JVM you are running, and the correct ABI and ISA for the platform.
Check that the files are where the JVM expects to find them. The JVM searches for libraries in directories listed in the "java.library.path" system property. You can (if necessary) set the path using a -Djava.library.path=... JVM option.
See "java.library.path – What is it and how to use" for more information on library loading.
(There is absolutely no need to do step 2 "from" or "in" Java.)
I think I have finally worked out what you are doing.
The Version you are using is actually coming from the package com.sun.jna.platform.win32. It is not part of the JNA library (jna.jar). I think it is actually part of jna-platform.jar. If I understand things correctly, that is the generated JNA adapter library for the Windows COM dlls.
If I have that correct, you would actually need the Windows COM native libraries compiled and built for the Linux platform to do what you are trying to do.
AFAIK, that's not possible.
So how could you make this work? Basically you need to do one of the following:
Look for an existing pure Java library for extracting the version information from a Windows ".exe" file. I don't think it is likely that you will find one.
Find the specification for the Windows ".exe" file format and write your own Java code to extract the version information. I haven't looked for the spec to see how much work it would be.
Then you rewrite the code that you added your question to use the alternative API.
The "libversion" file that I mentioned in my other answer is not relevant. It is something else. It is a red herring.
Related
First of: I don't know if this problem is rather related to UWP and it's strange security settings or rather me mis-using JNI's Invocation
Anyhow, I use this code to spawn a jvm inside a c++ static library (With /ZW, so compiling for UWP, but disabling it doesn't change a thing).
The following code is based upon Oracle's Notes and openjdk's code (exeinvoke.c, launcher.c, ...):
void JVM4UWP::loadVM(string vmOptions[], int numOptions, int jni_version) {
/* Copy the VM Options */
options = new JavaVMOption[numOptions];
for (uint8_t i = 0; i < numOptions; i++) {
options[i].optionString = new char[vmOptions[i].length() + 1]; // \0 takes the additional byte
strncpy_s(options[i].optionString, vmOptions[i].length() + 1, vmOptions[i].c_str(), vmOptions[i].length() + 1);
}
//options[0].optionString = "-Djava.class.path=/usr/lib/java";
vm_args = new JavaVMInitArgs();
vm_args->version = jni_version;
vm_args->nOptions = numOptions;
vm_args->options = options;
vm_args->ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, vm_args);
delete options;
/* invoke the Main.test method using the JNI */
jclass cls = env->FindClass("Main");
jmethodID mid = env->GetStaticMethodID(cls, "main", "(I)V");
env->CallStaticVoidMethod(cls, mid, 100);
/* We are done. */
jvm->DestroyJavaVM();
}
Now the problem with this is: I added #include <jni.h>, I added lib/jvm.lib as static library (taken from oracle's jdk), however it seems that something is still incomplete.
When I run the application, I get an error upon loadup (App::App of the sample UWP application isn't even run).
The console returns:
Das Programm "[1892] TestJVM.exe" wurde mit Code -1073741515 (0xc0000135) 'Es wurde keine abhängige DLL gefunden' beendet.
Loosely translated it's: Could find no dependant dll.
Now this reminds me of this MSDN Article. I have to admit that I don't fully understand it's contents. Especially since jvm.lib cannot have any Manifests. I tried to add jvm.dll, java.dll and jli.dll to the folder of the .exe but for some reason it still doesn't work.
Since the above code should work without dlls, I think it might be related to UWP, maybe even to my code:
My Setup currently is like this: I have the JVM4UWP static library which in-turn is linked statistically against jvm.lib. This Library is then included into the Demo UWP Project, which I am trying to execute.
Note that only subset of usual Windows APIs is available for uwp applications and I doubt that jvm is compatible with these restrictions. "lib/jvm.lib" might actually be an export library, not static library.
Also you probably might want to get familiar with Using a Win32 DLL in a Universal Windows Platform App.
It looks like JVM library is missing.
Take a look here for a sample where JVM is executed from C code:
http://jnicookbook.owsiak.org/recipe-no-027/
This sample is little bit more complex (it uses POSIX threads) but still, basics are the same.
Make sure that all libraries are on PATH so they can be loaded by either C++ or JVM.
Have fun with JNI!
Okay, so I made progress:
This Question, compiling for Win32 and the fact that you have to embed dll's so they are available, showed me that the jvm.dll was missing, even though I added it to the program's path. Using gflags as pointed out above shows you which dlls it's trying to load and where it tried to locate them.
Actually, you don't have to embed dll's for debugging purposes, but the dlls have to reside inside of the AppX Folder, instead of the Root folder (even though there is also an exe file there).
So since it's not mentioned elsewhere: When linking against jvm.lib, you still need the jvm.dll since this lib only seems to handle dynamic loading/wrapping/etc
Note that only subset of usual Windows APIs is available for uwp applications and I doubt that jvm is compatible with these restrictions.
Yes and no, I had a look at openJDKs Sourcecode and the Majority of it is only File Handling and such, so I guess the part which could require reworking is the Memory and Threading related things.
Anyhow I was expecting such errors rather as Access Violations since some OS imports are simply 0x0 (aka not found), so I could now what I have to change to make it UWP compatible.
I might also give recompiling the openJDK with /ZW a try, but it's a challenging task.
Using a Win32 DLL in a Universal Windows Platform App also tells that at least the static librarys seem to work as is, maybe one needs wrappers for the illegal API to link into the project.
I was having trouble running my obfuscated jar, I got the "Java Virtual Machine Launcher" message "A Java Exception has occurred" when I try to run it. (This error is a popup, not on the command line) I tried running from the command line and printing the stacktrace from the jvm console and didn't see any exceptions.
So I did a little troubleshooting: I disabled shrinking, obfuscating AND optimizing so that the output (should be) the same as the input. The output jar turned out to be a few KB less than the input. I extracted both jars, decompiled the .class files and compared the source of every single file and the MANIFEST.MF and they were all the same*!
I'm using ProGuard 4.10, JDK 1.7.0_25 x64 and Windows 8. I'm building a fairly elaborate JavaFX program and I believe I properly configured ProGuard- but that shouldn't matter anymore if shrinking, obfuscating and optimizing are all disabled, right? Here are the notes that ProGuard gives me:
Note: duplicate definition of library class [netscape.javascript.JSException]
Note: duplicate definition of library class [netscape.javascript.JSObject]
Note: com.javafx.main.Main accesses a field 'HKEY_LOCAL_MACHINE' dynamically
Note: com.javafx.main.Main accesses a field 'HKEY_LOCAL_MACHINE' dynamically
Note: com.javafx.main.Main accesses a declared method 'reset()' dynamically
Note: com.javafx.main.Main$2 accesses a field 'ERROR_MESSAGE' dynamically
When I first tried obfuscating, I skipped com.javafx.main.** but I still got those messages. Any help would be greatly appreciated. I had no problems running my jar before it processed by ProGuard. Thanks.
*EDIT: When doing a binary comparison, the manifest files differ (the original is longer) but doing an ASCII comparison both files are identical. All other files are completely identical. The difference between the two MANIFEST.MF files is PC vs. UNIX line breaks.
*EDIT2: I ran the file with the -jar option, and I did get an explicit exception!
java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
From com.javafx.main.Main here is the use of ERROR_MESSAGE and HKEY_LOCAL_MACHINE:
int ERROR_MESSAGE = ERROR_MESSAGE_Field.getInt(null);
Method showMessageDialogMethod = jOptionPaneClass.getMethod("showMessageDialog", new Class[] { componentClass, Object.class, String.class, Integer.TYPE });
showMessageDialogMethod.invoke(null, new Object[] { null, this.val$string, "JavaFX Launcher Error", new Integer(ERROR_MESSAGE) });
...
Field HKEY_LOCAL_MACHINE_Field = winRegistryClass.getField("HKEY_LOCAL_MACHINE");
int HKEY_LOCAL_MACHINE = HKEY_LOCAL_MACHINE_Field.getInt(null);
String path = (String)mGet.invoke(null, new Object[] { new Integer(HKEY_LOCAL_MACHINE), "Software\\Oracle\\JavaFX\\" + version, "Path" });
The jar was signed; I made an unsigned version and it obfuscated correctly!
I'm trying to use RemoveDrive.exe, found here, in my Java application. I have it in my JAR, and I'm extracting it to a temporary file using the following code, however when I try to run it I get an IOException which says CreateProcess error=5, Access is denied. The program doesn't normally need admin priviledges though. Any ideas on what could be causing the issue?
File RDexe = File.createTempFile("rmvd", ".exe");
InputStream exesrc = (InputStream) GraphicUI.class.getResource("RemoveDrive.exe").openStream();
FileOutputStream out = new FileOutputStream(RDexe);
byte[] temp = new byte[1024];
int rc;
while((rc = exesrc.read(temp)) > 0)
out.write(temp, 0, rc);
exesrc.close();
out.close();
RDexe.deleteOnExit();
// run executable
Runtime runtime = Runtime.getRuntime();
System.out.println(RDexe.getPath() + " " + "F:\\" + " -b -s");
Process proc = runtime.exec(RDexe.getPath() + " " + "F:\\" + " -b");
InputStream is = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line; boolean ejected = false;
while((line = reader.readLine()) != null)
if(line.equalsIgnoreCase("failed")) ejected = false;
else if(line.equalsIgnoreCase("success")) ejected = true;
reader.close();
is.close();
UPDATE: If I enable the built-in Administrator account (net user administrator /active:yes), everything works fine from there. However if I right click and run as administrator in my standard account, I still get the error and UAC doesn't even ask for permission.
EDIT: Seeing as though the bounty is nearly finished, please see my SuperUser question which has helped me solve this problem... I'll be awarding the bounty and accepting an answer soon.
This may not be the problem in your situation, but some anti-virus programs will prevent executables or scripts inside temporary folders from being run. Instead of creating a temporary file, try putting it in the user directory:
File rdExe = new File(System.getProperty("user.home") + "/.yourProgramName/rmvd.exe");
rdExe.getParentFile().mkdirs();
just a heads up on another way to run files, have you thought of using the java Desktop object? : http://docs.oracle.com/javase/6/docs/api/java/awt/Desktop.html
i've found it useful when needing to run programs through my java program. something like this could work for you:
Desktop.getDesktop().open(new File("enter path and name of the file"));
hope you find it useful
I am not JAVA user but isn't it 32 vs. 64 bit issue ?
On 64 bit Windows error code 5 usually means that executable is not 64 bit compatible. Sometimes this is the case even when executable need to access only some (older win) system directory which does not exist anymore. To prove this try to use your executable in command line. if you can manage to get it work there than it is different issue. If not find executable for your OS.
Another possibility is that the file has to be physically present on some drive.
You wrote that you has it as temporary. Not shore what it means for JAVA. If it only copy it to some file and delete after use than its OK but if it is only in memory somewhere than that could be problem if executable need access to itself. To prove this just copy the file to some known location and then run it from there (in JAVA). if it works than you will need to do something about it (copy and delete executable from JAVA before and after execution to physical disk medium or whatever)
Another possibility is that error code 5 comes from JAVA environment an not from OS
In that case I have not a clue what it means (not JAVA user)
Seeing as though it has only been touched on here, I will say that the issue was related to permissions in Windows, and is not anything to do with Java.
As stated in the SuperUser question I've linked to in my original question, I found that my usual account did not have ownership of that folder for some unknown reason - so nothing could be executed; it wasn't just the temporary file I had created in Java.
Even though I am an administrator, in order to take ownership of the folder I had to enable the Built-In administrator account and grant myself ownership. Since I did that, it has all worked as expected.
Thanks to all for their efforts, I will award the bounty to the answer that was most detailed and put me on the right tracks.
What version of Windows are you running? Microsoft significantly tightened the restrictions around executing programs in Windows 7. My guess is that it the OS won't allow you to fork something that wasn't authenticated at the time your program was launched. I'd try running it on Windows 2000 or XP and see if you have the same issues.
How can I associate a file type like .vlan in mac within java code?
Runtime.getRuntime().exec("defaults write com.apple.LaunchServices LSHandlers -array-add
\"<dict><key>LSHandlerContentTag</key>
<string>.vlan</string><key>LSHandlerContentTagClass</key>
<string>public.filename-extension</string><key>LSHandlerRoleAll</key>
<string>org.category.program</string></dict>\"");
If the app. has a GUI, deploy it using Java Web Start and declare the file extension/type in the JNLP (launch) file. Here is a demo. of the JNLP API file services which should be able to associate the text/sleepytime content type of .zzz file with the (small) app.
Adding an association for a file type is supported to work on Windows, OS X & *nix (for all permissions, as well as sand-boxed apps. (the latter prompted)).
Try this :
Runtime.getRuntime().exec(
new String[] {
"defaults",
"write",
"com.apple.LaunchServices",
"LSHandlers",
"-array-add",
"<dict><key>LSHandlerContentTag</key><string>.vlan</string><key>LSHandlerContentTagClass</key><string>public.filename-extension</string><key>LSHandlerRoleAll</key><string>org.category.program</string></dict>"
}
);
And just to add, Runtime's exec has quite some pitfalls one need to be aware of.
I became so much upset with this simple code segment:
FileConnection fc = (FileConnection) Connector.open("file:///root1/photos/2.png");
System.out.println(is.available());
byte[] fileBytes = new byte[is.available()];
int sizef = is.read(fileBytes);
System.out.println("filesize:"+sizef);
When I deploy this midlet in my mobile (with proper file location) It works properly i.e it shows proper filesize, but in the pc emulator it is constantly giving filesize of: 0. Obviously no Exception and I have the proper file in that location.
I am using j2mewtk sdk in netbeans 6.9. I tried uninstalling, installing wtk & netbeans.
Another thing is Everytime I run the emulator it creates C:\Users\Mahsruf\j2mewtk\2.5.2\appdb\temp.DefaultColorPhone6 new location like temp.DefaultColorPhone1,2,3,4 etc.
If I use jme SDK 3.0 in netbeans the file size is still 0, but now with a extra line in output window: [WARN] [rms ] javacall_file_open: _wopen failed for: C:\Users\Mahsruf\javame-sdk\3.0\work\0\appdb\_delete_notify.dat
What am I doing wrong?
This is not coding related issue. If multiple instances of the same emulator skin run simultaneously, the toolkit generates unique file paths for each one. For example, on Windows instances of DefaultColorPhone might have a file path name of workdir\appdb\temp.DefaultColorPhone1, workdir\appdb\temp.DefaultColorPhone2, and so forth.
Solution: The file workdir\appdb\DefaultColorPhone\in.use keeps track of the number of storage roots marked as in use. If the emulator crashes, you need to delete the in.use file