Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added VersionUtil class and utility method for obtaining file version information #562

Merged
merged 1 commit into from
Dec 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Features
* [#554](https://github.com/java-native-access/jna/pull/554): Initial code for a few Unix 'libc' API(s) [@lgoldstein](https://github.com/lgoldstein)
* [#552](https://github.com/java-native-access/jna/pull/552): Added `Module32FirstW` and `Module32NextW` to `com.sun.jna.platform.win32.Kernel32` (and helper to `com.sun.jna.platform.win32.Kernel32Util`) and `MODULEENTRY32W` structure to `com.sun.jna.platform.win32.Tlhelp32` - [@mlfreeman2](https://github.com/mlfreeman2).
* [#564](https://github.com/java-native-access/jna/pull/564): Use generic definition of Native#loadLibrary [@lgoldstein](https://github.com/lgoldstein)
* [#562](https://github.com/java-native-access/jna/pull/562): Added `com.sun.jna.platform.win32.VersionUtil` with `getFileVersionInfo` utility method to get file major, minor, revision, and build version parts - [@mlfreeman2](https://github.com/mlfreeman2).

Bug Fixes
---------
Expand Down
32 changes: 32 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/VerRsrc.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,38 @@ public VS_FIXEDFILEINFO(Pointer memory) {
*/
public WinDef.DWORD dwFileDateLS;

public int getFileVersionMajor() {
return dwFileVersionMS.intValue() >>> 16;
}

public int getFileVersionMinor() {
return dwFileVersionMS.intValue() & 0xffff;
}

public int getFileVersionRevision() {
return dwFileVersionLS.intValue() >>> 16;
}

public int getFileVersionBuild() {
return dwFileVersionLS.intValue() & 0xffff;
}

public int getProductVersionMajor() {
return dwProductVersionMS.intValue() >>> 16;
}

public int getProductVersionMinor() {
return dwProductVersionMS.intValue() & 0xffff;
}

public int getProductVersionRevision() {
return dwProductVersionLS.intValue() >>> 16;
}

public int getProductVersionBuild() {
return dwProductVersionLS.intValue() & 0xffff;
}

protected List getFieldOrder() {
return Arrays.asList(new String[] { "dwSignature", "dwStrucVersion", "dwFileVersionMS", "dwFileVersionLS", "dwProductVersionMS", "dwProductVersionLS", "dwFileFlagsMask", "dwFileFlags", "dwFileOS", "dwFileType", "dwFileSubtype", "dwFileDateMS", "dwFileDateLS" });
}
Expand Down
77 changes: 77 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/VersionUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* Copyright (c) 2015 Michael Freeman, All Rights Reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.win32;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.VerRsrc.VS_FIXEDFILEINFO;
import com.sun.jna.platform.win32.Version;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;

/**
* Reads Windows Version info from files (the version details you can see by
* right-clicking and choosing properties)
*
* @author mlfreeman[at]gmail.com
*/
public class VersionUtil {

/**
* Gets the file's version number info
*
* @param filePath
* The path to the file
* @return The VS_FIXEDFILEINFO structure read from the file.<br>
* Use the getFileVersionMajor(), getFileVersionMinor(),
* getFileVersionRevision(), and getFileVersionBuild()
* @throws UnsupportedOperationException
* if VerQueryValue fails to get version info from the file.
*/
public static VS_FIXEDFILEINFO getFileVersionInfo(String filePath) {
IntByReference dwDummy = new IntByReference();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of returning an int[], return a class - e.g.:

public class VersionInfo implements Serializable, Cloneable, Comparable<VersionInfo> {
    private static final long serialVersionUID = ...generate it via the IDE...;

    private int major = -1;
    private int minor = -1;
    private int buildNumber = -1;
    private int revision = -1;

    public VersionInfo() {
          super();
    }

    public VersionInfo(int major, int minor, int buildNumber, int release) {
        this.minor/major/build/release = ...respective parameter...
    }

    public int getMajor/Minor/BuildNumber/Release() { 
        return ...respective member...
    }

    public void setMajor/Minor/BuildNumber/Release(int value) {
        this..minor/major/build/release = value;
    }

   @Override
    public int hashCode() {
        ...
    }

   @Override
    public boolean equals(Object o) {
        ...check if null, this == o and getClass() == o.getClass()...
       return compare((VersionInfo) o) == 0;
    }

    @Override
    public int compare(VersionInfo other) {
        ..check if other is null or this...
       ...compare major/minor/build/release - in this order...
    }

    @Override
    public VersionInfo clone() {
        try {
            return getClass().cast(super.clone());
        } catch(CloneNotSupportedException e) { // unexpected
            throw new RuntimeException("Failed to clone: " + toString(), e);
        }
    }

   @Override
    public String toString() {
        return getMajor() + "." + getMinor() + "." + getBuildNumber() + "." + getRevision();
    }

}

This way one does not need to remember which index in the array is the major/minor/build/revision...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this sound as an option?

  1. Change the method name to getFileVersionInfo()
  2. Return the VS_FIXEDFILEINFO that I already have in the function.
  3. Add the following four getters to VS_FIXEDFILEINFO:
        public int getFileVersionMajor() {
            return dwFileVersionMS.intValue() >>> 16;
        }

        public int getFileVersionMinor() {
            return dwFileVersionMS.intValue() & 0xffff;
        }

        public int getFileVersionRevision() {
            return dwFileVersionLS.intValue() >>> 16;
        }

        public int getFileVersionBuild() {
            return dwFileVersionLS.intValue() & 0xffff;
        }


int versionLength = Version.INSTANCE.GetFileVersionInfoSize(filePath, dwDummy);

// Reading version info failed.
// throw a Win32Exception with GetLastError()
if (versionLength == 0) {
throw new Win32Exception(Native.getLastError());
}

// buffer to hold version info
Pointer lpData = new Memory(versionLength);

// pointer to pointer to location in aforementioned buffer
PointerByReference lplpBuffer = new PointerByReference();

if (!Version.INSTANCE.GetFileVersionInfo(filePath, 0, versionLength, lpData)) {
throw new Win32Exception(Native.getLastError());
}

// here to make VerQueryValue happy.
IntByReference puLen = new IntByReference();

// this does not set GetLastError, so no need to throw a Win32Exception
if (!Version.INSTANCE.VerQueryValue(lpData, "\\", lplpBuffer, puLen)) {
throw new UnsupportedOperationException("Unable to extract version info from the file: \"" + filePath + "\"");
}

VS_FIXEDFILEINFO fileInfo = new VS_FIXEDFILEINFO(lplpBuffer.getValue());
fileInfo.read();
return fileInfo;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.sun.jna.platform.win32;

import java.io.File;

import com.sun.jna.platform.win32.VerRsrc.VS_FIXEDFILEINFO;

import junit.framework.TestCase;

public class VersionUtilTest extends TestCase {

public static void main(String[] args) {
junit.textui.TestRunner.run(VersionUtilTest.class);
}

public void testGetFileVersionNumbers() {
String testFileName = "regedit.exe";
File file = new File(System.getenv("SystemRoot"), testFileName);
assertTrue("Test file with version info in it should exist.", file.exists());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be more specific:

assertTrue("Test file not found: " + file, file.exists());

This may seem superfluous since it is "obvious" that you are looking for regedit.exe. However, what if the test system is mis-configured and SystemRoot is not defined or defined incorrectly ? The "message-less" assertion would not provide enough information to diagnose the failure. Failing assertions should provide as much information as possible to enable us to diagnose the problem.

VS_FIXEDFILEINFO version = VersionUtil.getFileVersionInfo(file.getAbsolutePath());
assertNotNull("Version info should have been returned.", version);

assertTrue("The major file version number should be greater than 0 when pulling version from \"" + testFileName + "\"", version.getFileVersionMajor() > 0);
assertTrue("The minor file version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getFileVersionMinor() >= 0);
assertTrue("The revision file version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getFileVersionRevision() >= 0);
assertTrue("The build file version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getFileVersionBuild() > 0);

assertTrue("The major product version number should be greater than 0 when pulling version from \"" + testFileName + "\"", version.getProductVersionMajor() > 0);
assertTrue("The minor product version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getProductVersionMinor() >= 0);
assertTrue("The revision product version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getProductVersionRevision() >= 0);
assertTrue("The build product version number should be greater than or equal to 0 when pulling version from \"" + testFileName + "\"", version.getProductVersionBuild() > 0);
}
}