正如我今天偶然发现this post here,展示了如何提取版本信息一个可执行文件,你的后回到我的脑海,所以我开始了一些调查。
This further post指出只能从可执行文件本身提取进程描述,因此我们需要将手放在JNA上,而不是解析WMIC
或TASKLIST
的某些输出。这篇文章进一步链接了提供C方法提取流程描述的MSDN page for VerQueryValue函数。这里尤其是第二个参数应该是有趣的,因为它定义了要返回的内容。
使用第一篇文章中提到的代码,它现在将C struct typedefs转换为Java等价物。我将张贴在这里的完整代码,至少对我的作品上Windows 7 64bit
:
的Maven的pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>at.rovo.test</groupId>
<artifactId>JNI_Test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>JNI_Test</name>
<url>http://maven.apache.org</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- JNA 3.4
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>platform</artifactId>
<version>3.4.0</version>
</dependency>
-->
<!-- JNA 4.0.0 -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>4.0.0</version>
</dependency>
<!-- -->
</dependencies>
</project>
LangAndCodePage.java - 一个辅助类,其中提取的十六进制值映射到人类可读的输出包含在翻译表中的语言和代码页信息。值因此从this page here采取:
package at.rovo.test.jni_test;
import java.util.HashMap;
import java.util.Map;
public final class LangAndCodePage
{
private final static Map<String, String> languages = new HashMap<>();
private final static Map<String, String> codePage = new HashMap<>();
static
{
languages.put("0000", "Language Neutral");
languages.put("0401", "Arabic");
languages.put("0402", "Bulgarian");
languages.put("0403", "Catalan");
languages.put("0404", "Traditional Chinese");
languages.put("0405", "Czech");
languages.put("0406", "Danish");
languages.put("0407", "German");
languages.put("0408", "Greek");
languages.put("0409", "U.S. English");
languages.put("040A", "Castilian Spanish");
languages.put("040B", "Finnish");
languages.put("040C", "French");
languages.put("040D", "Hebrew");
languages.put("040E", "Hungarian");
languages.put("040F", "Icelandic");
languages.put("0410", "Italian");
languages.put("0411", "Japanese");
languages.put("0412", "Korean");
languages.put("0413", "Dutch");
languages.put("0414", "Norwegian ? Bokmal");
languages.put("0810", "Swiss Italian");
languages.put("0813", "Belgian Dutch");
languages.put("0814", "Norwegian ? Nynorsk");
languages.put("0415", "Polish");
languages.put("0416", "Portuguese (Brazil)");
languages.put("0417", "Rhaeto-Romanic");
languages.put("0418", "Romanian");
languages.put("0419", "Russian");
languages.put("041A", "Croato-Serbian (Latin)");
languages.put("041B", "Slovak");
languages.put("041C", "Albanian");
languages.put("041D", "Swedish");
languages.put("041E", "Thai");
languages.put("041F", "Turkish");
languages.put("0420", "Urdu");
languages.put("0421", "Bahasa");
languages.put("0804", "Simplified Chinese");
languages.put("0807", "Swiss German");
languages.put("0809", "U.K. English");
languages.put("080A", "Spanish (Mexico)");
languages.put("080C", "Belgian French");
languages.put("0C0C", "Canadian French");
languages.put("100C", "Swiss French");
languages.put("0816", "Portuguese (Portugal)");
languages.put("081A", "Serbo-Croatian (Cyrillic)");
codePage.put("0000", "7-bit ASCII");
codePage.put("03A4", "Japan (Shift ? JIS X-0208)");
codePage.put("03B5", "Korea (Shift ? KSC 5601)");
codePage.put("03B6", "Taiwan (Big5)");
codePage.put("04B0", "Unicode");
codePage.put("04E2", "Latin-2 (Eastern European)");
codePage.put("04E3", "Cyrillic");
codePage.put("04E4", "Multilingual");
codePage.put("04E5", "Greek");
codePage.put("04E6", "Turkish");
codePage.put("04E7", "Hebrew");
codePage.put("04E8", "Arabic");
}
// prohibit instantiation
private LangAndCodePage()
{
}
public static void printTranslationInfo(String lang, String cp)
{
StringBuilder builder = new StringBuilder();
builder.append("Language: ");
builder.append(languages.get(lang));
builder.append(" (");
builder.append(lang);
builder.append("); ");
builder.append("CodePage: ");
builder.append(codePage.get(cp));
builder.append(" (");
builder.append(cp);
builder.append(");");
System.out.println(builder.toString());
}
}
最后但并非最不重要,提取文件的版本和Windows资源管理器的文件描述的代码。该代码包含大量的文档,我用它来学习的东西,我自己;)
package at.rovo.test.jni_test;
import java.io.IOException;
import com.sun.jna.Memory;
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.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class FileVersion
{
// The structure as implemented by the MSDN article
public static class LANGANDCODEPAGE extends Structure
{
/** The language contained in the translation table **/
public short wLanguage;
/** The code page contained in the translation table **/
public short wCodePage;
public LANGANDCODEPAGE(Pointer p)
{
useMemory(p);
}
public LANGANDCODEPAGE(Pointer p, int offset)
{
useMemory(p, offset);
}
public static int sizeOf()
{
return 4;
}
// newer versions of JNA require a field order to be set
@Override
protected List getFieldOrder()
{
List fieldOrder = new ArrayList();
fieldOrder.add("wLanguage");
fieldOrder.add("wCodePage");
return fieldOrder;
}
}
public static void main(String[] args) throws IOException
{
// http://msdn.microsoft.com/en-us/library/ms647464%28v=vs.85%29.aspx
//
// VerQueryValue will take two input and two output parameters
// 1. parameter: is a pointer to the version-information returned
// by GetFileVersionInfo
// 2. parameter: will take a string and return an output depending on
// the string:
// "\\"
// Is the root block and retrieves a VS_FIXEDFILEINFO struct
// "\\VarFileInfo\Translation"
// will return an array of Var variable information structure
// holding the language and code page identifier
// "\\StringFileInfo\\{lang-codepage}\\string-name"
// will return a string value of the language and code page
// requested. {lang-codepage} is a concatenation of a language
// and the codepage identifier pair found within the translation
// array in a hexadecimal string! string-name must be one of the
// following values:
// Comments, InternalName, ProductName, CompanyName,
// LegalCopyright, ProductVersion, FileDescription,
// LegalTrademarks, PrivateBuild, FileVersion,
// OriginalFilename, SpecialBuild
// 3. parameter: contains the address of a pointer to the requested
// version information in the buffer of the 1st parameter.
// 4. parameter: contains a pointer to the size of the requested data
// pointed to by the 3rd parameter. The length depends on
// the input of the 2nd parameter:
// *) For root block, the size in bytes of the structure
// *) For translation array values, the size in bytes of
// the array stored at lplpBuffer;
// *) For version information values, the length in
// character of the string stored at lplpBuffer;
String filePath = "C:\\Windows\\explorer.exe";
IntByReference dwDummy = new IntByReference();
dwDummy.setValue(0);
int versionlength =
Version.INSTANCE.GetFileVersionInfoSize(filePath, dwDummy);
if (versionlength > 0)
{
// will hold the bytes of the FileVersionInfo struct
byte[] bufferarray = new byte[versionlength];
// allocates space on the heap (== malloc in C/C++)
Pointer lpData = new Memory(bufferarray.length);
// will contain the address of a pointer to the requested version
// information
PointerByReference lplpBuffer = new PointerByReference();
// will contain a pointer to the size of the requested data pointed
// to by lplpBuffer.
IntByReference puLen = new IntByReference();
// reads versionLength bytes from the executable file into the FileVersionInfo struct buffer
boolean fileInfoResult =
Version.INSTANCE.GetFileVersionInfo(
filePath, 0, versionlength, lpData);
// retrieve file description for language and code page "i"
boolean verQueryVal =
Version.INSTANCE.VerQueryValue(
lpData, "\\", lplpBuffer, puLen);
// contains version information for a file. This information is
// language and code page independent
VS_FIXEDFILEINFO lplpBufStructure =
new VS_FIXEDFILEINFO(lplpBuffer.getValue());
lplpBufStructure.read();
int v1 = (lplpBufStructure.dwFileVersionMS).intValue() >> 16;
int v2 = (lplpBufStructure.dwFileVersionMS).intValue() & 0xffff;
int v3 = (lplpBufStructure.dwFileVersionLS).intValue() >> 16;
int v4 = (lplpBufStructure.dwFileVersionLS).intValue() & 0xffff;
System.out.println(
String.valueOf(v1) + "." +
String.valueOf(v2) + "." +
String.valueOf(v3) + "." +
String.valueOf(v4));
// creates a (reference) pointer
PointerByReference lpTranslate = new PointerByReference();
IntByReference cbTranslate = new IntByReference();
// Read the list of languages and code pages
verQueryVal = Version.INSTANCE.VerQueryValue(
lpData, "\\VarFileInfo\\Translation", lpTranslate, cbTranslate);
if (cbTranslate.getValue() > 0)
{
System.out.println("Found "+(cbTranslate.getValue()/4)
+ " translation(s) (length of cbTranslate: "
+ cbTranslate.getValue()+" bytes)");
}
else
{
System.err.println("No translation found!");
return;
}
// Read the file description
// msdn has this example here:
// for(i=0; i < (cbTranslate/sizeof(struct LANGANDCODEPAGE)); i++)
// where LANGANDCODEPAGE is a struct holding two WORDS. A word is
// 16 bits (2x 8 bit = 2 bytes) long and as the struct contains two
// words the length of the struct should be 4 bytes long
for (int i=0; i < (cbTranslate.getValue()/LANGANDCODEPAGE.sizeOf()); i++))
{
// writes formatted data to the specified string
// out: pszDest - destination buffer which receives the formatted, null-terminated string created from pszFormat
// in: ccDest - the size of the destination buffer, in characters. This value must be sufficiently large to accomodate the final formatted string plus 1 to account for the terminating null character.
// in: pszFormat - the format string. This string must be null-terminated
// in: ... The arguments to be inserted into the pszFormat string
// hr = StringCchPrintf(SubBlock, 50,
// TEXT("\\StringFileInfo\\%04x%04x\\FileDescription"),
// lpTranslate[i].wLanguage,
// lpTranslate[i].wCodePage);
// fill the structure with the appropriate values
LANGANDCODEPAGE langCodePage =
new LANGANDCODEPAGE(lpTranslate.getValue(), i*LANGANDCODEPAGE.sizeOf());
langCodePage.read();
// convert short values to hex-string:
// https://stackoverflow.com/questions/923863/converting-a-string-to-hexadecimal-in-java
String lang = String.format("%04x", langCodePage.wLanguage);
String codePage = String.format("%04x",langCodePage.wCodePage);
// see http://msdn.microsoft.com/en-us/library/windows/desktop/aa381058.aspx
// for proper values for lang and codePage
LangAndCodePage.printTranslationInfo(lang.toUpperCase(), codePage.toUpperCase());
// build the string for querying the file description stored in
// the executable file
StringBuilder subBlock = new StringBuilder();
subBlock.append("\\StringFileInfo\\");
subBlock.append(lang);
subBlock.append(codePage);
subBlock.append("\\FileDescription");
printDescription(lpData, subBlock.toString());
}
}
else
System.out.println("No version info available");
}
private static void printDescription(Pointer lpData, String subBlock)
{
PointerByReference lpBuffer = new PointerByReference();
IntByReference dwBytes = new IntByReference();
// Retrieve file description for language and code page "i"
boolean verQueryVal = Version.INSTANCE.VerQueryValue(
lpData, subBlock, lpBuffer, dwBytes);
// a single character is represented by 2 bytes!
// the last character is the terminating "\n"
byte[] description =
lpBuffer.getValue().getByteArray(0, (dwBytes.getValue()-1)*2);
System.out.println("File-Description: \""
+ new String(description, StandardCharsets.UTF_16LE)+"\"");
}
}
最后,我收到我的德国的Windows 7 64位输出:
[exec:exec]
Version: 6.1.7601.17567
Found 1 translation(s) (length of cbTranslate: 4 bytes)
Language: German (0407); CodePage: Unicode (04B0);
File-Description: "Windows-Explorer"
HTH
@编辑:更新代码以使用结构的类表示来简化值的提取(处理字节确实需要它们交换接收到的字节的顺序 - big和littl e endian issue)
在一些尝试使用多种语言和代码页的应用程序之后发现它们的文件,我可以测试多个翻译的输出。
@ Edit2:重构代码并将其放在github上。正如在评论中提到,从github上回购了罗技僚机事件监视器运行代码的输出返回多个语言和代码页段:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXX Information contained in: C:\Program Files\Logitech\Gaming Software\LWEMon.exe
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
File: C:\Program Files\Logitech\Gaming Software\LWEMon.exe
Version: 5.10.127.0
Language: U.S. English
CodePage: Multilingual
Original-Filename: LWEMon.exe
Company-Name: Logitech Inc.
File-Description: Logitech WingMan Event Monitor
File-Version: 5.10.127
Product-Version: 5.10.127
Product-Name: Logitech Gaming Software
Internal-Name: LWEMon
Private-Build:
Special-Build:
Legal-Copyright: © 1999-2010 Logitech. All rights reserved.
Legal-Trademark: Logitech, the Logitech logo, and other Logitech marks are owned by Logitech and may be registered. All other trademarks are the property of their respective owners.
Comment: Created by the WingMan Team.
File: C:\Program Files\Logitech\Gaming Software\LWEMon.exe
Version: 5.10.127.0
Language: Japanese
CodePage: Multilingual
Original-Filename: LWEMon.exe
Company-Name: Logicool Co. Ltd.
File-Description: Logicool WingMan Event Monitor
File-Version: 5.10.127
Product-Version: 5.10.127
Product-Name: Logicool Gaming Software
Internal-Name: LWEMon
Private-Build:
Special-Build:
Legal-Copyright: © 1999-2010 Logicool Co. Ltd. All rights reserved.
Legal-Trademark: Logicool, the Logicool logo, and other Logicool marks are owned by Logicool and may be registered. All other trademarks are the property of their respective owners.
Comment: Created by the WingMan Team.
谢谢它真的为我工作。其实我要运行它作为一个java web开始阻止一些在远程操作系统上运行的下载服务。它适用于所有版本的Windows操作系统? –
WMIC的最低要求是在客户端Windows XP和服务器端Windows Server 2003([Source](http://msdn.microsoft.com/en-us/library/aa394531%28v=vs.85%29)。 aspx)) - 所以如果你需要支持Windows 3.1,95,ME或NT(尽管我怀疑它是今天常用的),你将需要其他解决方案。 –
谢谢。我正在尝试wmic.exe PROCESS WHERE(name =“dap.exe”)获取Processid,Caption,Command line,Description给出的说明与说明相同,即DAP.exe它的作用,t给出process/exe的实际描述。下载加速器Plus(DAP)的实际描述在哪里。 –