diff --git a/CHANGES.md b/CHANGES.md index d57bae50c1..26ff281a14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -46,6 +46,7 @@ Features * [#618](https://github.com/java-native-access/jna/pull/618): Implement SAFEARRAY access and bugfix VARIANT - [@matthiasblaesing](https://github.com/matthiasblaesing). * [#616](https://github.com/java-native-access/jna/pull/616): Allow access to base interfaces (most important IDispatch) via ProxyObject and improve binding by allowing to use dispId for the call - [@matthiasblaesing](https://github.com/matthiasblaesing). * [#621](https://github.com/java-native-access/jna/pull/621): Added TYPEFLAGS-constants for `wTypeFlags` in `com.sun.jna.platform.win32.OaIdl.TYPEATTR` - [@SevenOf9Sleeper](https://github.com/SevenOf9Sleeper). +* [#625](https://github.com/java-native-access/jna/pull/625): Make conversion to/from java to/from VARIANT in `com.sun.jna.platform.win32.COM.util.Convert` more flexible and dependable by introducing more conversions and unittests - [@matthiasblaesing](https://github.com/matthiasblaesing). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/CallbackProxy.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/CallbackProxy.java index 5d2bda3ea7..22ffcba029 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/CallbackProxy.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/CallbackProxy.java @@ -133,24 +133,8 @@ void invokeOnThread(final DISPID dispIdMember, final REFIID riid, LCID lcid, WOR for ( int i = 0; i < vargs.variantArg.length; i++) { Class targetClass = params[vargs.variantArg.length - 1 - i]; Variant.VARIANT varg = vargs.variantArg[i]; - Object jarg = Convert.toJavaObject(varg, targetClass); - if (jarg instanceof IDispatch) { - // If a dispatch is returned try to wrap it into a proxy - // helper if the target is ComInterface annotated - IDispatch dispatch = (IDispatch) jarg; - //get raw IUnknown interface - PointerByReference ppvObject = new PointerByReference(); - IID iid = com.sun.jna.platform.win32.COM.IUnknown.IID_IUNKNOWN; - dispatch.QueryInterface(new REFIID(iid), ppvObject); - IUnknown unk = CallbackProxy.this.factory.createProxy(IUnknown.class, dispatch); - if(targetClass.getAnnotation(ComInterface.class) != null) { - rjargs.add(unk.queryInterface(targetClass)); - } else { - rjargs.add(unk); - } - } else { - rjargs.add(jarg); - } + Object jarg = Convert.toJavaObject(varg, targetClass, factory, true); + rjargs.add(jarg); } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/Convert.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/Convert.java index 6fce8d824e..b523e351f0 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/Convert.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/Convert.java @@ -12,6 +12,7 @@ */ package com.sun.jna.platform.win32.COM.util; +import com.sun.jna.platform.win32.OaIdl.DATE; import com.sun.jna.platform.win32.OaIdl.VARIANT_BOOL; import com.sun.jna.platform.win32.OleAuto; import com.sun.jna.platform.win32.Variant; @@ -21,10 +22,43 @@ import java.lang.reflect.Proxy; import java.util.Date; -import com.sun.jna.platform.win32.WTypes; import com.sun.jna.platform.win32.WinDef; import com.sun.jna.platform.win32.Variant.VARIANT; import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WinDef.BOOL; +import com.sun.jna.platform.win32.WinDef.BYTE; +import com.sun.jna.platform.win32.WinDef.CHAR; +import com.sun.jna.platform.win32.WinDef.LONG; +import com.sun.jna.platform.win32.WinDef.SHORT; +import com.sun.jna.platform.win32.OaIdl; +import static com.sun.jna.platform.win32.Variant.VT_ARRAY; +import static com.sun.jna.platform.win32.Variant.VT_BOOL; +import static com.sun.jna.platform.win32.Variant.VT_BSTR; +import static com.sun.jna.platform.win32.Variant.VT_BYREF; +import static com.sun.jna.platform.win32.Variant.VT_CY; +import static com.sun.jna.platform.win32.Variant.VT_DATE; +import static com.sun.jna.platform.win32.Variant.VT_DECIMAL; +import static com.sun.jna.platform.win32.Variant.VT_DISPATCH; +import static com.sun.jna.platform.win32.Variant.VT_EMPTY; +import static com.sun.jna.platform.win32.Variant.VT_ERROR; +import static com.sun.jna.platform.win32.Variant.VT_I1; +import static com.sun.jna.platform.win32.Variant.VT_I2; +import static com.sun.jna.platform.win32.Variant.VT_I4; +import static com.sun.jna.platform.win32.Variant.VT_I8; +import static com.sun.jna.platform.win32.Variant.VT_INT; +import static com.sun.jna.platform.win32.Variant.VT_NULL; +import static com.sun.jna.platform.win32.Variant.VT_R4; +import static com.sun.jna.platform.win32.Variant.VT_R8; +import static com.sun.jna.platform.win32.Variant.VT_RECORD; +import static com.sun.jna.platform.win32.Variant.VT_SAFEARRAY; +import static com.sun.jna.platform.win32.Variant.VT_UI1; +import static com.sun.jna.platform.win32.Variant.VT_UI2; +import static com.sun.jna.platform.win32.Variant.VT_UI4; +import static com.sun.jna.platform.win32.Variant.VT_UI8; +import static com.sun.jna.platform.win32.Variant.VT_UINT; +import static com.sun.jna.platform.win32.Variant.VT_UNKNOWN; +import static com.sun.jna.platform.win32.Variant.VT_VARIANT; +import com.sun.jna.platform.win32.WinDef.PVOID; /** * This class is considered internal to the package. @@ -50,20 +84,42 @@ class Convert { public static VARIANT toVariant(Object value) { if (value instanceof VARIANT) { return (VARIANT) value; - } else if (value instanceof Boolean) { - return new VARIANT((Boolean) value); - } else if (value instanceof Long) { - return new VARIANT(new WinDef.LONG((Long) value)); + } else if (value instanceof BSTR) { + return new VARIANT((BSTR) value); + } else if (value instanceof VARIANT_BOOL) { + return new VARIANT((VARIANT_BOOL) value); + } else if (value instanceof BOOL) { + return new VARIANT((BOOL) value); + } else if (value instanceof LONG) { + return new VARIANT((LONG) value); + } else if (value instanceof SHORT) { + return new VARIANT((SHORT) value); + } else if (value instanceof DATE) { + return new VARIANT((DATE) value); + } else if (value instanceof BYTE) { + return new VARIANT((BYTE) value); + } else if (value instanceof Byte) { + return new VARIANT((Byte) value); + } else if (value instanceof Character) { + return new VARIANT((Character) value); + } else if (value instanceof CHAR) { + return new VARIANT((CHAR) value); + } else if (value instanceof Short) { + return new VARIANT((Short) value); } else if (value instanceof Integer) { return new VARIANT((Integer) value); - } else if (value instanceof Short) { - return new VARIANT(new WinDef.SHORT((Short) value)); + } else if (value instanceof Long) { + return new VARIANT((Long) value); } else if (value instanceof Float) { return new VARIANT((Float) value); } else if (value instanceof Double) { return new VARIANT((Double) value); } else if (value instanceof String) { return new VARIANT((String) value); + } else if (value instanceof Boolean) { + return new VARIANT((Boolean) value); + } else if (value instanceof com.sun.jna.platform.win32.COM.IDispatch) { + return new VARIANT((com.sun.jna.platform.win32.COM.IDispatch) value); } else if (value instanceof Date) { return new VARIANT((Date) value); } else if (value instanceof Proxy) { @@ -78,43 +134,171 @@ public static VARIANT toVariant(Object value) { } } - public static Object toJavaObject(VARIANT value, Class targetClass) { - if (null==value) { + public static Object toJavaObject(VARIANT value, Class targetClass, Factory factory, boolean addReference) { + if (null==value + || value.getVarType().intValue() == VT_EMPTY + || value.getVarType().intValue() == VT_NULL) { return null; } + if (targetClass != null + && (!targetClass.isAssignableFrom(Object.class))) { + if (targetClass != null && targetClass.isAssignableFrom(value.getClass())) { + return value; + } + + Object vobj = value.getValue(); + if (vobj != null && (targetClass == null || targetClass.isAssignableFrom(vobj.getClass()))) { + return vobj; + } + } + + if (value.getVarType().intValue() == (VT_BYREF | VT_VARIANT)) { + value = (VARIANT) value.getValue(); + } + // Passing null or Object.class as targetClass switch to default // handling - boolean concreteClassRequested = targetClass != null - && (! targetClass.isAssignableFrom(Object.class)); - - if (concreteClassRequested && targetClass.isAssignableFrom(value.getClass())) { - return value; + if (targetClass == null + || (targetClass.isAssignableFrom(Object.class))) { + + targetClass = null; + + int varType = value.getVarType().intValue(); + + switch (value.getVarType().intValue()) { + case VT_UI1: + case VT_I1: + case VT_BYREF | VT_UI1: + case VT_BYREF | VT_I1: + targetClass = Byte.class; + break; + case VT_I2: + case VT_BYREF | VT_I2: + targetClass = Short.class; + break; + case VT_UI2: + case VT_BYREF | VT_UI2: + targetClass = Character.class; + break; + case VT_INT: + case VT_UINT: + case VT_UI4: + case VT_I4: + case VT_BYREF | VT_I4: + case VT_BYREF | VT_UI4: + case VT_BYREF | VT_INT: + case VT_BYREF | VT_UINT: + targetClass = Integer.class; + break; + case VT_UI8: + case VT_I8: + case VT_BYREF | VT_I8: + case VT_BYREF | VT_UI8: + targetClass = Long.class; + break; + case VT_R4: + case VT_BYREF | VT_R4: + targetClass = Float.class; + break; + case VT_R8: + case VT_BYREF | VT_R8: + targetClass = Double.class; + break; + case VT_BOOL: + case VT_BYREF | VT_BOOL: + targetClass = Boolean.class; + break; + case VT_ERROR: + case VT_BYREF | VT_ERROR: + targetClass = WinDef.SCODE.class; + break; + case VT_CY: + case VT_BYREF | VT_CY: + targetClass = OaIdl.CURRENCY.class; + break; + case VT_DATE: + case VT_BYREF | VT_DATE: + targetClass = Date.class; + break; + case VT_BSTR: + case VT_BYREF | VT_BSTR: + targetClass = String.class; + break; + case VT_UNKNOWN: + case VT_BYREF | VT_UNKNOWN: + targetClass = com.sun.jna.platform.win32.COM.IUnknown.class; + break; + case VT_DISPATCH: + case VT_BYREF | VT_DISPATCH: + targetClass = IDispatch.class; + break; + case VT_BYREF | VT_VARIANT: + targetClass = Variant.class; + break; + case VT_BYREF: + targetClass = PVOID.class; + break; + case VT_BYREF | VT_DECIMAL: + targetClass = OaIdl.DECIMAL.class; + break; + case VT_RECORD: + default: + if ((varType & VT_ARRAY) > 0 + || ((varType & VT_SAFEARRAY) > 0)) { + targetClass = OaIdl.SAFEARRAY.class; + } + } } - Object vobj = value.getValue(); - if (vobj != null && concreteClassRequested && targetClass.isAssignableFrom(vobj.getClass())) { - return vobj; + + Object result; + if(Byte.class.equals(targetClass) || byte.class.equals(targetClass)) { + result = value.byteValue(); + } else if (Short.class.equals(targetClass) || short.class.equals(targetClass)) { + result = value.shortValue(); + } else if (Character.class.equals(targetClass) || char.class.equals(targetClass)) { + result = (char) value.intValue(); + } else if (Integer.class.equals(targetClass) || int.class.equals(targetClass)) { + result = value.intValue(); + } else if (Long.class.equals(targetClass) || long.class.equals(targetClass) || IComEnum.class.isAssignableFrom(targetClass)) { + result = value.longValue(); + } else if (Float.class.equals(targetClass) || float.class.equals(targetClass)) { + result = value.floatValue(); + } else if (Double.class.equals(targetClass) || double.class.equals(targetClass)) { + result = value.doubleValue(); + } else if (Boolean.class.equals(targetClass) || boolean.class.equals(targetClass)) { + result = value.booleanValue(); + } else if (Date.class.equals(targetClass)) { + result = value.dateValue(); + } else if (String.class.equals(targetClass)) { + result = value.stringValue(); + } else if (value.getValue() instanceof com.sun.jna.platform.win32.COM.IDispatch) { + com.sun.jna.platform.win32.COM.IDispatch d = (com.sun.jna.platform.win32.COM.IDispatch) value.getValue(); + Object proxy = factory.createProxy(targetClass, d); + // must release a COM reference, createProxy adds one, as does the + // call + if (!addReference) { + int n = d.Release(); + } + result = proxy; + } else { + /* + WinDef.SCODE.class.equals(targetClass) + || OaIdl.CURRENCY.class.equals(targetClass) + || OaIdl.DECIMAL.class.equals(targetClass) + || OaIdl.SAFEARRAY.class.equals(targetClass) + || com.sun.jna.platform.win32.COM.IUnknown.class.equals(targetClass) + || Variant.class.equals(targetClass) + || PVOID.class.equals(targetClass + */ + result = value.getValue(); } - // Handle VARIANTByRef - if(vobj instanceof VARIANT) { - vobj = ((VARIANT) vobj).getValue(); + + if (IComEnum.class.isAssignableFrom(targetClass)) { + return targetClass.cast(Convert.toComEnum((Class) targetClass, result)); + } else { + return result; } - if (vobj instanceof WinDef.BOOL) { - return ((WinDef.BOOL) vobj).booleanValue(); - } else if (vobj instanceof VARIANT_BOOL) { - return ((VARIANT_BOOL) vobj).booleanValue(); - } else if (vobj instanceof WinDef.LONG) { - return ((WinDef.LONG) vobj).longValue(); - } else if (vobj instanceof WinDef.SHORT) { - return ((WinDef.SHORT) vobj).shortValue(); - } else if (vobj instanceof WinDef.UINT) { - return ((WinDef.UINT) vobj).intValue(); - } else if (vobj instanceof WinDef.WORD) { - return ((WinDef.WORD) vobj).intValue(); - } else if (vobj instanceof WTypes.BSTR) { - return ((WTypes.BSTR) vobj).getValue(); - } - return vobj; } public static T toComEnum(Class enumType, Object value) { diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java index decfc3b6d9..a328e3b529 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/util/ProxyObject.java @@ -402,20 +402,9 @@ public T invokeMethod(Class returnType, DISPID dispID, Object... args) { } private T convertAndFreeReturn(VARIANT.ByReference result, Class returnType) { - Object jobj = Convert.toJavaObject(result, returnType); - if (IComEnum.class.isAssignableFrom(returnType)) { - return returnType.cast(Convert.toComEnum((Class) returnType, jobj)); - } else if (jobj instanceof IDispatch) { - IDispatch d = (IDispatch) jobj; - T t = this.factory.createProxy(returnType, d); - // must release a COM reference, createProxy adds one, as does the - // call - int n = d.Release(); - return t; - } else { - Convert.free(result, returnType); - return returnType.cast(jobj); - } + Object jobj = Convert.toJavaObject(result, returnType, factory, false); + Convert.free(result, returnType); + return returnType.cast(jobj); } @Override diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Variant.java b/contrib/platform/src/com/sun/jna/platform/win32/Variant.java index 891ac46511..bbfbeabb38 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Variant.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Variant.java @@ -533,32 +533,36 @@ public Object getValue() { } public byte byteValue() { - return ((IntegerType) this.getValue()).byteValue(); + return ((Number) this.getValue()).byteValue(); } public short shortValue() { - return ((IntegerType) this.getValue()).shortValue(); + return ((Number) this.getValue()).shortValue(); } public int intValue() { - return ((IntegerType) this.getValue()).intValue(); + return ((Number) this.getValue()).intValue(); } public long longValue() { - return ((IntegerType) this.getValue()).longValue(); + return ((Number) this.getValue()).longValue(); } public float floatValue() { - return (Float) this.getValue(); + return ((Number) this.getValue()).floatValue(); } public double doubleValue() { - return (Double) this.getValue(); + return ((Number) this.getValue()).doubleValue(); } public String stringValue() { BSTR bstr = (BSTR) this.getValue(); - return bstr.getValue(); + if(bstr == null) { + return null; + } else { + return bstr.getValue(); + } } public boolean booleanValue() { @@ -568,7 +572,11 @@ public boolean booleanValue() { public Date dateValue() { DATE varDate = (DATE) this.getValue(); - return varDate.getAsJavaDate(); + if(varDate == null) { + return null; + } else { + return varDate.getAsJavaDate(); + } } @Deprecated diff --git a/contrib/platform/test/com/sun/jna/platform/win32/COM/util/ConvertTest.java b/contrib/platform/test/com/sun/jna/platform/win32/COM/util/ConvertTest.java new file mode 100644 index 0000000000..73ea3d7002 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/COM/util/ConvertTest.java @@ -0,0 +1,274 @@ +package com.sun.jna.platform.win32.COM.util; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OaIdl.DATE; +import com.sun.jna.platform.win32.OaIdl.VARIANT_BOOL; +import com.sun.jna.platform.win32.Ole32; +import com.sun.jna.platform.win32.Variant; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WinDef.BOOL; +import com.sun.jna.platform.win32.WinDef.BYTE; +import com.sun.jna.platform.win32.WinDef.CHAR; +import com.sun.jna.platform.win32.WinDef.LONG; +import com.sun.jna.platform.win32.WinDef.SHORT; +import java.util.Date; +import org.junit.AfterClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.junit.BeforeClass; + +// Untested: IDispatch +// Untested: Proxy +public class ConvertTest { + + private static Factory fact; + + @BeforeClass + public static void init() { + Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); + fact = new Factory(); + } + + @AfterClass + public static void destruct() { + fact.disposeAll(); + fact = null; + Ole32.INSTANCE.CoUninitialize(); + } + + @Test + public void convertVariant() { + VARIANT testValue = new Variant.VARIANT(42); + VARIANT resultVariant = Convert.toVariant(testValue); + assertSame(testValue, resultVariant); + assertSame(testValue, Convert.toJavaObject(resultVariant, VARIANT.class, fact, true)); + assertSame(42, Convert.toJavaObject(testValue, Object.class, fact, true)); + } + + @Test + public void convertString() { + // This test leaks the allocated BSTR -- this is tollerated here, as memory usage is minimal + String testString = "Hallo"; + BSTR testValue = new BSTR(testString); + VARIANT resultVariant = Convert.toVariant(testValue); + assertEquals(testString, resultVariant.stringValue()); + assertEquals(testString, Convert.toJavaObject(resultVariant, Object.class, fact, true)); + assertEquals(testString, Convert.toJavaObject(resultVariant, String.class, fact, true)); + + resultVariant = Convert.toVariant(testString); + assertEquals(testString, resultVariant.stringValue()); + assertEquals(testString, Convert.toJavaObject(resultVariant, Object.class, fact, true)); + assertEquals(testString, Convert.toJavaObject(resultVariant, String.class, fact, true)); + } + + @Test + public void convertBoolean() { + VARIANT_BOOL testVariantBOOL = new VARIANT_BOOL(true); + VARIANT resultVariantBOOL = Convert.toVariant(testVariantBOOL); + assertEquals(true, resultVariantBOOL.booleanValue()); + assertEquals(true, Convert.toJavaObject(resultVariantBOOL, Object.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultVariantBOOL, Boolean.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultVariantBOOL, boolean.class, fact, true)); + + BOOL testBOOL = new BOOL(true); + VARIANT resultBOOL = Convert.toVariant(testBOOL); + assertEquals(true, resultBOOL.booleanValue()); + assertEquals(true, Convert.toJavaObject(resultBOOL, Object.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBOOL, Boolean.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBOOL, boolean.class, fact, true)); + + Boolean testBooleanObj = true; + VARIANT resultBooleanObj = Convert.toVariant(testBooleanObj); + boolean testBoolean = true; + VARIANT resultBoolean = Convert.toVariant(testBoolean); + + assertEquals(true, resultBooleanObj.booleanValue()); + assertEquals(true, resultBoolean.booleanValue()); + assertEquals(true, Convert.toJavaObject(resultBooleanObj, Object.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBoolean, Object.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBooleanObj, boolean.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBoolean, boolean.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBooleanObj, Boolean.class, fact, true)); + assertEquals(true, Convert.toJavaObject(resultBoolean, Boolean.class, fact, true)); + } + + @Test + public void convertIntTypes() { + LONG testLONG = new LONG(42); + VARIANT resultLONG = Convert.toVariant(testLONG); + assertEquals(42, resultLONG.longValue()); + assertEquals(Integer.class, Convert.toJavaObject(resultLONG, Object.class, fact, true).getClass()); + assertEquals(42, Convert.toJavaObject(resultLONG, int.class, fact, true)); + assertEquals(42, Convert.toJavaObject(resultLONG, Integer.class, fact, true)); + + SHORT testSHORT = new SHORT(42); + VARIANT resultSHORT = Convert.toVariant(testSHORT); + assertEquals(42, resultSHORT.longValue()); + assertEquals(Short.class, Convert.toJavaObject(resultSHORT, Object.class, fact, true).getClass()); + assertEquals((short) 42, Convert.toJavaObject(resultSHORT, short.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultSHORT, Short.class, fact, true)); + + BYTE testBYTE = new BYTE(42); + VARIANT resultBYTE = Convert.toVariant(testBYTE); + Byte testByteObj = 42; + VARIANT resultByteObj = Convert.toVariant(testByteObj); + byte testByte = 42; + VARIANT resultByte = Convert.toVariant(testByte); + + assertEquals(42, resultBYTE.longValue()); + assertEquals(42, resultByteObj.longValue()); + assertEquals(42, resultByte.longValue()); + assertEquals(Byte.class, Convert.toJavaObject(resultBYTE, Object.class, fact, true).getClass()); + assertEquals(Byte.class, Convert.toJavaObject(resultByteObj, Object.class, fact, true).getClass()); + assertEquals(Byte.class, Convert.toJavaObject(resultByte, Object.class, fact, true).getClass()); + assertEquals((byte) 42, Convert.toJavaObject(resultBYTE, byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultByteObj, byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultByte, byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultBYTE, Byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultByteObj, Byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultByte, Byte.class, fact, true)); + + Character testCharObj = 42; + VARIANT resultCharObj = Convert.toVariant(testCharObj); + char testChar = 42; + VARIANT resultChar = Convert.toVariant(testChar); + + assertEquals(42, resultCharObj.longValue()); + assertEquals(42, resultChar.longValue()); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultCharObj, Object.class, fact, true)); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultChar, Object.class, fact, true)); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultCharObj, char.class, fact, true)); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultChar, char.class, fact, true)); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultCharObj, Character.class, fact, true)); + assertEquals(testCharObj, (Character) Convert.toJavaObject(resultChar, Character.class, fact, true)); + + + CHAR testCHAR = new CHAR(42); + VARIANT resultCHAR = Convert.toVariant(testCHAR); + + assertEquals(42, resultCHAR.longValue()); + assertEquals((byte) 42, Convert.toJavaObject(resultCHAR, Object.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultCHAR, byte.class, fact, true)); + assertEquals((byte) 42, Convert.toJavaObject(resultCHAR, Byte.class, fact, true)); + + Short testShortObj = 42; + VARIANT resultShortObj = Convert.toVariant(testShortObj); + short testShort = 42; + VARIANT resultShort = Convert.toVariant(testShort); + + assertEquals(42, resultShortObj.longValue()); + assertEquals(42, resultShort.longValue()); + assertEquals((short) 42, Convert.toJavaObject(resultShortObj, Object.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultShort, Object.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultShortObj, short.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultShort, short.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultShortObj, Short.class, fact, true)); + assertEquals((short) 42, Convert.toJavaObject(resultShort, Short.class, fact, true)); + + Integer testIntegerObj = 42; + VARIANT resultIntegerObj = Convert.toVariant(testIntegerObj); + int testInteger = 42; + VARIANT resultInteger = Convert.toVariant(testInteger); + + assertEquals(42, resultIntegerObj.longValue()); + assertEquals(42, resultInteger.longValue()); + assertEquals((int) 42, Convert.toJavaObject(resultIntegerObj, Object.class, fact, true)); + assertEquals((int) 42, Convert.toJavaObject(resultInteger, Object.class, fact, true)); + assertEquals((int) 42, Convert.toJavaObject(resultIntegerObj, int.class, fact, true)); + assertEquals((int) 42, Convert.toJavaObject(resultInteger, int.class, fact, true)); + assertEquals((int) 42, Convert.toJavaObject(resultIntegerObj, Integer.class, fact, true)); + assertEquals((int) 42, Convert.toJavaObject(resultInteger, Integer.class, fact, true)); + + Long testLongObj = 42L; + VARIANT resultLongObj = Convert.toVariant(testLongObj); + long testLong = 42; + VARIANT resultLong = Convert.toVariant(testLong); + + assertEquals(42, resultLongObj.longValue()); + assertEquals(42, resultLong.longValue()); + assertEquals((long) 42, Convert.toJavaObject(resultLongObj, Object.class, fact, true)); + assertEquals((long) 42, Convert.toJavaObject(resultLong, Object.class, fact, true)); + assertEquals((long) 42, Convert.toJavaObject(resultLongObj, long.class, fact, true)); + assertEquals((long) 42, Convert.toJavaObject(resultLong, long.class, fact, true)); + assertEquals((long) 42, Convert.toJavaObject(resultLongObj, Long.class, fact, true)); + assertEquals((long) 42, Convert.toJavaObject(resultLong, Long.class, fact, true)); + } + + @Test + public void convertFloat() { + Float testFloatObj = 42.23f; + VARIANT resultFloatObj = Convert.toVariant(testFloatObj); + float testFloat = 42.23f; + VARIANT resultFloat = Convert.toVariant(testFloat); + + assertEquals(42.23f, resultFloatObj.floatValue(), 0.01); + assertEquals(42.23f, resultFloat.floatValue(), 0.01); + assertEquals(42.23d, resultFloat.doubleValue(), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloatObj, Object.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloat, Object.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloatObj, float.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloat, float.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloatObj, Float.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultFloat, Float.class, fact, true), 0.01); + assertEquals(42.23d, (Double) Convert.toJavaObject(resultFloat, double.class, fact, true), 0.01); + + Double testDoubleObj = 42.23; + VARIANT resultDoubleObj = Convert.toVariant(testDoubleObj); + double testDouble = 42.23; + VARIANT resultDouble = Convert.toVariant(testDouble); + + assertEquals(42.23, resultDoubleObj.doubleValue(), 0.01); + assertEquals(42.23, resultDouble.doubleValue(), 0.01); + assertEquals(42.23f, resultDouble.floatValue(), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDoubleObj, Object.class, fact, true), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDouble, Object.class, fact, true), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDoubleObj, double.class, fact, true), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDouble, double.class, fact, true), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDoubleObj, Double.class, fact, true), 0.01); + assertEquals(42.23, (Double) Convert.toJavaObject(resultDouble, Double.class, fact, true), 0.01); + assertEquals(42.23f, (Float) Convert.toJavaObject(resultDouble, float.class, fact, true), 0.01); + } + + @Test + public void convertDate() { + Date testDate = new Date(2015 - 1900, 1, 1, 9, 0, 0); + VARIANT resultDate = Convert.toVariant(testDate); + DATE testDATE = new DATE(testDate); + VARIANT resultDATE = Convert.toVariant(testDATE); + + assertEquals(testDate, resultDate.dateValue()); + assertEquals(testDate, resultDATE.dateValue()); + assertEquals(testDate, Convert.toJavaObject(resultDate, Object.class, fact, true)); + assertEquals(testDate, Convert.toJavaObject(resultDATE, Object.class, fact, true)); + assertEquals(testDate, Convert.toJavaObject(resultDate, Date.class, fact, true)); + assertEquals(testDate, Convert.toJavaObject(resultDATE, Date.class, fact, true)); + } + + @Test + public void convertEnum() { + TestEnum testEnum = TestEnum.Val2; + VARIANT resultEnum = Convert.toVariant(testEnum); + assertEquals((int) testEnum.getValue(), resultEnum.intValue()); + assertEquals((int) testEnum.getValue(), Convert.toJavaObject(resultEnum, Object.class, fact, true)); + assertEquals(testEnum, Convert.toJavaObject(resultEnum, TestEnum.class, fact, true)); + } + +} + +enum TestEnum implements IComEnum { + Val1(1), + Val2(2), + Val3(3),; + + long value; + + private TestEnum(long val) { + this.value = val; + } + + public long getValue() { + return this.value; + } + +} diff --git a/contrib/platform/test/com/sun/jna/platform/win32/COM/util/IDispatchTest.java b/contrib/platform/test/com/sun/jna/platform/win32/COM/util/IDispatchTest.java index 3232f4b77e..a6b8846ca7 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/COM/util/IDispatchTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/COM/util/IDispatchTest.java @@ -1,16 +1,19 @@ package com.sun.jna.platform.win32.COM.util; import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.util.annotation.ComEventCallback; +import com.sun.jna.platform.win32.COM.util.annotation.ComInterface; import com.sun.jna.platform.win32.COM.util.annotation.ComMethod; import com.sun.jna.platform.win32.COM.util.annotation.ComObject; import com.sun.jna.platform.win32.COM.util.annotation.ComProperty; import com.sun.jna.platform.win32.OaIdl.DISPID; import com.sun.jna.platform.win32.Ole32; import junit.framework.TestCase; -import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.assertFalse; import org.junit.After; import org.junit.Before; import org.junit.Test; +import static junit.framework.TestCase.assertTrue; public class IDispatchTest { @@ -56,9 +59,9 @@ public void testDispatchBaseOnMethodName() throws InterruptedException { } } - TestCase.assertTrue(navigationHappend); - ieApp.Quit(); + + TestCase.assertTrue(navigationHappend); } @ComObject(progId = "Internet.Explorer.1", clsId = "{0002DF01-0000-0000-C000-000000000046}") @@ -95,9 +98,9 @@ public void testDispatchBaseOnNamed() throws InterruptedException { ieApp.Navigate2_MOD("https://github.com/java-native-access/"); - // Check max. 2s if Navigation happend + // Check max. 10s if Navigation happend boolean navigationHappend = false; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 50; i++) { String url = ieApp.getLocationURL_MOD(); if (!url.isEmpty()) { navigationHappend = true; @@ -107,9 +110,9 @@ public void testDispatchBaseOnNamed() throws InterruptedException { } } - TestCase.assertTrue(navigationHappend); - ieApp.Quit_MOD(); + + TestCase.assertTrue(navigationHappend); } @ComObject(progId = "Internet.Explorer.1", clsId = "{0002DF01-0000-0000-C000-000000000046}") @@ -148,7 +151,7 @@ public void testDispatchBaseOnDISPID() throws InterruptedException { // Check max. 2s if Navigation happend boolean navigationHappend = false; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 50; i++) { String url = ieApp.getLocationURL_MOD(); if (!url.isEmpty()) { navigationHappend = true; @@ -158,9 +161,9 @@ public void testDispatchBaseOnDISPID() throws InterruptedException { } } - TestCase.assertTrue(navigationHappend); - ieApp.Quit_MOD(); + + TestCase.assertTrue(navigationHappend); } @ComObject(progId = "Internet.Explorer.1", clsId = "{0002DF01-0000-0000-C000-000000000046}") @@ -197,9 +200,9 @@ public void testIDispatchName() throws InterruptedException { ieApp.invokeMethod(Void.class, "Navigate2", "https://github.com/java-native-access/"); - // Check max. 2s if Navigation happend + // Check max. 10s if Navigation happend boolean navigationHappend = false; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 50; i++) { String url = ieApp.getProperty(String.class, "LocationURL"); if (!url.isEmpty()) { navigationHappend = true; @@ -209,9 +212,9 @@ public void testIDispatchName() throws InterruptedException { } } - TestCase.assertTrue(navigationHappend); - ieApp.invokeMethod(Void.class, "Quit"); + + TestCase.assertTrue(navigationHappend); } @Test @@ -235,9 +238,9 @@ public void testIDispatchDISPID() throws InterruptedException { ieApp.invokeMethod(Void.class, navigate2, "https://github.com/java-native-access/"); - // Check max. 2s if Navigation happend + // Check max. 10s if Navigation happend boolean navigationHappend = false; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 50; i++) { String url = ieApp.getProperty(String.class, locationURL); if (!url.isEmpty()) { navigationHappend = true; @@ -247,12 +250,176 @@ public void testIDispatchDISPID() throws InterruptedException { } } - TestCase.assertTrue(navigationHappend); - ieApp.invokeMethod(Void.class, quit); + + TestCase.assertTrue(navigationHappend); } @ComObject(progId = "Internet.Explorer.1", clsId = "{0002DF01-0000-0000-C000-000000000046}") interface ComInternetExplorerIDispatch extends IDispatch { } + + @Test + public void testCallbackAll() throws InterruptedException { + ComInternetExplorerEventTest ieApp = factory.createObject(ComInternetExplorerEventTest.class); + ieApp.setVisible(false); + + DWebBrowserEvents2_Listener listener1 = new DWebBrowserEvents2_Listener(); + DWebBrowserEvents2_Listener listener2 = new DWebBrowserEvents2_Listener(); + DWebBrowserEvents2_Listener listener3 = new DWebBrowserEvents2_Listener(); + DWebBrowserEvents2_Listener listener4 = new DWebBrowserEvents2_Listener(); + DWebBrowserEvents2_Listener listener5 = new DWebBrowserEvents2_Listener(); + + IComEventCallbackCookie cookie1 = ieApp.advise(DWebBrowserEvents2EventTestIDispatch.class, listener1); + IComEventCallbackCookie cookie2 = ieApp.advise(DWebBrowserEvents2EventTestIUnknown.class, listener2); + IComEventCallbackCookie cookie3 = ieApp.advise(DWebBrowserEvents2EventTestUtilIDispatch.class, listener3); + IComEventCallbackCookie cookie4 = ieApp.advise(DWebBrowserEvents2EventTestUtilIUnknown.class, listener4); + IComEventCallbackCookie cookie5 = ieApp.advise(DWebBrowserEvents2EventTestSubclass.class, listener5); + + ieApp.Navigate2("https://github.com/"); + + for(int i = 0; i < 50; i++) { + Thread.sleep(200); + if(listener1.IDispatch && listener2.IUnknown && listener3.UtilIDispatch && listener4.UtilIUnknown && listener5.Subclass) { + break; + } + } + + ieApp.unadvise(DWebBrowserEvents2EventTestIDispatch.class, cookie1); + ieApp.unadvise(DWebBrowserEvents2EventTestIUnknown.class, cookie2); + ieApp.unadvise(DWebBrowserEvents2EventTestUtilIDispatch.class, cookie3); + ieApp.unadvise(DWebBrowserEvents2EventTestUtilIUnknown.class, cookie4); + ieApp.unadvise(DWebBrowserEvents2EventTestSubclass.class, cookie5); + + ieApp.Quit(); + + assertTrue(listener1.IDispatch); + assertFalse(listener1.IUnknown); + assertFalse(listener1.UtilIDispatch); + assertFalse(listener1.UtilIUnknown); + assertFalse(listener1.Subclass); + + assertFalse(listener2.IDispatch); + assertTrue(listener2.IUnknown); + assertFalse(listener2.UtilIDispatch); + assertFalse(listener2.UtilIUnknown); + assertFalse(listener2.Subclass); + + assertFalse(listener3.IDispatch); + assertFalse(listener3.IUnknown); + assertTrue(listener3.UtilIDispatch); + assertFalse(listener3.UtilIUnknown); + assertFalse(listener3.Subclass); + + assertFalse(listener4.IDispatch); + assertFalse(listener4.IUnknown); + assertFalse(listener4.UtilIDispatch); + assertTrue(listener4.UtilIUnknown); + assertFalse(listener4.Subclass); + + assertFalse(listener5.IDispatch); + assertFalse(listener5.IUnknown); + assertFalse(listener5.UtilIDispatch); + assertFalse(listener5.UtilIUnknown); + assertTrue(listener5.Subclass); + } + + @ComObject(progId = "Internet.Explorer.1", clsId = "{0002DF01-0000-0000-C000-000000000046}") + interface ComInternetExplorerEventTest extends ComIWebBrowser2EventTest { + } + + @ComInterface(iid = "{D30C1661-CDAF-11D0-8A3E-00C04FC9E26E}") + interface ComIWebBrowser2EventTest extends IUnknown, IConnectionPoint { + + @ComProperty + boolean getVisible(); + + @ComProperty + void setVisible(boolean value); + + @ComMethod + void Quit(); + + @ComMethod + void Navigate2(String url); + } + + @ComInterface(iid = "{34A715A0-6587-11D0-924A-0020AFC7AC4D}") + interface DWebBrowserEvents2EventTestUtilIUnknown { + + @ComEventCallback(dispid = 0x000000fc) + void NavigateComplete2UtilIUnknown(IUnknown source, Object url); + } + + @ComInterface(iid = "{34A715A0-6587-11D0-924A-0020AFC7AC4D}") + interface DWebBrowserEvents2EventTestUtilIDispatch { + + @ComEventCallback(dispid = 0x000000fc) + void NavigateComplete2UtilIDispatch(IDispatch source, Object url); + } + + @ComInterface(iid = "{34A715A0-6587-11D0-924A-0020AFC7AC4D}") + interface DWebBrowserEvents2EventTestIUnknown { + + @ComEventCallback(dispid = 0x000000fc) + void NavigateComplete2IUnknown(com.sun.jna.platform.win32.COM.IUnknown source, Object url); + } + + @ComInterface(iid = "{34A715A0-6587-11D0-924A-0020AFC7AC4D}") + interface DWebBrowserEvents2EventTestIDispatch { + + @ComEventCallback(dispid = 0x000000fc) + void NavigateComplete2IDispatch(com.sun.jna.platform.win32.COM.IDispatch source, Object url); + } + + @ComInterface(iid = "{34A715A0-6587-11D0-924A-0020AFC7AC4D}") + interface DWebBrowserEvents2EventTestSubclass { + + @ComEventCallback(dispid = 0x000000fc) + void NavigateComplete2Subclass(ComIWebBrowser2EventTest source, Object url); + } + + class DWebBrowserEvents2_Listener extends AbstractComEventCallbackListener implements DWebBrowserEvents2EventTestUtilIUnknown, DWebBrowserEvents2EventTestUtilIDispatch, DWebBrowserEvents2EventTestIUnknown, DWebBrowserEvents2EventTestIDispatch, DWebBrowserEvents2EventTestSubclass { + + volatile boolean UtilIUnknown = false; + volatile boolean UtilIDispatch = false; + volatile boolean IUnknown = false; + volatile boolean IDispatch = false; + volatile boolean Subclass = false; + + @Override + public void errorReceivingCallbackEvent(String message, Exception exception) { +// System.err.println(message); + } + + public void NavigateComplete2UtilIUnknown(IUnknown source, Object url) { + if (url instanceof String && ((String) url).startsWith("https://github.com/")) { + UtilIUnknown = true; + } + } + + public void NavigateComplete2UtilIDispatch(IDispatch source, Object url) { + if (url instanceof String && ((String) url).startsWith("https://github.com/")) { + UtilIDispatch = true; + } + } + + public void NavigateComplete2IUnknown(com.sun.jna.platform.win32.COM.IUnknown source, Object url) { + if (url instanceof String && ((String) url).startsWith("https://github.com/")) { + IUnknown = true; + } + } + + public void NavigateComplete2IDispatch(com.sun.jna.platform.win32.COM.IDispatch source, Object url) { + if (url instanceof String && ((String) url).startsWith("https://github.com/")) { + IDispatch = true; + } + } + + public void NavigateComplete2Subclass(ComIWebBrowser2EventTest source, Object url) { + if (url instanceof String && ((String) url).startsWith("https://github.com/")) { + Subclass = true; + } + } + } }