diff --git a/CHANGES.md b/CHANGES.md index 8707a27d63..c9d585b4af 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). +* [#984](https://github.com/java-native-access/jna/issues/984): Added `CM_Locate_DevNode`, `CM_Get_Parent`, `CM_Get_Child`, `CM_Get_Sibling`, `CM_Get_Device_ID`, and `CM_Get_Device_ID_Size` to `c.s.j.platform.win32.Cfgmgr32.java` and a `c.s.j.platform.win32.Cfgmgr32Util` class for `CM_Get_Device_ID` - [@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/Cfgmgr32.java b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java new file mode 100644 index 0000000000..b857c6667b --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java @@ -0,0 +1,192 @@ +/* Copyright (c) 2018 Daniel Widdis, 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 com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.W32APIOptions; + +/** + * Windows Cfgmgr32. + * + * @author widdis[at]gmail[dot]com + */ +public interface Cfgmgr32 extends Library { + Cfgmgr32 INSTANCE = Native.load("Cfgmgr32", Cfgmgr32.class, W32APIOptions.DEFAULT_OPTIONS); + + public final static int CR_SUCCESS = 0; + public final static int CR_BUFFER_SMALL = 0x0000001A; + + public final static int CM_LOCATE_DEVNODE_NORMAL = 0; + public final static int CM_LOCATE_DEVNODE_PHANTOM = 1; + public final static int CM_LOCATE_DEVNODE_CANCELREMOVE = 2; + public final static int CM_LOCATE_DEVNODE_NOVALIDATION = 4; + public final static int CM_LOCATE_DEVNODE_BITS = 7; + + /** + * The CM_Locate_DevNode function obtains a device instance handle to the + * device node that is associated with a specified device instance ID on the + * local machine. + * + * @param pdnDevInst + * A pointer to a device instance handle that CM_Locate_DevNode + * retrieves. The retrieved handle is bound to the local machine. + * @param pDeviceID + * A pointer to a NULL-terminated string representing a device + * instance ID. If this value is NULL, or if it points to a + * zero-length string, the function retrieves a device instance + * handle to the device at the root of the device tree. * + * @param ulFlags + * A variable of ULONG type that supplies one of the following + * flag values that apply if the caller supplies a device + * instance identifier: CM_LOCATE_DEVNODE_NORMAL, + * CM_LOCATE_DEVNODE_PHANTOM, CM_LOCATE_DEVNODE_CANCELREMOVE, or + * CM_LOCATE_DEVNODE_NOVALIDATION + * @return If the operation succeeds, CM_Locate_DevNode returns CR_SUCCESS. + * Otherwise, the function returns one of the CR_Xxx error codes + * that are defined in Cfgmgr32.h. + * @see + * CM_Locate_DevNode + */ + int CM_Locate_DevNode(IntByReference pdnDevInst, String pDeviceID, int ulFlags); + + /** + * The CM_Get_Parent function obtains a device instance handle to the parent + * node of a specified device node (devnode) in the local machine's device + * tree. + * + * @param pdnDevInst + * Caller-supplied pointer to the device instance handle to the + * parent node that this function retrieves. The retrieved handle + * is bound to the local machine. + * @param dnDevInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @param ulFlags + * Not used, must be zero. + * @return If the operation succeeds, the function returns CR_SUCCESS. + * Otherwise, it returns one of the CR_-prefixed error codes defined + * in Cfgmgr32.h. + * @see + * CM_Get_Parent + */ + int CM_Get_Parent(IntByReference pdnDevInst, int dnDevInst, int ulFlags); + + /** + * The CM_Get_Child function is used to retrieve a device instance handle to + * the first child node of a specified device node (devnode) in the local + * machine's device tree. + * + * @param pdnDevInst + * Caller-supplied pointer to the device instance handle to the + * child node that this function retrieves. The retrieved handle + * is bound to the local machine. + * @param dnDevInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @param ulFlags + * Not used, must be zero. + * @return If the operation succeeds, the function returns CR_SUCCESS. + * Otherwise, it returns one of the CR_-prefixed error codes defined + * in Cfgmgr32.h. + * @see + * CM_Get_Child + */ + int CM_Get_Child(IntByReference pdnDevInst, int dnDevInst, int ulFlags); + + /** + * The CM_Get_Sibling function obtains a device instance handle to the next + * sibling node of a specified device node (devnode) in the local machine's + * device tree. + * + * @param pdnDevInst + * Caller-supplied pointer to the device instance handle to the + * sibling node that this function retrieves. The retrieved + * handle is bound to the local machine. + * @param dnDevInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @param ulFlags + * Not used, must be zero. + * @return If the operation succeeds, the function returns CR_SUCCESS. + * Otherwise, it returns one of the CR_-prefixed error codes defined + * in Cfgmgr32.h. + * @see + * CM_Get_Sibling + */ + int CM_Get_Sibling(IntByReference pdnDevInst, int dnDevInst, int ulFlags); + + /** + * The CM_Get_Device_ID function retrieves the device instance ID for a + * specified device instance on the local machine. + * + * @param devInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @param Buffer + * Address of a buffer to receive a device instance ID string. + * The required buffer size can be obtained by calling + * CM_Get_Device_ID_Size, then incrementing the received value to + * allow room for the string's terminating NULL. + * @param BufferLen + * Caller-supplied length, in characters, of the buffer specified + * by Buffer. + * @param ulFlags + * Not used, must be zero. + * @return If the operation succeeds, the function returns CR_SUCCESS. + * Otherwise, it returns one of the CR_-prefixed error codes defined + * in Cfgmgr32.h. + * @see + * CM_Get_Device_ID + */ + int CM_Get_Device_ID(int devInst, Pointer Buffer, int BufferLen, int ulFlags); + + /** + * The CM_Get_Device_ID_Size function retrieves the buffer size required to + * hold a device instance ID for a device instance on the local machine. + * + * @param pulLen + * Receives a value representing the required buffer size, in + * characters. + * @param dnDevInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @param ulFlags + * Not used, must be zero. + * @return If the operation succeeds, the function returns CR_SUCCESS. + * Otherwise, it returns one of the CR_-prefixed error codes defined + * in Cfgmgr32.h. + * @see + * CM_Get_Device_ID_Size + */ + int CM_Get_Device_ID_Size(IntByReference pulLen, int dnDevInst, int ulFlags); +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java new file mode 100644 index 0000000000..935cbf8761 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java @@ -0,0 +1,99 @@ +/* Copyright (c) 2018 Daniel Widdis, 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 com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.ptr.IntByReference; + +/** + * Cfgmgr32 utility API. + * + * @author widdis[at]gmail[dot]com + */ +public abstract class Cfgmgr32Util { + @SuppressWarnings("serial") + public static class Cfgmgr32Exception extends RuntimeException { + private final int errorCode; + + public Cfgmgr32Exception(int errorCode) { + this.errorCode = errorCode; + } + + public int getErrorCode() { + return errorCode; + } + } + + /** + * Utility method to call Cfgmgr32's CM_Get_Device_ID that allocates the + * required memory for the Buffer parameter based on the type mapping used, + * calls to CM_Get_Device_ID, and returns the received string. + * + * @param devInst + * Caller-supplied device instance handle that is bound to the + * local machine. + * @return The device instance ID string. + * @throws Cfgmgr32Exception + */ + public static String CM_Get_Device_ID(int devInst) throws Cfgmgr32Exception { + int charToBytes = Boolean.getBoolean("w32.ascii") ? 1 : Native.WCHAR_SIZE; + + // Get Device ID character count + IntByReference pulLen = new IntByReference(); + int ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID_Size(pulLen, devInst, 0); + if (ret != Cfgmgr32.CR_SUCCESS) { + throw new Cfgmgr32Exception(ret); + } + + // Add one to length to allow null terminator + Memory buffer = new Memory((pulLen.getValue() + 1) * charToBytes); + // Zero the buffer (including the extra character) + buffer.clear(); + // Fetch the buffer specifying only the current length + ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID(devInst, buffer, pulLen.getValue(), 0); + // In the unlikely event the device id changes this might not be big + // enough, try again. This happens rarely enough one retry should be + // sufficient. + if (ret == Cfgmgr32.CR_BUFFER_SMALL) { + ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID_Size(pulLen, devInst, 0); + if (ret != Cfgmgr32.CR_SUCCESS) { + throw new Cfgmgr32Exception(ret); + } + buffer = new Memory((pulLen.getValue() + 1) * charToBytes); + buffer.clear(); + ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID(devInst, buffer, pulLen.getValue(), 0); + } + // If we still aren't successful throw an exception + if (ret != Cfgmgr32.CR_SUCCESS) { + throw new Cfgmgr32Exception(ret); + } + // Convert buffer to Java String + if (charToBytes == 1) { + return buffer.getString(0); + } else { + return buffer.getWideString(0); + } + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java new file mode 100644 index 0000000000..885bd4b87e --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java @@ -0,0 +1,90 @@ +/* Copyright (c) 2018 Daniel Widdis, 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; + +import org.junit.Test; + +import com.sun.jna.ptr.IntByReference; + +/** + * Tests methods in Cfgmgr32 + * + * @author widdis[at]gmail[dot]com + */ +public class Cfgmgr32Test { + /** + * Tests CM_Locate_DevNode, CM_Get_Parent, CM_Get_Child, CM_Get_Sibling + */ + @Test + public void testDevNode() { + // Fetch the root node + IntByReference outputNode = new IntByReference(); + assertEquals(Cfgmgr32.CR_SUCCESS, + Cfgmgr32.INSTANCE.CM_Locate_DevNode(outputNode, null, Cfgmgr32.CM_LOCATE_DEVNODE_NORMAL)); + // Get first child + int rootNode = outputNode.getValue(); + int inputNode = rootNode; + assertEquals(Cfgmgr32.CR_SUCCESS, Cfgmgr32.INSTANCE.CM_Get_Child(outputNode, inputNode, 0)); + // Iterate this child and its siblings + do { + inputNode = outputNode.getValue(); + // Get parent, confirm it matches root + assertEquals(Cfgmgr32.CR_SUCCESS, Cfgmgr32.INSTANCE.CM_Get_Parent(outputNode, inputNode, 0)); + assertEquals(rootNode, outputNode.getValue()); + } while (Cfgmgr32.CR_SUCCESS == Cfgmgr32.INSTANCE.CM_Get_Sibling(outputNode, inputNode, 0)); + } + + /** + * Tests CM_Locate_DevNode, CM_Get_Device_ID_Size, CM_Get_Device_ID + * + * @throws UnsupportedEncodingException + */ + @Test + public void testDeviceID() { + // Fetch the root node + IntByReference outputNode = new IntByReference(); + assertEquals(Cfgmgr32.CR_SUCCESS, + Cfgmgr32.INSTANCE.CM_Locate_DevNode(outputNode, null, Cfgmgr32.CM_LOCATE_DEVNODE_NORMAL)); + int rootNode = outputNode.getValue(); + + // Get Device ID character count + IntByReference pulLen = new IntByReference(); + Cfgmgr32.INSTANCE.CM_Get_Device_ID_Size(pulLen, rootNode, 0); + assertTrue(pulLen.getValue() > 0); + + // Get Device ID from util + String deviceId = Cfgmgr32Util.CM_Get_Device_ID(rootNode); + assertEquals(pulLen.getValue(), deviceId.length()); + + // Look up node from device ID + assertEquals(Cfgmgr32.CR_SUCCESS, + Cfgmgr32.INSTANCE.CM_Locate_DevNode(outputNode, deviceId, Cfgmgr32.CM_LOCATE_DEVNODE_NORMAL)); + assertEquals(rootNode, outputNode.getValue()); + } +}