diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java index b857c66..5797262 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32.java @@ -37,8 +37,9 @@ 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 CR_SUCCESS = 0; + public final static int CR_INVALID_POINTER = 0x00000003; + 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; diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java index 2a8fe3f..0df12fc 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Cfgmgr32Util.java @@ -33,7 +33,18 @@ * @author widdis[at]gmail[dot]com */ public abstract class Cfgmgr32Util { + 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, @@ -44,40 +55,43 @@ * local machine. * @return The device instance ID string. */ - public static String CM_Get_Device_ID(int devInst) { + 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(); - Cfgmgr32.INSTANCE.CM_Get_Device_ID_Size(pulLen, devInst, 0); - - // Add 1 for null terminator - int deviceIdLength = pulLen.getValue() + 1; - Memory buffer = new Memory(deviceIdLength * charToBytes); - // Fetch the buffer - int ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID(devInst, buffer, deviceIdLength, 0); - // In the unlikely event the device id changes this might not be big - // enough, try again - while (ret == Cfgmgr32.CR_BUFFER_SMALL) { + int errorCode = 0; + + // Do multiple tries if size of the ID changes between calls to + // CM_Get_Device_ID_Size and CM_Get_Device_ID + for(int i = 0; i < 10; i++) { + // Get Device ID character count + IntByReference pulLen = new IntByReference(); Cfgmgr32.INSTANCE.CM_Get_Device_ID_Size(pulLen, devInst, 0); - deviceIdLength = pulLen.getValue() + 1; - buffer = new Memory(deviceIdLength * charToBytes); - ret = Cfgmgr32.INSTANCE.CM_Get_Device_ID(devInst, buffer, deviceIdLength, 0); - } - // Convert buffer to Java String - String deviceId; - if (charToBytes == 1) { - deviceId = buffer.getString(0); - } else { - deviceId = buffer.getWideString(0); + // Add 1 for null terminator + int deviceIdLength = pulLen.getValue(); + // CM_Get_Device_ID does not write NULL terminator if not enougth + // space is available. This ensures, that after the written string + // at least one final NULL character follows + Memory buffer = new Memory((deviceIdLength + 1) * charToBytes); + buffer.clear(); + // Fetch the buffer + errorCode = Cfgmgr32.INSTANCE.CM_Get_Device_ID(devInst, buffer, deviceIdLength, 0); + if(errorCode == Cfgmgr32.CR_BUFFER_SMALL) { + //retry if buffer is too small + continue; + } + if (errorCode == Cfgmgr32.CR_SUCCESS) { + // Convert buffer to Java String + if (charToBytes == 1) { + return buffer.getString(0); + } else { + return buffer.getWideString(0); + } + } else { + throw new Cfgmgr32Exception(errorCode); + } } - // Edge case where there's not enough room for null terminator - // but returns successfully. In this case getString() grabs stray - // characters from memory outside our buffer. - if (deviceId.length() > deviceIdLength) { - deviceId = deviceId.substring(0, deviceIdLength); - } - return deviceId; + + throw new Cfgmgr32Exception(errorCode); } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java index 8c86a6d..8435e41 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Cfgmgr32Test.java @@ -23,12 +23,14 @@ */ package com.sun.jna.platform.win32; +import com.sun.jna.platform.win32.Cfgmgr32Util.Cfgmgr32Exception; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.junit.Test; import com.sun.jna.ptr.IntByReference; +import static junit.framework.TestCase.assertNotNull; /** * Tests methods in Cfgmgr32 @@ -83,4 +85,17 @@ Cfgmgr32.INSTANCE.CM_Locate_DevNode(outputNode, deviceId, Cfgmgr32.CM_LOCATE_DEVNODE_NORMAL)); assertEquals(rootNode, outputNode.getValue()); } + + @Test + public void testCMGetDeviceIDUtil() { + Exception caughtException = null; + try { + Cfgmgr32Util.CM_Get_Device_ID(-1); + } catch (Cfgmgr32Exception ex) { + caughtException = ex; + } + assertNotNull(caughtException); + assertTrue(caughtException instanceof Cfgmgr32Exception); + assertEquals(Cfgmgr32.CR_INVALID_POINTER, ((Cfgmgr32Exception)caughtException).getErrorCode()); + } }