diff --git a/CHANGES.md b/CHANGES.md index 34e5431780..8707a27d63 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -24,6 +24,7 @@ Features * [#980](https://github.com/java-native-access/jna/issues/980): Added `PERF_OBJECT_TYPE`, `PERF_COUNTER_BLOCK`, and `PERF_COUNTER_DEFINITION` to `c.s.j.platform.win32.WinPerf` and added `Pointer` constructors to ``PERF_INSTANCE_DEFINITION` and `PERF_DATA_BLOCK` - [@dbwiddis](https://github.com/dbwiddis). * [#981](https://github.com/java-native-access/jna/issues/981): Added `WTS_PROCESS_INFO_EX`, `WTSEnumerateProcessesEx`, and `WTSFreeMemoryEx` to `c.s.j.platform.win32.Wtsapi32` - [@dbwiddis](https://github.com/dbwiddis). * [#983](https://github.com/java-native-access/jna/issues/983): Added `GetIfEntry`, `GetIfEntry2`, and `GetNetworkParams` and supporting structures `MIB_IFROW`, `MIB_IF_ROW2`, and `FIXED_INFO` to `c.s.j.platform.win32.IPHlpAPI.java` - [@dbwiddis](https://github.com/dbwiddis). +* [#988](https://github.com/java-native-access/jna/issues/988): Added `PdhLookupPerfIndexByEnglishName` to `c.s.j.platform.win32.PdhUtil` - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java index 7b8788d038..4143e41374 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/PdhUtil.java @@ -37,6 +37,12 @@ * @author widdis[at]gmail[dot]com */ public abstract class PdhUtil { + private static final int CHAR_TO_BYTES = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE; + + // This REG_MULTI_SZ value in HKLM provides English counters regardless of + // the current locale setting + private static final String ENGLISH_COUNTER_KEY = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"; + private static final String ENGLISH_COUNTER_VALUE = "Counter"; /** * Utility method to call Pdh's PdhLookupPerfNameByIndex that allocates the @@ -53,24 +59,58 @@ public abstract class PdhUtil { * @return Returns the name of the performance object or counter. */ public static String PdhLookupPerfNameByIndex(String szMachineName, int dwNameIndex) { - int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE; - // Call once to get required buffer size DWORDByReference pcchNameBufferSize = new DWORDByReference(new DWORD(0)); - Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, dwNameIndex, null, pcchNameBufferSize); + Pdh.INSTANCE.PdhLookupPerfNameByIndex(szMachineName, dwNameIndex, null, pcchNameBufferSize); + // Can't allocate 0 memory + if (pcchNameBufferSize.getValue().intValue() < 1) { + return ""; + } // Allocate buffer and call again - Memory mem = new Memory(pcchNameBufferSize.getValue().intValue() * charToBytes); - Pdh.INSTANCE.PdhLookupPerfNameByIndex(null, dwNameIndex, mem, pcchNameBufferSize); + Memory mem = new Memory(pcchNameBufferSize.getValue().intValue() * CHAR_TO_BYTES); + Pdh.INSTANCE.PdhLookupPerfNameByIndex(szMachineName, dwNameIndex, mem, pcchNameBufferSize); // Convert buffer to Java String - if (charToBytes == 1) { + if (CHAR_TO_BYTES == 1) { return mem.getString(0); } else { return mem.getWideString(0); } } + /** + * Utility method similar to Pdh's PdhLookupPerfIndexByName that returns the + * counter index corresponding to the specified counter name in English. + * Uses the registry on the local machine to find the index in the English + * locale, regardless of the current language setting on the machine. + * + * @param szNameBuffer + * The English name of the performance counter + * @return The counter's index if it exists, or 0 otherwise. + */ + public static int PdhLookupPerfIndexByEnglishName(String szNameBuffer) { + // Look up list of english names and ids + String[] counters = Advapi32Util.registryGetStringArray(WinReg.HKEY_LOCAL_MACHINE, ENGLISH_COUNTER_KEY, + ENGLISH_COUNTER_VALUE); + // Array contains alternating index/name pairs + // {"1", "1847", "2", "System", "4", "Memory", ... } + // Get position of name in the array (odd index), return parsed value of + // previous even index + for (int i = 1; i < counters.length; i += 2) { + if (counters[i].equals(szNameBuffer)) { + try { + return Integer.parseInt(counters[i - 1]); + } catch (NumberFormatException e) { + // Unexpected but handle anyway + return 0; + } + } + } + // Didn't find the String + return 0; + } + /** * Utility method to call Pdh's PdhEnumObjectItems that allocates the * required memory for the mszCounterList parameter based on the type @@ -99,7 +139,7 @@ public static String PdhLookupPerfNameByIndex(String szMachineName, int dwNameIn */ public static List PdhEnumObjectItemCounters(String szDataSource, String szMachineName, String szObjectName, int dwDetailLevel) { - int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE; + List counters = new ArrayList(); // Call once to get string lengths DWORDByReference pcchCounterListLength = new DWORDByReference(new DWORD(0)); @@ -107,18 +147,22 @@ public static List PdhEnumObjectItemCounters(String szDataSource, String Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength, null, pcchInstanceListLength, dwDetailLevel, 0); + // Can't allocate 0 memory if no counters + if (pcchCounterListLength.getValue().intValue() < 1) { + return counters; + } // Allocate memory and call again to populate strings - Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * charToBytes); - Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * charToBytes); + Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * CHAR_TO_BYTES); + // Don't need the instances + pcchInstanceListLength.getValue().setValue(0); Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList, - pcchCounterListLength, mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0); + pcchCounterListLength, null, pcchInstanceListLength, dwDetailLevel, 0); // Fetch counters - List counters = new ArrayList(); int offset = 0; while (offset < mszCounterList.size()) { String s = null; - if (charToBytes == 1) { + if (CHAR_TO_BYTES == 1) { s = mszCounterList.getString(offset); } else { s = mszCounterList.getWideString(offset); @@ -129,7 +173,7 @@ public static List PdhEnumObjectItemCounters(String szDataSource, String } counters.add(s); // Increment for string + null terminator - offset += (s.length() + 1) * charToBytes; + offset += (s.length() + 1) * CHAR_TO_BYTES; } return counters; @@ -163,7 +207,7 @@ public static List PdhEnumObjectItemCounters(String szDataSource, String */ public static List PdhEnumObjectItemInstances(String szDataSource, String szMachineName, String szObjectName, int dwDetailLevel) { - int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE; + List instances = new ArrayList(); // Call once to get string lengths DWORDByReference pcchCounterListLength = new DWORDByReference(new DWORD(0)); @@ -171,17 +215,22 @@ public static List PdhEnumObjectItemInstances(String szDataSource, Strin Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength, null, pcchInstanceListLength, dwDetailLevel, 0); + // Can't allocate 0 memory if no instances + if (pcchInstanceListLength.getValue().intValue() < 1) { + return instances; + } // Allocate memory and call again to populate strings - Memory mszCounterList = new Memory(pcchCounterListLength.getValue().intValue() * charToBytes); - Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * charToBytes); - Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, mszCounterList, - pcchCounterListLength, mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0); + Memory mszInstanceList = new Memory(pcchInstanceListLength.getValue().intValue() * CHAR_TO_BYTES); + // Don't need the counters + pcchCounterListLength.getValue().setValue(0); + Pdh.INSTANCE.PdhEnumObjectItems(szDataSource, szMachineName, szObjectName, null, pcchCounterListLength, + mszInstanceList, pcchInstanceListLength, dwDetailLevel, 0); - List instances = new ArrayList(); + // Fetch instances int offset = 0; while (offset < mszInstanceList.size()) { String s = null; - if (charToBytes == 1) { + if (CHAR_TO_BYTES == 1) { s = mszInstanceList.getString(offset); } else { s = mszInstanceList.getWideString(offset); @@ -192,7 +241,7 @@ public static List PdhEnumObjectItemInstances(String szDataSource, Strin } instances.add(s); // Increment for string + null terminator - offset += (s.length() + 1) * charToBytes; + offset += (s.length() + 1) * CHAR_TO_BYTES; } return instances; diff --git a/contrib/platform/test/com/sun/jna/platform/win32/PdhTest.java b/contrib/platform/test/com/sun/jna/platform/win32/PdhTest.java index 04013eb0af..a3462c7b47 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/PdhTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/PdhTest.java @@ -174,6 +174,9 @@ public void testLookupPerfIndex() { DWORDByReference pdwIndex = new DWORDByReference(); Pdh.INSTANCE.PdhLookupPerfIndexByName(null, testStr, pdwIndex); assertEquals(processorIndex, pdwIndex.getValue().intValue()); + + // Test English name to index + assertEquals(processorIndex, PdhUtil.PdhLookupPerfIndexByEnglishName(processorStr)); } @Test