com.sun.jna.platform.win32.Kernel32Util Maven / Gradle / Ivy
Show all versions of jna-platform Show documentation
/* Copyright (c) 2010, 2013 Daniel Doubrovkine, Markus Karg, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna.platform.win32;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.sun.jna.LastErrorException;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.WinNT.LOGICAL_PROCESSOR_RELATIONSHIP;
import com.sun.jna.platform.win32.WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.win32.W32APITypeMapper;
/**
* Kernel32 utility API.
*
* @author dblock[at]dblock.org
* @author markus[at]headcrashing[dot]eu
* @author Andreas "PAX" Lück, onkelpax-git[at]yahoo.de
*/
public abstract class Kernel32Util implements WinDef {
/**
* Get current computer NetBIOS name.
*
* @return Netbios name.
*/
public static String getComputerName() {
char buffer[] = new char[WinBase.MAX_COMPUTERNAME_LENGTH + 1];
IntByReference lpnSize = new IntByReference(buffer.length);
if (!Kernel32.INSTANCE.GetComputerName(buffer, lpnSize)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return Native.toString(buffer);
}
/**
* Invokes {@link Kernel32#LocalFree(Pointer)} and checks if it succeeded.
*
* @param ptr The {@link Pointer} to the memory to be released - ignored if NULL
* @throws Win32Exception if non-{@code ERROR_SUCCESS} code reported
*/
public static void freeLocalMemory(Pointer ptr) {
Pointer res = Kernel32.INSTANCE.LocalFree(ptr);
if (res != null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
/**
* Invokes {@link Kernel32#GlobalFree(Pointer)} and checks if it succeeded.
*
* @param ptr The {@link Pointer} to the memory to be released - ignored if NULL
* @throws Win32Exception if non-{@code ERROR_SUCCESS} code reported
*/
public static void freeGlobalMemory(Pointer ptr) {
Pointer res = Kernel32.INSTANCE.GlobalFree(ptr);
if (res != null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
/**
* Closes all referenced handles. If an exception is thrown for
* a specific handle, then it is accumulated until all
* handles have been closed. If more than one exception occurs,
* then it is added as a suppressed exception to the first one.
* Once closed all handles, the accumulated exception (if any) is thrown
*
* @param refs The references to close
* @see #closeHandleRef(WinNT.HANDLEByReference)
*/
public static void closeHandleRefs(HANDLEByReference... refs) {
Win32Exception err = null;
for (HANDLEByReference r : refs) {
try {
closeHandleRef(r);
} catch(Win32Exception e) {
if (err == null) {
err = e;
} else {
err.addSuppressedReflected(e);
}
}
}
if (err != null) {
throw err;
}
}
/**
* Closes the handle in the reference
*
* @param ref The handle reference - ignored if {@code null}
* @see #closeHandle(WinNT.HANDLE)
*/
public static void closeHandleRef(HANDLEByReference ref) {
closeHandle((ref == null) ? null : ref.getValue());
}
/**
* Invokes {@link #closeHandle(WinNT.HANDLE)} on each handle. If an exception
* is thrown for a specific handle, then it is accumulated until all
* handles have been closed. If more than one exception occurs, then it
* is added as a suppressed exception to the first one. Once closed all
* handles, the accumulated exception (if any) is thrown
*
* @param handles The handles to be closed
* @see Throwable#getSuppressed()
*/
public static void closeHandles(HANDLE... handles) {
Win32Exception err = null;
for (HANDLE h : handles) {
try {
closeHandle(h);
} catch(Win32Exception e) {
if (err == null) {
err = e;
} else {
err.addSuppressedReflected(e);
}
}
}
if (err != null) {
throw err;
}
}
/**
* Invokes {@link Kernel32#CloseHandle(WinNT.HANDLE)} and checks the success code.
* If not successful, then throws a {@link Win32Exception} with the
* {@code GetLastError} value
*
* @param h The handle to be closed - ignored if {@code null}
*/
public static void closeHandle(HANDLE h) {
if (h == null) {
return;
}
if (!Kernel32.INSTANCE.CloseHandle(h)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
/**
* Format a message from the value obtained from
* {@link Kernel32#GetLastError()} or {@link Native#getLastError()}.
*
* @param code The error code
* @return Formatted message.
*/
public static String formatMessage(int code) {
PointerByReference buffer = new PointerByReference();
int nLen = Kernel32.INSTANCE.FormatMessage(
WinBase.FORMAT_MESSAGE_ALLOCATE_BUFFER
| WinBase.FORMAT_MESSAGE_FROM_SYSTEM
| WinBase.FORMAT_MESSAGE_IGNORE_INSERTS,
null,
code,
0, // TODO: // MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT)
buffer, 0, null);
if (nLen == 0) {
throw new LastErrorException(Native.getLastError());
}
Pointer ptr = buffer.getValue();
try {
String s = ptr.getWideString(0);
return s.trim();
} finally {
freeLocalMemory(ptr);
}
}
/**
* Format a message from an HRESULT.
*
* @param code
* HRESULT
* @return Formatted message.
*/
public static String formatMessage(HRESULT code) {
return formatMessage(code.intValue());
}
/**
* Format a system message from an error code.
*
* @param code
* Error code, typically a result of GetLastError.
* @return Formatted message.
*/
public static String formatMessageFromLastErrorCode(int code) {
return formatMessage(W32Errors.HRESULT_FROM_WIN32(code));
}
/**
* @return Obtains the human-readable error message text from the last error
* that occurred by invocating {@code Kernel32.GetLastError()}.
*/
public static String getLastErrorMessage() {
return Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE
.GetLastError());
}
/**
* Return the path designated for temporary files.
*
* @return Path.
*/
public static String getTempPath() {
DWORD nBufferLength = new DWORD(WinDef.MAX_PATH);
char[] buffer = new char[nBufferLength.intValue()];
if (Kernel32.INSTANCE.GetTempPath(nBufferLength, buffer).intValue() == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return Native.toString(buffer);
}
public static void deleteFile(String filename) {
if (!Kernel32.INSTANCE.DeleteFile(filename)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
/**
* Returns valid drives in the system.
*
* @return A {@link List} of valid drives.
*/
public static List getLogicalDriveStrings() {
DWORD dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(new DWORD(0), null);
if (dwSize.intValue() <= 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
char buf[] = new char[dwSize.intValue()];
dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(dwSize, buf);
int bufSize = dwSize.intValue();
if (bufSize <= 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return Native.toStringList(buf, 0, bufSize);
}
/**
* Retrieves file system attributes for a specified file or directory.
*
* @param fileName
* The name of the file or directory.
* @return The attributes of the specified file or directory.
*/
public static int getFileAttributes(String fileName) {
int fileAttributes = Kernel32.INSTANCE.GetFileAttributes(fileName);
if (fileAttributes == WinBase.INVALID_FILE_ATTRIBUTES) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return fileAttributes;
}
/**
* Retrieves the result of GetFileType, provided the file exists.
* @param fileName file name
* @return file type
* @throws FileNotFoundException if file not found
*/
public static int getFileType(String fileName) throws FileNotFoundException {
File f = new File(fileName);
if (!f.exists()) {
throw new FileNotFoundException(fileName);
}
HANDLE hFile = null;
Win32Exception err = null;
try {
hFile = Kernel32.INSTANCE.CreateFile(fileName, WinNT.GENERIC_READ,
WinNT.FILE_SHARE_READ, new WinBase.SECURITY_ATTRIBUTES(),
WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL,
new HANDLEByReference().getValue());
if (WinBase.INVALID_HANDLE_VALUE.equals(hFile)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
int type = Kernel32.INSTANCE.GetFileType(hFile);
switch (type) {
case WinNT.FILE_TYPE_UNKNOWN:
int rc = Kernel32.INSTANCE.GetLastError();
switch (rc) {
case WinError.NO_ERROR:
break;
default:
throw new Win32Exception(rc);
}
default:
return type;
}
} catch(Win32Exception e) {
err = e;
throw err; // re-throw so finally block executed
} finally {
try {
closeHandle(hFile);
} catch(Win32Exception e) {
if (err == null) {
err = e;
} else {
err.addSuppressedReflected(e);
}
}
if (err != null) {
throw err;
}
}
}
/**
* @param rootName name of root drive
* @return One of the WinBase.DRIVE_* constants.
*/
public static int getDriveType(String rootName) {
return Kernel32.INSTANCE.GetDriveType(rootName);
}
/**
* Get the value of an environment variable.
*
* @param name
* Name of the environment variable.
* @return Value of an environment variable.
*/
public static String getEnvironmentVariable(String name) {
// obtain the buffer size
int size = Kernel32.INSTANCE.GetEnvironmentVariable(name, null, 0);
if (size == 0) {
return null;
} else if (size < 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
// obtain the value
char[] buffer = new char[size];
size = Kernel32.INSTANCE.GetEnvironmentVariable(name, buffer,
buffer.length);
if (size <= 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return Native.toString(buffer);
}
/**
* Uses the {@link Kernel32#GetEnvironmentStrings()} to retrieve and
* parse the current process environment
* @return The current process environment as a {@link Map}.
* @throws LastErrorException if failed to get or free the environment
* data block
* @see #getEnvironmentVariables(Pointer, long)
*/
public static Map getEnvironmentVariables() {
Pointer lpszEnvironmentBlock=Kernel32.INSTANCE.GetEnvironmentStrings();
if (lpszEnvironmentBlock == null) {
throw new LastErrorException(Kernel32.INSTANCE.GetLastError());
}
try {
return getEnvironmentVariables(lpszEnvironmentBlock, 0L);
} finally {
if (!Kernel32.INSTANCE.FreeEnvironmentStrings(lpszEnvironmentBlock)) {
throw new LastErrorException(Kernel32.INSTANCE.GetLastError());
}
}
}
/**
* @param lpszEnvironmentBlock The environment block as received from the
* GetEnvironmentStrings
* function
* @param offset Offset within the block to parse the data
* @return A {@link Map} of the parsed name=value
pairs.
* Note: if the environment block is {@code null} then {@code null}
* is returned instead of an empty map since we want to distinguish
* between the case that the data block is {@code null} and when there are
* no environment variables (as unlikely as it may be)
*/
public static Map getEnvironmentVariables(Pointer lpszEnvironmentBlock, long offset) {
if (lpszEnvironmentBlock == null) {
return null;
}
Map vars=new TreeMap();
boolean asWideChars=isWideCharEnvironmentStringBlock(lpszEnvironmentBlock, offset);
long stepFactor=asWideChars ? 2L : 1L;
for (long curOffset=offset; ; ) {
String nvp=readEnvironmentStringBlockEntry(lpszEnvironmentBlock, curOffset, asWideChars);
int len=nvp.length();
if (len == 0) { // found the ending '\0'
break;
}
int pos=nvp.indexOf('=');
if (pos < 0) {
throw new IllegalArgumentException("Missing variable value separator in " + nvp);
}
String name=nvp.substring(0, pos), value=nvp.substring(pos + 1);
vars.put(name, value);
curOffset += (len + 1 /* skip the ending '\0' */) * stepFactor;
}
return vars;
}
/**
* @param lpszEnvironmentBlock The environment block as received from the
* GetEnvironmentStrings
* function
* @param offset Offset within the block to look for the entry
* @param asWideChars If {@code true} then the block contains {@code wchar_t}
* instead of "plain old" {@code char}s
* @return A {@link String} containing the name=value
pair or
* empty if reached end of block
* @see #isWideCharEnvironmentStringBlock
* @see #findEnvironmentStringBlockEntryEnd
*/
public static String readEnvironmentStringBlockEntry(Pointer lpszEnvironmentBlock, long offset, boolean asWideChars) {
long endOffset=findEnvironmentStringBlockEntryEnd(lpszEnvironmentBlock, offset, asWideChars);
int dataLen=(int) (endOffset - offset);
if (dataLen == 0) {
return "";
}
int charsLen=asWideChars ? (dataLen / 2) : dataLen;
char[] chars=new char[charsLen];
long curOffset=offset, stepSize=asWideChars ? 2L : 1L;
ByteOrder byteOrder=ByteOrder.nativeOrder();
for (int index=0; index < chars.length; index++, curOffset += stepSize) {
byte b=lpszEnvironmentBlock.getByte(curOffset);
if (asWideChars) {
byte x=lpszEnvironmentBlock.getByte(curOffset + 1L);
if (ByteOrder.LITTLE_ENDIAN.equals(byteOrder)) {
chars[index] = (char) (((x << Byte.SIZE) & 0xFF00) | (b & 0x00FF));
} else { // unlikely, but handle it
chars[index] = (char) (((b << Byte.SIZE) & 0xFF00) | (x & 0x00FF));
}
} else {
chars[index] = (char) (b & 0x00FF);
}
}
return new String(chars);
}
/**
* @param lpszEnvironmentBlock The environment block as received from the
* GetEnvironmentStrings
* function
* @param offset Offset within the block to look for the entry
* @param asWideChars If {@code true} then the block contains {@code wchar_t}
* instead of "plain old" {@code char}s
* @return The offset of the first {@code '\0'} in the data block
* starting at the specified offset - can be the start offset itself if empty
* string.
* @see #isWideCharEnvironmentStringBlock
*/
public static long findEnvironmentStringBlockEntryEnd(Pointer lpszEnvironmentBlock, long offset, boolean asWideChars) {
for (long curOffset=offset, stepSize=asWideChars ? 2L : 1L; ; curOffset += stepSize) {
byte b=lpszEnvironmentBlock.getByte(curOffset);
if (b == 0) {
return curOffset;
}
}
}
/**
* Attempts to determine whether the data block uses {@code wchar_t}
* instead of "plain old" {@code char}s. It does that by reading
* 2 bytes from the specified offset - the character value and its charset
* indicator - and examining them as follows:
*
* -
* If the charset indicator is non-zero then it is assumed to be
* a "plain old" {@code char}s data block. Note:
* the assumption is that the environment variable name (at
* least) is ASCII.
*
*
* -
* Otherwise (i.e., zero charset indicator), it is assumed to be
* a {@code wchar_t}
*
*
* Note: the code takes into account the {@link ByteOrder} even though
* only {@link ByteOrder#LITTLE_ENDIAN} is the likely one
* @param lpszEnvironmentBlock The environment block as received from the
* GetEnvironmentStrings
* function
* @param offset offset
* @return {@code true} if the block contains {@code wchar_t} instead of
* "plain old" {@code char}s
*/
public static boolean isWideCharEnvironmentStringBlock(Pointer lpszEnvironmentBlock, long offset) {
byte b0=lpszEnvironmentBlock.getByte(offset);
byte b1=lpszEnvironmentBlock.getByte(offset + 1L);
ByteOrder byteOrder=ByteOrder.nativeOrder();
if (ByteOrder.LITTLE_ENDIAN.equals(byteOrder)) {
return isWideCharEnvironmentStringBlock(b1);
} else {
return isWideCharEnvironmentStringBlock(b0);
}
}
private static boolean isWideCharEnvironmentStringBlock(byte charsetIndicator) {
// assume wchar_t for environment variables represents ASCII letters
if (charsetIndicator != 0) {
return false;
} else {
return true;
}
}
/**
* Retrieves an integer associated with a key in the specified section of an
* initialization file.
*
* @param appName
* The name of the section in the initialization file.
* @param keyName
* The name of the key whose value is to be retrieved. This value
* is in the form of a string; the
* {@link Kernel32#GetPrivateProfileInt} function converts the
* string into an integer and returns the integer.
* @param defaultValue
* The default value to return if the key name cannot be found in
* the initialization file.
* @param fileName
* The name of the initialization file. If this parameter does
* not contain a full path to the file, the system searches for
* the file in the Windows directory.
* @return The retrieved integer, or the default if not found.
*/
public static final int getPrivateProfileInt(final String appName,
final String keyName, final int defaultValue, final String fileName) {
return Kernel32.INSTANCE.GetPrivateProfileInt(appName, keyName,
defaultValue, fileName);
}
/**
* Retrieves a string from the specified section in an initialization file.
*
* @param lpAppName
* The name of the section containing the key name. If this
* parameter is {@code null}, the
* {@link Kernel32#GetPrivateProfileString} function copies all
* section names in the file to the supplied buffer.
* @param lpKeyName
* The name of the key whose associated string is to be
* retrieved. If this parameter is {@code null}, all key names in
* the section specified by the {@code lpAppName} parameter are
* returned.
* @param lpDefault
* A default string. If the {@code lpKeyName} key cannot be found
* in the initialization file,
* {@link Kernel32#GetPrivateProfileString} returns the default.
* If this parameter is {@code null}, the default is an empty
* string, {@code ""}.
*
* Avoid specifying a default string with trailing blank
* characters. The function inserts a {@code null} character in
* the {@code lpReturnedString} buffer to strip any trailing
* blanks.
*
* @param lpFileName
* The name of the initialization file. If this parameter does
* not contain a full path to the file, the system searches for
* the file in the Windows directory.
* @return
* If neither {@code lpAppName} nor {@code lpKeyName} is
* {@code null} and the destination buffer is too small to hold the
* requested string, the string is truncated.
*
*
* If either {@code lpAppName} or {@code lpKeyName} is {@code null}
* and the destination buffer is too small to hold all the strings,
* the last string is truncated and followed by two {@code null}
* characters.
*
*
* In the event the initialization file specified by
* {@code lpFileName} is not found, or contains invalid values, this
* function will set errorno with a value of '0x2' (File Not Found).
* To retrieve extended error information, call
* {@link Kernel32#GetLastError}.
*
*/
public static final String getPrivateProfileString(final String lpAppName,
final String lpKeyName, final String lpDefault,
final String lpFileName) {
final char buffer[] = new char[1024];
Kernel32.INSTANCE.GetPrivateProfileString(lpAppName, lpKeyName,
lpDefault, buffer, new DWORD(buffer.length), lpFileName);
return Native.toString(buffer);
}
public static final void writePrivateProfileString(final String appName,
final String keyName, final String string, final String fileName) {
if (!Kernel32.INSTANCE.WritePrivateProfileString(appName, keyName,
string, fileName))
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
/**
* Convenience method to get the processor information. Takes care of
* auto-growing the array.
*
* @return the array of processor information.
*/
public static final WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] getLogicalProcessorInformation() {
int sizePerStruct = new WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION()
.size();
WinDef.DWORDByReference bufferSize = new WinDef.DWORDByReference(
new WinDef.DWORD(sizePerStruct));
Memory memory;
while (true) {
memory = new Memory(bufferSize.getValue().intValue());
if (!Kernel32.INSTANCE.GetLogicalProcessorInformation(memory,
bufferSize)) {
int err = Kernel32.INSTANCE.GetLastError();
if (err != WinError.ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(err);
} else {
break;
}
}
WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION firstInformation = new WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION(
memory);
int returnedStructCount = bufferSize.getValue().intValue()
/ sizePerStruct;
return (WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[]) firstInformation
.toArray(new WinNT.SYSTEM_LOGICAL_PROCESSOR_INFORMATION[returnedStructCount]);
}
/**
* Convenience method to get the processor information. Takes care of
* auto-growing the array and populating variable-length arrays in
* structures.
*
* @param relationshipType
* The type of relationship to retrieve. This parameter can be
* one of the following values:
* {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationCache},
* {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationGroup},
* {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationNumaNode},
* {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorCore},
* {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationProcessorPackage},
* or {@link LOGICAL_PROCESSOR_RELATIONSHIP#RelationAll}
* @return the array of processor information.
*/
public static final SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[] getLogicalProcessorInformationEx(
int relationshipType) {
WinDef.DWORDByReference bufferSize = new WinDef.DWORDByReference(new WinDef.DWORD(1));
Memory memory;
while (true) {
memory = new Memory(bufferSize.getValue().intValue());
if (!Kernel32.INSTANCE.GetLogicalProcessorInformationEx(relationshipType, memory, bufferSize)) {
int err = Kernel32.INSTANCE.GetLastError();
if (err != WinError.ERROR_INSUFFICIENT_BUFFER)
throw new Win32Exception(err);
} else {
break;
}
}
// Array elements have variable size; iterate to populate array
List procInfoList = new ArrayList();
int offset = 0;
while (offset < bufferSize.getValue().intValue()) {
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX information = SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX
.fromPointer(memory.share(offset));
procInfoList.add(information);
offset += information.size;
}
return procInfoList.toArray(new SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX[0]);
}
/**
* Retrieves all the keys and values for the specified section of an initialization file.
*
*
* Each string has the following format: {@code key=string}.
*
*
* This operation is atomic; no updates to the specified initialization file are allowed while this method is executed.
*
*
* @param appName
* The name of the section in the initialization file.
* @param fileName
* The name of the initialization file. If this parameter does not contain a full path to the file, the system searches for the file in the
* Windows directory.
* @return The key name and value pairs associated with the named section.
*/
public static final String[] getPrivateProfileSection(final String appName, final String fileName) {
final char buffer[] = new char[32768]; // Maximum section size according to MSDN (http://msdn.microsoft.com/en-us/library/windows/desktop/ms724348(v=vs.85).aspx)
if (Kernel32.INSTANCE.GetPrivateProfileSection(appName, buffer, new DWORD(buffer.length), fileName).intValue() == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new String(buffer).split("\0");
}
/**
* Retrieves the names of all sections in an initialization file.
*
* This operation is atomic; no updates to the initialization file are allowed while this method is executed.
*
*
* @param fileName
* The name of the initialization file. If this parameter is {@code NULL}, the function searches the Win.ini file. If this parameter does not
* contain a full path to the file, the system searches for the file in the Windows directory.
* @return the section names associated with the named file.
*/
public static final String[] getPrivateProfileSectionNames(final String fileName) {
final char buffer[] = new char[65536]; // Maximum INI file size according to MSDN (http://support.microsoft.com/kb/78346)
if (Kernel32.INSTANCE.GetPrivateProfileSectionNames(buffer, new DWORD(buffer.length), fileName).intValue() == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new String(buffer).split("\0");
}
/**
* @param appName
* The name of the section in which data is written. This section name is typically the name of the calling application.
* @param strings
* The new key names and associated values that are to be written to the named section. Each entry must be of the form {@code key=value}.
* @param fileName
* The name of the initialization file. If this parameter does not contain a full path for the file, the function searches the Windows directory
* for the file. If the file does not exist and lpFileName does not contain a full path, the function creates the file in the Windows directory.
*/
public static final void writePrivateProfileSection(final String appName, final String[] strings, final String fileName) {
final StringBuilder buffer = new StringBuilder();
for (final String string : strings)
buffer.append(string).append('\0');
buffer.append('\0');
if (! Kernel32.INSTANCE.WritePrivateProfileSection(appName, buffer.toString(), fileName)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
/**
* Invokes the {@link Kernel32#QueryDosDevice(String, char[], int)} method
* and parses the result
* @param lpszDeviceName The device name
* @param maxTargetSize The work buffer size to use for the query
* @return The parsed result
*/
public static final List queryDosDevice(String lpszDeviceName, int maxTargetSize) {
char[] lpTargetPath = new char[maxTargetSize];
int dwSize = Kernel32.INSTANCE.QueryDosDevice(lpszDeviceName, lpTargetPath, lpTargetPath.length);
if (dwSize == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return Native.toStringList(lpTargetPath, 0, dwSize);
}
/**
* Invokes and parses the result of {@link Kernel32#GetVolumePathNamesForVolumeName(String, char[], int, IntByReference)}
* @param lpszVolumeName The volume name
* @return The parsed result
* @throws Win32Exception If failed to retrieve the required information
*/
public static final List getVolumePathNamesForVolumeName(String lpszVolumeName) {
char[] lpszVolumePathNames = new char[WinDef.MAX_PATH + 1];
IntByReference lpcchReturnLength = new IntByReference();
if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
int hr = Kernel32.INSTANCE.GetLastError();
if (hr != WinError.ERROR_MORE_DATA) {
throw new Win32Exception(hr);
}
int required = lpcchReturnLength.getValue();
lpszVolumePathNames = new char[required];
// this time we MUST succeed
if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
int bufSize = lpcchReturnLength.getValue();
return Native.toStringList(lpszVolumePathNames, 0, bufSize);
}
// prefix and suffix of a volume GUID path
public static final String VOLUME_GUID_PATH_PREFIX = "\\\\?\\Volume{";
public static final String VOLUME_GUID_PATH_SUFFIX = "}\\";
/**
* Parses and returns the pure GUID value of a volume name obtained
* from {@link Kernel32#FindFirstVolume(char[], int)} or
* {@link Kernel32#FindNextVolume} calls
*
* @param volumeGUIDPath
* The volume GUID path as returned by one of the above mentioned calls
* @return The pure GUID value after stripping the "\\?\" prefix and
* removing the trailing backslash.
* @throws IllegalArgumentException if bad format encountered
* @see Naming a Volume
*/
public static final String extractVolumeGUID(String volumeGUIDPath) {
if ((volumeGUIDPath == null)
|| (volumeGUIDPath.length() <= (VOLUME_GUID_PATH_PREFIX.length() + VOLUME_GUID_PATH_SUFFIX.length()))
|| (!volumeGUIDPath.startsWith(VOLUME_GUID_PATH_PREFIX))
|| (!volumeGUIDPath.endsWith(VOLUME_GUID_PATH_SUFFIX))) {
throw new IllegalArgumentException("Bad volume GUID path format: " + volumeGUIDPath);
}
return volumeGUIDPath.substring(VOLUME_GUID_PATH_PREFIX.length(), volumeGUIDPath.length() - VOLUME_GUID_PATH_SUFFIX.length());
}
/**
* This function retrieves the full path of the executable file of a given process identifier.
*
* @param pid
* Identifier for the running process
* @param dwFlags
* 0 - The name should use the Win32 path format.
* 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format.
*
* @return the full path of the process's executable file of null if failed. To get extended error information,
* call GetLastError.
*/
public static final String QueryFullProcessImageName(int pid, int dwFlags) {
HANDLE hProcess = null;
Win32Exception we = null;
try {
hProcess = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION | WinNT.PROCESS_VM_READ, false, pid);
if (hProcess == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return QueryFullProcessImageName(hProcess, dwFlags);
} catch (Win32Exception e) {
we = e;
throw we; // re-throw to invoke finally block
} finally {
try {
closeHandle(hProcess);
} catch (Win32Exception e) {
if (we == null) {
we = e;
} else {
we.addSuppressed(e);
}
}
if (we != null) {
throw we;
}
}
}
/**
*
* This function retrieves the full path of the executable file of a given process.
*
* @param hProcess
* Handle for the running process
* @param dwFlags
* 0 - The name should use the Win32 path format.
* 1(WinNT.PROCESS_NAME_NATIVE) - The name should use the native system path format.
*
* @return the full path of the process's executable file of null if failed. To get extended error information,
* call GetLastError.
*/
public static final String QueryFullProcessImageName(HANDLE hProcess, int dwFlags) {
int size = WinDef.MAX_PATH; // Start with MAX_PATH, then increment with 1024 each iteration
IntByReference lpdwSize = new IntByReference();
do {
char[] lpExeName = new char[size];
lpdwSize.setValue(size);
if (Kernel32.INSTANCE.QueryFullProcessImageName(hProcess, dwFlags, lpExeName, lpdwSize)) {
return new String(lpExeName, 0, lpdwSize.getValue());
}
size += 1024;
} while (Kernel32.INSTANCE.GetLastError() == Kernel32.ERROR_INSUFFICIENT_BUFFER);
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
/**
* Gets the specified resource out of the specified executable file
*
* @param path
* The path to the executable file
* @param type
* The type of the resource (either a type name or type ID is
* allowed)
* @param name
* The name or ID of the resource
* @return The resource bytes, or null if no such resource exists.
* @throws IllegalStateException if the call to LockResource fails
*/
public static byte[] getResource(String path, String type, String name) {
HMODULE target = Kernel32.INSTANCE.LoadLibraryEx(path, null, Kernel32.LOAD_LIBRARY_AS_DATAFILE);
if (target == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
Win32Exception err = null;
Pointer start = null;
int length = 0;
byte[] results = null;
try {
Pointer t = null;
try {
t = new Pointer(Long.parseLong(type));
} catch (NumberFormatException e) {
t = new Memory(Native.WCHAR_SIZE * (type.length() + 1));
t.setWideString(0, type);
}
Pointer n = null;
try {
n = new Pointer(Long.parseLong(name));
} catch (NumberFormatException e) {
n = new Memory(Native.WCHAR_SIZE * (name.length() + 1));
n.setWideString(0, name);
}
HRSRC hrsrc = Kernel32.INSTANCE.FindResource(target, n, t);
if (hrsrc == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
// according to MSDN, on 32 bit Windows or newer, calling FreeResource() is not necessary - and in fact does nothing but return false.
HANDLE loaded = Kernel32.INSTANCE.LoadResource(target, hrsrc);
if (loaded == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
length = Kernel32.INSTANCE.SizeofResource(target, hrsrc);
if (length == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
// MSDN: It is not necessary to unlock resources because the system automatically deletes them when the process that created them terminates.
// MSDN does not say that LockResource sets GetLastError
start = Kernel32.INSTANCE.LockResource(loaded);
if (start == null) {
throw new IllegalStateException("LockResource returned null.");
}
// have to capture it into a byte array before you free the library, otherwise bad things happen.
results = start.getByteArray(0, length);
} catch (Win32Exception we) {
err = we;
} finally {
// from what I can tell on MSDN, the only thing that needs cleanup on this is the HMODULE from LoadLibrary
if (target != null) {
if (!Kernel32.INSTANCE.FreeLibrary(target)) {
Win32Exception we = new Win32Exception(Kernel32.INSTANCE.GetLastError());
if (err != null) {
we.addSuppressedReflected(err);
}
throw we;
}
}
}
if (err != null) {
throw err;
}
return results;
}
/**
* Gets a list of all resources from the specified executable file
*
* @param path
* The path to the executable file
* @return A map of resource type name/ID => resources.
* A map key + a single list item + the path to the executable can
* be handed off to getResource() to actually get the resource.
*/
public static Map> getResourceNames(String path) {
HMODULE target = Kernel32.INSTANCE.LoadLibraryEx(path, null, Kernel32.LOAD_LIBRARY_AS_DATAFILE);
if (target == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
final List types = new ArrayList();
final Map> result = new LinkedHashMap>();
WinBase.EnumResTypeProc ertp = new WinBase.EnumResTypeProc() {
@Override
public boolean invoke(HMODULE module, Pointer type, Pointer lParam) {
// simulate IS_INTRESOURCE macro defined in WinUser.h
// basically that means that if "type" is less than or equal to 65,535
// it assumes it's an ID.
// otherwise it assumes it's a pointer to a string
if (Pointer.nativeValue(type) <= 65535) {
types.add(Pointer.nativeValue(type) + "");
} else {
types.add(type.getWideString(0));
}
return true;
}
};
WinBase.EnumResNameProc ernp = new WinBase.EnumResNameProc() {
@Override
public boolean invoke(HMODULE module, Pointer type, Pointer name, Pointer lParam) {
String typeName = "";
if (Pointer.nativeValue(type) <= 65535) {
typeName = Pointer.nativeValue(type) + "";
} else {
typeName = type.getWideString(0);
}
if (Pointer.nativeValue(name) < 65535) {
result.get(typeName).add(Pointer.nativeValue(name) + "");
} else {
result.get(typeName).add(name.getWideString(0));
}
return true;
}
};
Win32Exception err = null;
try {
if (!Kernel32.INSTANCE.EnumResourceTypes(target, ertp, null)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
for (final String typeName : types) {
result.put(typeName, new ArrayList());
// simulate MAKEINTRESOURCE macro in WinUser.h
// basically, if the value passed in can be parsed as a number then convert it into one and run with that.
// otherwise, assume it's a string and construct a pointer to said string.
Pointer pointer = null;
try {
pointer = new Pointer(Long.parseLong(typeName));
} catch (NumberFormatException e) {
pointer = new Memory(Native.WCHAR_SIZE * (typeName.length() + 1));
pointer.setWideString(0, typeName);
}
boolean callResult = Kernel32.INSTANCE.EnumResourceNames(target, pointer, ernp, null);
if (!callResult) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
}
} catch (Win32Exception e) {
err = e;
} finally {
// from what I can tell on MSDN, the only thing that needs cleanup
// on this is the HMODULE from LoadLibrary
if (target != null) {
if (!Kernel32.INSTANCE.FreeLibrary(target)) {
Win32Exception we = new Win32Exception(Kernel32.INSTANCE.GetLastError());
if (err != null) {
we.addSuppressedReflected(err);
}
throw we;
}
}
}
if (err != null) {
throw err;
}
return result;
}
/**
* Returns all the executable modules for a given process ID.
*
* @param processID
* The process ID to get executable modules for
* @return All the modules in the process.
*/
public static List getModules(int processID) {
HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(processID));
if (snapshot == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
Win32Exception we = null;
try {
Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W();
if (!Kernel32.INSTANCE.Module32FirstW(snapshot, first)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
List modules = new ArrayList();
modules.add(first);
Tlhelp32.MODULEENTRY32W next = new Tlhelp32.MODULEENTRY32W();
while (Kernel32.INSTANCE.Module32NextW(snapshot, next)) {
modules.add(next);
next = new Tlhelp32.MODULEENTRY32W();
}
int lastError = Kernel32.INSTANCE.GetLastError();
// if we got a false from Module32Next,
// check to see if it returned false because we're genuinely done
// or if something went wrong.
if (lastError != W32Errors.ERROR_SUCCESS && lastError != W32Errors.ERROR_NO_MORE_FILES) {
throw new Win32Exception(lastError);
}
return modules;
} catch (Win32Exception e) {
we = e;
throw we; // re-throw so finally block is executed
} finally {
try {
closeHandle(snapshot);
} catch(Win32Exception e) {
if (we == null) {
we = e;
} else {
we.addSuppressedReflected(e);
}
}
if (we != null) {
throw we;
}
}
}
/**
* Expands environment-variable strings and replaces them with the values
* defined for the current user.
*
* @param input A string that contains one or more environment-variable
* strings in the form: %variableName%. For each such
* reference, the %variableName% portion is replaced with the
* current value of that environment variable.
*
* Case is ignored when looking up the environment-variable
* name. If the name is not found, the %variableName% portion
* is left unexpanded.
*
* Note that this function does not support all the features
* that Cmd.exe supports. For example, it does not support
* %variableName:str1=str2% or %variableName:~offset,length%.
*
* @return the replaced string
* @throws Win32Exception if an error occurs
*/
public static String expandEnvironmentStrings(String input) {
if(input == null) {
return "";
}
int resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, null, 0);
if(resultChars == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
Memory resultMemory;
if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {
resultMemory = new Memory(resultChars * Native.WCHAR_SIZE);
} else {
// return value is length in chars including terminating NULL,
// documentation for ANSI version says: buffer size should be the
// string length, plus terminating null character, plus one
resultMemory = new Memory(resultChars + 1);
}
resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, resultMemory, resultChars);
if(resultChars == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {
return resultMemory.getWideString(0);
} else {
return resultMemory.getString(0);
}
}
}