diff --git a/CHANGES.md b/CHANGES.md index 3cc8452362..d57bae50c1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -43,6 +43,7 @@ Features * [#608](https://github.com/java-native-access/jna/pull/608): Mavenize the build process - change parent and native pom artifactId/name to differentiate in IDE and build tools. - [@bhamail](https://github.com/bhamail) * [#613](https://github.com/java-native-access/jna/pull/613): Make `com.sun.jna.platform.win32.Win32Exception` extend `com.sun.jna.LastErrorException` - [@lgoldstein](https://github.com/lgoldstein). * [#614](https://github.com/java-native-access/jna/pull/614): Added standard `com.sun.jna.platform.win32.Kernel32Util.closeHandle()` method that throws a `com.sun.jna.platform.win32.Win32Exception` if failed to close the handle - [@lgoldstein](https://github.com/lgoldstein). +* [#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). diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/COMLateBindingObject.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/COMLateBindingObject.java index c54c94d2f2..d837159030 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/COM/COMLateBindingObject.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/COMLateBindingObject.java @@ -149,7 +149,7 @@ protected boolean getBooleanProperty(String propertyName) { this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, this.getIDispatch(), propertyName); - return (((VARIANT_BOOL) result.getValue()).intValue() != 0); + return result.booleanValue(); } /** @@ -179,7 +179,7 @@ protected int getIntProperty(String propertyName) { this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, this.getIDispatch(), propertyName); - return ((LONG) result.getValue()).intValue(); + return result.intValue(); } /** @@ -194,7 +194,7 @@ protected short getShortProperty(String propertyName) { this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, this.getIDispatch(), propertyName); - return ((SHORT) result.getValue()).shortValue(); + return result.shortValue(); } /** @@ -209,7 +209,7 @@ protected String getStringProperty(String propertyName) { this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, this.getIDispatch(), propertyName); - return result.getValue().toString(); + return result.stringValue(); } /** 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 9a0c707ef5..6fce8d824e 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.VARIANT_BOOL; import com.sun.jna.platform.win32.OleAuto; import com.sun.jna.platform.win32.Variant; import java.lang.reflect.InvocationHandler; @@ -100,6 +101,8 @@ public static Object toJavaObject(VARIANT value, Class targetClass) { } 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) { diff --git a/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java b/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java index 4547e959ac..d223eb5f3b 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java @@ -6,11 +6,14 @@ import java.util.List; import com.sun.jna.IntegerType; +import com.sun.jna.Memory; import com.sun.jna.NativeLong; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Union; import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.platform.win32.COM.Dispatch; import com.sun.jna.platform.win32.Guid.GUID; import com.sun.jna.platform.win32.Variant.VARIANT; import com.sun.jna.platform.win32.Variant.VariantArg; @@ -31,14 +34,41 @@ import com.sun.jna.platform.win32.WinDef.USHORT; import com.sun.jna.platform.win32.WinDef.WORD; import com.sun.jna.platform.win32.COM.TypeComp; +import com.sun.jna.platform.win32.COM.Unknown; +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_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_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_INT; +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_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_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.ptr.ByReference; +import com.sun.jna.ptr.PointerByReference; +import java.util.Date; -// TODO: Auto-generated Javadoc /** * The Interface OaIdl. */ public interface OaIdl { - + + // The DATE Type is defined in localtime and the java Date type always contains + // a a timezone offset, so the difference has to be calculated and can't be + // predetermined + public static final long DATE_OFFSET = new Date(1899 - 1900, 12 - 1, 30, 0, 0, 0).getTime(); + /** * The Class EXCEPINFO. */ @@ -116,6 +146,14 @@ public VARIANT_BOOL() { public VARIANT_BOOL(long value) { super(2, value); } + + public VARIANT_BOOL(boolean value) { + this(value ? 0xFFFF : 0x0000); + } + + public boolean booleanValue() { + return shortValue() != 0x0000; + } } public static class _VARIANT_BOOL extends VARIANT_BOOL { @@ -169,6 +207,8 @@ public VARIANT_BOOL getValue() { } public static class DATE extends Structure { + private final static long MICRO_SECONDS_PER_DAY = 24L * 60L * 60L * 1000L; + public static class ByReference extends DATE implements Structure.ByReference { } @@ -183,7 +223,32 @@ public DATE() { public DATE(double date) { this.date = date; } + + public DATE(Date javaDate) { + setFromJavaDate(javaDate); + } + public Date getAsJavaDate() { + long days = (((long) this.date) * MICRO_SECONDS_PER_DAY) + DATE_OFFSET; + int hours = (int) (24 * Math.abs(this.date - ((long) this.date))); + + Date baseDate = new Date(days); + baseDate.setHours(hours); + baseDate.setMinutes(0); + baseDate.setSeconds(0); + return baseDate; + } + + public void setFromJavaDate(Date javaDate) { + double msSinceOrigin = javaDate.getTime() - DATE_OFFSET; + double daysAsFract = msSinceOrigin / MICRO_SECONDS_PER_DAY; + + double dayPart = Math.floor(daysAsFract); + double hourPart = Math.signum(daysAsFract) * (javaDate.getHours() / 24d); + + this.date = dayPart + hourPart; + } + @Override protected List getFieldOrder() { return FIELDS; @@ -443,6 +508,38 @@ protected List getFieldOrder() { } }; + /** + * General comment: All indices in the helper methods use java int. + * + *

VARTYPE for the SAFEARRAY can be:

+ * + * + * + *

The native type for the indices is LONG, which is defined as:

+ * + *
A 32-bit signed integer. The range is �2147483648 through 2147483647 decimal.
+ */ public static class SAFEARRAY extends Structure { public static class ByReference extends SAFEARRAY implements Structure.ByReference { @@ -469,10 +566,431 @@ public SAFEARRAY(Pointer pointer) { this.read(); } + @Override + public void read() { + super.read(); + rgsabound = (SAFEARRAYBOUND[]) rgsabound[0].toArray(cDims.intValue()); + } + @Override protected List getFieldOrder() { return FIELDS; } + + /** + * Create a SAFEARRAY with supplied VARIANT as element type. + * + *

+ * This helper creates a basic SAFEARRAY with a base type of VT_VARIANT. + * The array will have as many dimensions as parameters are passed in. + * The lowerbound for each dimension is set to zero, the count to the + * parameter value.

+ * + * @param size array of dimension size + * @return SAFEARRAYWrapper or {@code NULL} if creation fails. + */ + public static SAFEARRAY createSafeArray(int... size) { + return createSafeArray(new WTypes.VARTYPE(Variant.VT_VARIANT), size); + } + + /** + * Create a SAFEARRAY with supplied element type. + * + *

+ * The array will have as many dimensions as parameters are passed in. + * The lowerbound for each dimension is set to zero, the count to the + * parameter value.

+ * + * @param vartype type of array contents (see Variant.VT_* constants) + * @param size array of dimension size + * @return SAFEARRAYWrapper or {@code NULL} if creation fails. + */ + public static SAFEARRAY createSafeArray(VARTYPE vartype, int... size) { + OaIdl.SAFEARRAYBOUND[] rgsabound = (OaIdl.SAFEARRAYBOUND[]) new OaIdl.SAFEARRAYBOUND().toArray(size.length); + for (int i = 0; i < size.length; i++) { + rgsabound[i].lLbound = new WinDef.LONG(0); + rgsabound[i].cElements = new WinDef.ULONG(size[size.length - i - 1]); + } + SAFEARRAY.ByReference data = OleAuto.INSTANCE.SafeArrayCreate(vartype, new WinDef.UINT(size.length), rgsabound); + return data; + } + + /** + * Set value at {@code indices} in {@code array} to arg. + * + *

+ * The supplied argument is copied into the array. If the value is no + * longer needed, it needs to be freed if not handled automatically.

+ * + * @param indices the index, order follows java/C convention + * @param arg the arg + */ + public void putElement(Object arg, int... indices) { + WinDef.LONG[] paramIndices = new WinDef.LONG[indices.length]; + for (int i = 0; i < indices.length; i++) { + paramIndices[i] = new WinDef.LONG(indices[indices.length - i - 1]); + } + + WinNT.HRESULT hr; + Memory mem; + switch (getVarType().intValue()) { + case VT_BOOL: + mem = new Memory(2); + if(arg instanceof Boolean) { + mem.setShort(0, (short) (((Boolean) arg) ? 0xFFFF : 0) ); + } else { + mem.setShort(0, (short) (((Number) arg).intValue() > 0 ? 0xFFFF : 0)); + } + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_UI1: + case VT_I1: + mem = new Memory(1); + mem.setByte(0, ((Number) arg).byteValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_UI2: + case VT_I2: + mem = new Memory(2); + mem.setShort(0, ((Number) arg).shortValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_UI4: + case VT_UINT: + case VT_I4: + case VT_INT: + mem = new Memory(4); + mem.setInt(0, ((Number) arg).intValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_ERROR: + mem = new Memory(4); + mem.setInt(0, ((Number) arg).intValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_R4: + mem = new Memory(4); + mem.setFloat(0, ((Number) arg).floatValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_R8: + mem = new Memory(8); + mem.setDouble(0, ((Number) arg).doubleValue()); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_DATE: + mem = new Memory(8); + mem.setDouble(0, ((DATE) arg).date); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + break; + case VT_BSTR: + if(arg instanceof String) { + BSTR bstr = OleAuto.INSTANCE.SysAllocString((String) arg); + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, bstr.getPointer()); + OleAuto.INSTANCE.SysFreeString(bstr); + COMUtils.checkRC(hr); + } else { + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((BSTR) arg).getPointer()); + COMUtils.checkRC(hr); + } + break; + case VT_VARIANT: + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((VARIANT) arg).getPointer()); + COMUtils.checkRC(hr); + break; + case VT_UNKNOWN: + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((Unknown) arg).getPointer()); + COMUtils.checkRC(hr); + break; + case VT_DISPATCH: + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((Dispatch) arg).getPointer()); + COMUtils.checkRC(hr); + break; + case VT_CY: + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((CURRENCY) arg).getPointer()); + COMUtils.checkRC(hr); + break; + case VT_DECIMAL: + hr = OleAuto.INSTANCE.SafeArrayPutElement(this, paramIndices, ((DECIMAL) arg).getPointer()); + COMUtils.checkRC(hr); + break; + case VT_RECORD: + default: + throw new IllegalStateException("Can't parse array content - type not supported: " + getVarType().intValue()); + } + } + + /** + * Retrieve the value at the referenced index from the SAFEARRAY. + * + *

The function creates a copy of the value. The values are + * allocated with native functions and need to be freed accordingly.

+ * + * @param indices the index, order follows java/C convention + * @return the variant + */ + public Object getElement(int... indices) { + WinDef.LONG[] paramIndices = new WinDef.LONG[indices.length]; + for (int i = 0; i < indices.length; i++) { + paramIndices[i] = new WinDef.LONG(indices[indices.length - i - 1]); + } + + Object result; + WinNT.HRESULT hr; + Memory mem; + PointerByReference pbr; + switch (getVarType().intValue()) { + case VT_BOOL: + mem = new Memory(2); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getShort(0) != 0; + break; + case VT_UI1: + case VT_I1: + mem = new Memory(1); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getByte(0); + break; + case VT_UI2: + case VT_I2: + mem = new Memory(2); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getShort(0); + break; + case VT_UI4: + case VT_UINT: + case VT_I4: + case VT_INT: + mem = new Memory(4); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getInt(0); + break; + case VT_ERROR: + mem = new Memory(4); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = new SCODE(mem.getInt(0)); + break; + case VT_R4: + mem = new Memory(4); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getFloat(0); + break; + case VT_R8: + mem = new Memory(8); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = mem.getDouble(0); + break; + case VT_DATE: + mem = new Memory(8); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, mem); + COMUtils.checkRC(hr); + result = new DATE(mem.getDouble(0)); + break; + case VT_BSTR: + pbr = new PointerByReference(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, pbr.getPointer()); + COMUtils.checkRC(hr); + BSTR bstr = new BSTR(pbr.getValue()); + result = bstr.getValue(); + OleAuto.INSTANCE.SysFreeString(bstr); + break; + case VT_VARIANT: + VARIANT holder = new Variant.VARIANT(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, holder.getPointer()); + COMUtils.checkRC(hr); + result = holder; + break; + case VT_UNKNOWN: + pbr = new PointerByReference(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, pbr.getPointer()); + COMUtils.checkRC(hr); + result = new Unknown(pbr.getValue()); + break; + case VT_DISPATCH: + pbr = new PointerByReference(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, pbr.getPointer()); + COMUtils.checkRC(hr); + result = new Dispatch(pbr.getValue()); + break; + case VT_CY: + CURRENCY currency = new CURRENCY(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, currency.getPointer()); + COMUtils.checkRC(hr); + result = currency; + break; + case VT_DECIMAL: + DECIMAL decimal = new DECIMAL(); + hr = OleAuto.INSTANCE.SafeArrayGetElement(this, paramIndices, decimal.getPointer()); + COMUtils.checkRC(hr); + result = decimal; + break; + case VT_RECORD: + default: + throw new IllegalStateException("Can't parse array content - type not supported: " + getVarType().intValue()); + } + + return result; + } + + /** + * Retrieve pointer to data element from array. + * + *

+ * Caller is responsible for (un)locking the array via + * {@link OleAuto#SafeArrayLock} and {@link OleAuto#SafeArrayUnlock} or + * the helper methods: {@link SAFEARRAY#lock} and + * {@link SAFEARRAY#unlock}.

+ * + * @param indices the index, order follows java/C convention + * @return the pointer to the data element + */ + public Pointer ptrOfIndex(int... indices) { + WinDef.LONG[] paramIndices = new WinDef.LONG[indices.length]; + for (int i = 0; i < indices.length; i++) { + paramIndices[i] = new WinDef.LONG(indices[indices.length - i - 1]); + } + PointerByReference pbr = new PointerByReference(); + WinNT.HRESULT hr = OleAuto.INSTANCE.SafeArrayPtrOfIndex(this, paramIndices, pbr); + COMUtils.checkRC(hr); + return pbr.getValue(); + } + + /** + * Destroy the underlying SAFEARRAY and free memory + */ + public void destroy() { + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayDestroy(this); + COMUtils.checkRC(res); + } + + /** + * Retrieve lower bound for the selected dimension. + * + *

As in the all the accessor functions, that index is converted to + * java conventions.

+ * + * @param dimension zerobased index + * @return + */ + public int getLBound(int dimension) { + int targetDimension = getDimensionCount() - dimension; + WinDef.LONGByReference bound = new WinDef.LONGByReference(); + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayGetLBound(this, new WinDef.UINT(targetDimension), bound); + COMUtils.checkRC(res); + return bound.getValue().intValue(); + } + + /** + * Retrieve upper bound for the selected dimension. + * + *

As in the all the accessor functions, that index is converted to + * java conventions.

+ * + * @param dimension zerobased index + * @return + */ + public int getUBound(int dimension) { + int targetDimension = getDimensionCount() - dimension; + WinDef.LONGByReference bound = new WinDef.LONGByReference(); + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayGetUBound(this, new WinDef.UINT(targetDimension), bound); + COMUtils.checkRC(res); + return bound.getValue().intValue(); + } + + /** + * Return number of dimensions of the SAFEARRAY + * + * @return + */ + public int getDimensionCount() { + return OleAuto.INSTANCE.SafeArrayGetDim(this).intValue(); + } + + /** + * Lock array and retrieve pointer to data + * + * @return Pointer to arraydata + */ + public Pointer accessData() { + PointerByReference pbr = new PointerByReference(); + WinNT.HRESULT hr = OleAuto.INSTANCE.SafeArrayAccessData(this, pbr); + COMUtils.checkRC(hr); + return pbr.getValue(); + } + + /** + * Unlock array and invalidate the pointer retrieved via + * SafeArrayAccessData + */ + public void unaccessData() { + WinNT.HRESULT hr = OleAuto.INSTANCE.SafeArrayUnaccessData(this); + COMUtils.checkRC(hr); + } + + /** + * Increments the lock count of an array, and places a pointer to the + * array data in pvData of the array descriptor. + */ + public void lock() { + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayLock(this); + COMUtils.checkRC(res); + } + + /** + * Decrements the lock count of an array so it can be freed or resized + */ + public void unlock() { + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayUnlock(this); + COMUtils.checkRC(res); + } + + /** + * Changes the right-most (least significant) bound of the specified + * safe array. + * + * @param cElements + * @param lLbound + */ + public void redim(int cElements, int lLbound) { + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayRedim(this, new OaIdl.SAFEARRAYBOUND(cElements, lLbound)); + COMUtils.checkRC(res); + } + + /** + * Return VARTYPE of the SAFEARRAY + * + * @return + */ + public VARTYPE getVarType() { + WTypes.VARTYPEByReference resultHolder = new WTypes.VARTYPEByReference(); + WinNT.HRESULT res = OleAuto.INSTANCE.SafeArrayGetVartype(this, resultHolder); + COMUtils.checkRC(res); + return resultHolder.getValue(); + } + + /** + * Get size of one element in bytes + * + * @return element size in bytes + */ + public long getElemsize() { + return OleAuto.INSTANCE.SafeArrayGetElemsize(this).longValue(); + } } public static class SAFEARRAYBOUND extends Structure { diff --git a/contrib/platform/src/com/sun/jna/platform/win32/OaIdlUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/OaIdlUtil.java new file mode 100644 index 0000000000..1ce2dc7e7e --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/OaIdlUtil.java @@ -0,0 +1,266 @@ +package com.sun.jna.platform.win32; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.OaIdl.DATE; +import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; +import com.sun.jna.platform.win32.Variant.VARIANT; +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_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_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_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_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.WTypes.BSTR; +import com.sun.jna.platform.win32.WinDef.SCODE; +import java.lang.reflect.Array; + +public abstract class OaIdlUtil { + + /** + * Read SAFEARRAY into a java array. Not all VARTYPEs are supported! + * + *

+ * Supported types:

+ *
    + *
  • VT_BOOL
  • + *
  • VT_UI1
  • + *
  • VT_I1
  • + *
  • VT_UI2
  • + *
  • VT_I2
  • + *
  • VT_UI4
  • + *
  • VT_UINT
  • + *
  • VT_I4
  • + *
  • VT_INT
  • + *
  • VT_ERROR
  • + *
  • VT_R4
  • + *
  • VT_R8
  • + *
  • VT_DATE
  • + *
  • VT_BSTR
  • + *
  • VT_VARIANT (Onle the following VARTYPES): + *
      + *
    • VT_EMPTY (converted to NULL)
    • + *
    • VT_NULL
    • + *
    • VT_BOOL
    • + *
    • VT_UI1
    • + *
    • VT_I1
    • + *
    • VT_UI2
    • + *
    • VT_I2
    • + *
    • VT_UI4
    • + *
    • VT_UINT
    • + *
    • VT_I4
    • + *
    • VT_INT
    • + *
    • VT_ERROR
    • + *
    • VT_R4
    • + *
    • VT_R8
    • + *
    • VT_DATE
    • + *
    • VT_BSTR
    • + *
    + *
  • + *
+ * + * @param sa SAFEARRAY to convert + * @param destruct if true the supplied SAFEARRAY is destroyed, there must + * not be additional locks on the array! + * @return + */ + public static Object toPrimitiveArray(SAFEARRAY sa, boolean destruct) { + Pointer dataPointer = sa.accessData(); + try { + int dimensions = sa.getDimensionCount(); + int[] minIdx = new int[dimensions]; + int[] maxIdx = new int[dimensions]; + int[] elements = new int[dimensions]; + int[] cumElements = new int[dimensions]; + int varType = sa.getVarType().intValue(); + + for (int i = 0; i < dimensions; i++) { + minIdx[i] = sa.getLBound(i); + maxIdx[i] = sa.getUBound(i); + elements[i] = maxIdx[i] - minIdx[i] + 1; + } + + for (int i = dimensions - 1; i >= 0; i--) { + if (i == (dimensions - 1)) { + cumElements[i] = 1; + } else { + cumElements[i] = cumElements[i + 1] * elements[i + 1]; + } + } + + if (dimensions == 0) { + throw new IllegalArgumentException("Supplied Array has no dimensions."); + } + + int elementCount = cumElements[0] * elements[0]; + + Object sourceArray; + switch (varType) { + case VT_UI1: + case VT_I1: + sourceArray = dataPointer.getByteArray(0, elementCount); + break; + case VT_BOOL: + case VT_UI2: + case VT_I2: + sourceArray = dataPointer.getShortArray(0, elementCount); + break; + case VT_UI4: + case VT_UINT: + case VT_I4: + case VT_INT: + case VT_ERROR: + sourceArray = dataPointer.getIntArray(0, elementCount); + break; + case VT_R4: + sourceArray = dataPointer.getFloatArray(0, elementCount); + break; + case VT_R8: + case VT_DATE: + sourceArray = dataPointer.getDoubleArray(0, elementCount); + break; + case VT_BSTR: + sourceArray = dataPointer.getPointerArray(0, elementCount); + break; + case VT_VARIANT: + VARIANT variant = new VARIANT(dataPointer); + sourceArray = variant.toArray(elementCount); + break; + case VT_UNKNOWN: + case VT_DISPATCH: + case VT_CY: + case VT_DECIMAL: + case VT_RECORD: + default: + throw new IllegalStateException("Type not supported: " + varType); + } + + Object targetArray = Array.newInstance(Object.class, elements); + toPrimitiveArray(sourceArray, targetArray, minIdx, maxIdx, elements, cumElements, varType, new int[0]); + return targetArray; + } finally { + sa.unaccessData(); + if (destruct) { + sa.destroy(); + } + } + } + + private static void toPrimitiveArray(Object dataArray, Object targetArray, int[] minIdx, int[] maxIdx, int[] elements, int[] cumElements, int varType, int[] currentIdx) { + int dimIdx = currentIdx.length; + int[] subIdx = new int[currentIdx.length + 1]; + System.arraycopy(currentIdx, 0, subIdx, 0, dimIdx); + for (int i = minIdx[dimIdx]; i <= maxIdx[dimIdx]; i++) { + subIdx[dimIdx] = i; + if (dimIdx == (minIdx.length - 1)) { + int offset = 0; + for (int j = 0; j < dimIdx; j++) { + offset += cumElements[j] * currentIdx[j]; + } + offset += subIdx[dimIdx] - minIdx[dimIdx]; + int targetPos = subIdx[dimIdx] - minIdx[dimIdx]; + switch (varType) { + case VT_BOOL: + Array.set(targetArray, targetPos, Array.getShort(dataArray, offset) != 0); + break; + case VT_UI1: + case VT_I1: + Array.set(targetArray, targetPos, Array.getByte(dataArray, offset)); + break; + case VT_UI2: + case VT_I2: + Array.set(targetArray, targetPos, Array.getShort(dataArray, offset)); + break; + case VT_UI4: + case VT_UINT: + case VT_I4: + case VT_INT: + Array.set(targetArray, targetPos, Array.getInt(dataArray, offset)); + break; + case VT_ERROR: + Array.set(targetArray, targetPos, new SCODE(Array.getInt(dataArray, offset))); + break; + case VT_R4: + Array.set(targetArray, targetPos, Array.getFloat(dataArray, offset)); + break; + case VT_R8: + Array.set(targetArray, targetPos, Array.getDouble(dataArray, offset)); + break; + case VT_DATE: + Array.set(targetArray, targetPos, new DATE(Array.getDouble(dataArray, offset)).getAsJavaDate()); + break; + case VT_BSTR: + Array.set(targetArray, targetPos, new BSTR((Pointer) Array.get(dataArray, offset)).getValue()); + break; + case VT_VARIANT: + VARIANT holder = (VARIANT) Array.get(dataArray, offset); + switch (holder.getVarType().intValue()) { + case VT_NULL: + case VT_EMPTY: + Array.set(targetArray, targetPos, null); + break; + case VT_BOOL: + Array.set(targetArray, targetPos, holder.booleanValue()); + break; + case VT_UI1: + case VT_I1: + Array.set(targetArray, targetPos, holder.byteValue()); + break; + case VT_UI2: + case VT_I2: + Array.set(targetArray, targetPos, holder.shortValue()); + break; + case VT_UI4: + case VT_UINT: + case VT_I4: + case VT_INT: + Array.set(targetArray, targetPos, holder.intValue()); + break; + case VT_ERROR: + Array.set(targetArray, targetPos, new SCODE(holder.intValue())); + break; + case VT_R4: + Array.set(targetArray, targetPos, holder.floatValue()); + break; + case VT_R8: + Array.set(targetArray, targetPos, holder.doubleValue()); + break; + case VT_DATE: + Array.set(targetArray, targetPos, holder.dateValue()); + break; + case VT_BSTR: + Array.set(targetArray, targetPos, holder.stringValue()); + break; + default: + throw new IllegalStateException("Type not supported: " + holder.getVarType().intValue()); + } + break; + case VT_UNKNOWN: + case VT_DISPATCH: + case VT_CY: + case VT_DECIMAL: + case VT_RECORD: + default: + throw new IllegalStateException("Type not supported: " + varType); + } + } else { + toPrimitiveArray(dataArray, Array.get(targetArray, i), minIdx, maxIdx, elements, cumElements, varType, subIdx); + } + } + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java b/contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java index d602890e87..f6e71c41b4 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/OleAuto.java @@ -26,8 +26,10 @@ import com.sun.jna.platform.win32.Variant.VariantArg; import com.sun.jna.platform.win32.WTypes.BSTR; import com.sun.jna.platform.win32.WTypes.VARTYPE; +import com.sun.jna.platform.win32.WTypes.VARTYPEByReference; import com.sun.jna.platform.win32.WinBase.SYSTEMTIME; import com.sun.jna.platform.win32.WinDef.LCID; +import com.sun.jna.platform.win32.WinDef.LONG; import com.sun.jna.platform.win32.WinDef.PVOID; import com.sun.jna.platform.win32.WinDef.UINT; import com.sun.jna.platform.win32.WinNT.HRESULT; @@ -36,7 +38,6 @@ import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; -// TODO: Auto-generated Javadoc /** * Oleaut32.dll Interface. * @@ -209,7 +210,7 @@ public interface OleAuto extends StdCallLibrary { * [in, out] The variant to clear. * @return the hresult */ - HRESULT VariantClear(Pointer pvarg); + HRESULT VariantClear(VARIANT pvarg); /** * Creates a new array descriptor, allocates and initializes the data for @@ -223,7 +224,7 @@ public interface OleAuto extends StdCallLibrary { * array. All other types are legal. cDims * * @param cDims - * the c dims + * the number of dims * @param rgsabound * the rgsabound * @@ -232,7 +233,7 @@ public interface OleAuto extends StdCallLibrary { * A safe array descriptor, or null if the array could not be * created. */ - SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, int cDims, + SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, UINT cDims, SAFEARRAYBOUND[] rgsabound); /** @@ -250,19 +251,65 @@ SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, int cDims, * * This function can return one of these values. * - * S_OK Success. + *
+ *
S_OK
Success.
+ *
DISP_E_BADINDEX
The specified index is not valid.
+ *
E_INVALIDARG
One of the arguments is not valid.
+ *
E_OUTOFMEMORY
Memory could not be allocated for the element.
+ *
+ */ + HRESULT SafeArrayPutElement(SAFEARRAY psa, LONG[] idx, Pointer pv); + + /** + * Retrieve the upper bound for the specified dimension of the supplied array * - * DISP_E_BADINDEX The specified index is not valid. + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * @param nDim + * [in] the dimension, one based + * @param bound + * [out] upper bound for the supplied dimension + * + * @return Return value * - * E_INVALIDARG One of the arguments is not valid. + * This function can return one of these values. * - * E_OUTOFMEMORY Memory could not be allocated for the element. + *
+ *
S_OK
Success.
+ *
DISP_E_BADINDEX
The specified index is not valid.
+ *
E_INVALIDARG
One of the arguments is not valid.
+ *
*/ - HRESULT SafeArrayPutElement(SAFEARRAY psa, long[] idx, VARIANT pv); - + HRESULT SafeArrayGetUBound(SAFEARRAY psa, UINT nDim, WinDef.LONGByReference bound); + + /** + * Retrieve the lower bound for the specified dimension of the supplied array + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * @param nDim + * [in] the dimension, one based + * @param bound + * [out] lower bound for the supplied dimension + * + * @return Return value + * + * This function can return one of these values. + * + *
+ *
S_OK
Success.
+ *
DISP_E_BADINDEX
The specified index is not valid.
+ *
E_INVALIDARG
One of the arguments is not valid.
+ *
+ */ + HRESULT SafeArrayGetLBound(SAFEARRAY psa, UINT nDim, WinDef.LONGByReference bound); + + /** * Retrieves a single element of the array. * + * The array is automaticly locked via SafeArrayLock and SafeArrayUnlock. + * * @param psa * [in] An array descriptor created by SafeArrayCreate. * @param rgIndices @@ -276,16 +323,41 @@ SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, int cDims, * * This function can return one of these values. * - * S_OK Success. + *
+ *
S_OK
Success.
+ *
DISP_E_BADINDEX
The specified index is not valid.
+ *
E_INVALIDARG
One of the arguments is not valid.
+ *
E_OUTOFMEMORY
Memory could not be allocated for the element.
+ *
+ */ + HRESULT SafeArrayGetElement(SAFEARRAY psa, LONG[] rgIndices, Pointer pv); + + /** + * Retrieves the pointer to a single element of the array. + * + *

The caller is responsible for locking.

* - * DISP_E_BADINDEX The specified index is not valid. + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * @param rgIndices + * [in] A vector of indexes for each dimension of the array. The + * right-most (least significant) dimension is rgIndices[0]. The + * left-most dimension is stored at rgIndices[psa->cDims - 1]. + * @param ppv + * [out] The element of the array. * - * E_INVALIDARG One of the arguments is not valid. + * @return Return value + * + * This function can return one of these values. * - * E_OUTOFMEMORY Memory could not be allocated for the element. + *
+ *
S_OK
Success.
+ *
DISP_E_BADINDEX
The specified index is not valid.
+ *
E_INVALIDARG
One of the arguments is not valid.
+ *
*/ - HRESULT SafeArrayGetElement(SAFEARRAY psa, long[] rgIndices, Pointer pv); - + HRESULT SafeArrayPtrOfIndex(SAFEARRAY psa, LONG[] rgIndices, PointerByReference ppv); + /** * Increments the lock count of an array, and places a pointer to the array * data in pvData of the array descriptor. @@ -297,11 +369,11 @@ SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, int cDims, * * This function can return one of these values. * - * S_OK Success. - * - * E_INVALIDARG The argument psa is not valid. - * - * E_UNEXPECTED The array could not be locked. + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
E_UNEXPECTED
The array could not be locked.
+ *
*/ HRESULT SafeArrayLock(SAFEARRAY psa); @@ -315,14 +387,131 @@ SAFEARRAY.ByReference SafeArrayCreate(VARTYPE vt, int cDims, * * This function can return one of these values. * - * S_OK Success. + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
E_UNEXPECTED
The array could not be locked.
+ *
+ */ + HRESULT SafeArrayUnlock(SAFEARRAY psa); + + /** + * Destroys an existing array descriptor and all of the data in the array. + * If objects are stored in the array, Release is called on each object + * in the array. * - * E_INVALIDARG The argument psa is not valid. + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * + * @return Return value + * + * This function can return one of these values. * - * E_UNEXPECTED The array could not be locked. + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
DISP_E_ARRAYISLOCKED
The array could not be locked.
+ *
*/ - HRESULT SafeArrayUnLock(SAFEARRAY psa); - + HRESULT SafeArrayDestroy(SAFEARRAY psa); + + /** + * Changes the right-most (least significant) bound of the specified safe array. + * + * @param psa + * [in, out] An array descriptor created by SafeArrayCreate. + * @param psaboundNew + * [in] New bounds for the least significant dimension + * + * @return Return value + * + * This function can return one of these values. + * + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
DISP_E_ARRAYISLOCKED
The array could not be locked.
+ *
+ */ + HRESULT SafeArrayRedim(SAFEARRAY psa, SAFEARRAYBOUND psaboundNew); + + /** + * Return VARTYPE of the SAFEARRAY + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * @param pvt + * [in] Vartype of the SAFEARRAY + * + * @return Return value + * + * This function can return one of these values. + * + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
+ */ + HRESULT SafeArrayGetVartype(SAFEARRAY psa, VARTYPEByReference pvt); + + /** + * Return number of dimensions of the SAFEARRAY + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * + * @return Return count of dimensions + */ + UINT SafeArrayGetDim(SAFEARRAY psa); + + /** + * Lock array and retrieve pointer to data + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * @param ppvData + * [in] pointer to the data array + * + * @return Return value + * + * This function can return one of these values. + * + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
E_UNEXPECTED
The array could not be locked.
+ *
+ */ + HRESULT SafeArrayAccessData(SAFEARRAY psa, PointerByReference ppvData); + + /** + * Unlock array and invalidate the pointer retrieved via SafeArrayAccessData + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * + * @return Return value + * + * This function can return one of these values. + * + *
+ *
S_OK
Success.
+ *
E_INVALIDARG
The argument psa is not valid.
+ *
E_UNEXPECTED
The array could not be locked.
+ *
+ */ + HRESULT SafeArrayUnaccessData(SAFEARRAY psa); + + /** + * Get size of one element in bytes + * + * @param psa + * [in] An array descriptor created by SafeArrayCreate. + * + * @return size in bytes + */ + UINT SafeArrayGetElemsize(SAFEARRAY psa); + /** * Retrieves a pointer to a running object that has been registered with * OLE. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/OleAutoUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/OleAutoUtil.java deleted file mode 100644 index a5c72f1298..0000000000 --- a/contrib/platform/src/com/sun/jna/platform/win32/OleAutoUtil.java +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2012 Tobias Wolf, All Rights Reserved - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ -package com.sun.jna.platform.win32; - -import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; -import com.sun.jna.platform.win32.OaIdl.SAFEARRAYBOUND; -import com.sun.jna.platform.win32.Variant.VARIANT; -import com.sun.jna.platform.win32.WTypes.VARTYPE; -import com.sun.jna.platform.win32.WinNT.HRESULT; -import com.sun.jna.platform.win32.COM.COMUtils; - -// TODO: Auto-generated Javadoc -/** - * The Class OleAut32Util. - * - * @author Tobias Wolf, wolf.tobias@gmx.net - */ -public abstract class OleAutoUtil { - - /** - * Creates a new the variant array. - * - * @param size the size - * @return the sAFEARRA y. by reference - */ - public static SAFEARRAY.ByReference createVarArray(int size) { - SAFEARRAY.ByReference psa; - SAFEARRAYBOUND[] rgsabound = new SAFEARRAYBOUND[1]; - rgsabound[0] = new SAFEARRAYBOUND(size, 0); - - psa = OleAuto.INSTANCE.SafeArrayCreate( - new VARTYPE(Variant.VT_VARIANT), 1, rgsabound); - - return psa; - } - - /** - * Safe array put element. - * - * @param array the array - * @param index the index - * @param arg the arg - */ - public static void SafeArrayPutElement(SAFEARRAY array, long index, - VARIANT arg) { - long[] idx = new long[1]; - idx[0] = index; - HRESULT hr = OleAuto.INSTANCE.SafeArrayPutElement(array, idx, arg); - COMUtils.SUCCEEDED(hr); - } - - /** - * Safe array get element. - * - * @param array the array - * @param index the index - * @return the variant - */ - public static VARIANT SafeArrayGetElement(SAFEARRAY array, long index) { - long[] idx = new long[1]; - idx[0] = index; - VARIANT result = new VARIANT(); - HRESULT hr = OleAuto.INSTANCE.SafeArrayGetElement(array, idx, - result.getPointer()); - COMUtils.SUCCEEDED(hr); - return result; - } -} 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 e6f8c790a3..891ac46511 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Variant.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Variant.java @@ -1,5 +1,6 @@ package com.sun.jna.platform.win32; +import com.sun.jna.IntegerType; import java.util.Date; import java.util.List; @@ -103,12 +104,14 @@ public interface Variant { public static VARIANT_BOOL VARIANT_TRUE = new VARIANT_BOOL(0xFFFF); public static VARIANT_BOOL VARIANT_FALSE = new VARIANT_BOOL(0x0000); + @Deprecated public final static long COM_DAYS_ADJUSTMENT = 25569L; // ((1969 - 1899) * // 365) +1 + Leap - // years = Days + // years = Days + @Deprecated public final static long MICRO_SECONDS_PER_DAY = 86400000L; // 24L * 60L * // 60L * 1000L; - + public static class VARIANT extends Union { public static class ByReference extends VARIANT implements @@ -141,6 +144,13 @@ public ByValue() { } } + public static final VARIANT VARIANT_MISSING; + + static { + VARIANT_MISSING = new VARIANT(); + VARIANT_MISSING.setValue(VT_ERROR, new SCODE(WinError.DISP_E_PARAMNOTFOUND)); + } + public _VARIANT _variant; public DECIMAL decVal; @@ -168,12 +178,11 @@ public VARIANT(BSTRByReference value) { public VARIANT(VARIANT_BOOL value) { this(); - this.setValue(VT_BOOL, new BOOL(value.intValue())); + this.setValue(VT_BOOL, value); } public VARIANT(BOOL value) { - this(); - this.setValue(VT_BOOL, value); + this(value.booleanValue()); } public VARIANT(LONG value) { @@ -201,9 +210,10 @@ public VARIANT(BYTE value) { } public VARIANT(char value) { - this(new CHAR(value)); + this(); + this.setValue(VT_UI2, new USHORT(value)); } - + public VARIANT(CHAR value) { this(); this.setValue(Variant.VT_I1, value); @@ -213,7 +223,7 @@ public VARIANT(short value) { this(); this.setValue(VT_I2, new SHORT(value)); } - + public VARIANT(int value) { this(); this.setValue(VT_I4, new LONG(value)); @@ -252,10 +262,7 @@ public VARIANT(String value) { public VARIANT(boolean value) { this(); - if (value) - this.setValue(VT_BOOL, new BOOL(VARIANT_TRUE.intValue())); - else - this.setValue(VT_BOOL, new BOOL(VARIANT_FALSE.intValue())); + this.setValue(VT_BOOL, new VARIANT_BOOL(value)); } public VARIANT(IDispatch value) { @@ -283,7 +290,8 @@ public void setValue(int vt, Object value) { } public void setValue(VARTYPE vt, Object value) { - switch (vt.intValue()) { + int varType = vt.intValue(); + switch (varType) { case VT_UI1: this._variant.__variant.writeField("bVal", value); break; @@ -323,12 +331,6 @@ public void setValue(VARTYPE vt, Object value) { case VT_DISPATCH: this._variant.__variant.writeField("pdispVal", value); break; - case VT_SAFEARRAY: - this._variant.__variant.writeField("parray", value); - break; - case VT_ARRAY: - this._variant.__variant.writeField("parray", value); - break; case VT_BYREF | VT_UI1: this._variant.__variant.writeField("pbVal", value); break; @@ -368,9 +370,6 @@ public void setValue(VARTYPE vt, Object value) { case VT_BYREF | VT_DISPATCH: this._variant.__variant.writeField("ppdispVal", value); break; - case VT_BYREF | VT_ARRAY: - this._variant.__variant.writeField("pparray", value); - break; case VT_BYREF | VT_VARIANT: this._variant.__variant.writeField("pvarVal", value); break; @@ -419,6 +418,14 @@ public void setValue(VARTYPE vt, Object value) { case VT_RECORD: this._variant.__variant.writeField("pvRecord", value); break; + default: + if ((varType & VT_ARRAY) > 0 || (varType & VT_SAFEARRAY) > 0) { + if ((varType & VT_BYREF) > 0) { + this._variant.__variant.writeField("pparray", value); + } else { + this._variant.__variant.writeField("parray", value); + } + } } this._variant.writeField("vt", vt); @@ -427,7 +434,10 @@ public void setValue(VARTYPE vt, Object value) { public Object getValue() { this.read(); + int varType = this.getVarType().intValue(); switch (this.getVarType().intValue()) { + case VT_UI1: + return this._variant.__variant.readField("bVal"); case VT_I2: return this._variant.__variant.readField("iVal"); case VT_I4: @@ -452,10 +462,6 @@ public Object getValue() { return this._variant.__variant.readField("punkVal"); case VT_DISPATCH: return this._variant.__variant.readField("pdispVal"); - case VT_SAFEARRAY: - return this._variant.__variant.readField("parray"); - case VT_ARRAY: - return this._variant.__variant.readField("parray"); case VT_BYREF | VT_UI1: return this._variant.__variant.readField("pbVal"); case VT_BYREF | VT_I2: @@ -482,8 +488,6 @@ public Object getValue() { return this._variant.__variant.readField("ppunkVal"); case VT_BYREF | VT_DISPATCH: return this._variant.__variant.readField("ppdispVal"); - case VT_BYREF | VT_ARRAY: - return this._variant.__variant.readField("pparray"); case VT_BYREF | VT_VARIANT: return this._variant.__variant.readField("pvarVal"); case VT_BYREF: @@ -517,20 +521,31 @@ public Object getValue() { case VT_RECORD: return this._variant.__variant.readField("pvRecord"); default: + if((varType & VT_ARRAY) > 0 || ((varType & VT_SAFEARRAY) > 0)) { + if((varType & VT_BYREF) > 0) { + return this._variant.__variant.readField("pparray"); + } else { + return this._variant.__variant.readField("parray"); + } + } return null; } } - public int shortValue() { - return (Short) this.getValue(); + public byte byteValue() { + return ((IntegerType) this.getValue()).byteValue(); + } + + public short shortValue() { + return ((IntegerType) this.getValue()).shortValue(); } public int intValue() { - return (Integer) this.getValue(); + return ((IntegerType) this.getValue()).intValue(); } public long longValue() { - return (Long) this.getValue(); + return ((IntegerType) this.getValue()).longValue(); } public float floatValue() { @@ -547,36 +562,23 @@ public String stringValue() { } public boolean booleanValue() { - return (Boolean) this.getValue(); + // getValue returns a VARIANT_BOOL + return ((VARIANT_BOOL) this.getValue()).booleanValue(); } public Date dateValue() { DATE varDate = (DATE) this.getValue(); - return this.toJavaDate(varDate); + return varDate.getAsJavaDate(); } + @Deprecated protected Date toJavaDate(DATE varDate) { - - double doubleDate = varDate.date; - long longDate = (long) doubleDate; - - double doubleTime = doubleDate - longDate; - long longTime = (long) doubleTime * MICRO_SECONDS_PER_DAY; - - return new Date( - ((longDate - COM_DAYS_ADJUSTMENT) * MICRO_SECONDS_PER_DAY) - + longTime); + return varDate.getAsJavaDate(); } + @Deprecated protected DATE fromJavaDate(Date javaDate) { - long longTime = javaDate.getTime() % MICRO_SECONDS_PER_DAY; - long longDate = ((javaDate.getTime() - longTime) / MICRO_SECONDS_PER_DAY) - + COM_DAYS_ADJUSTMENT; - - float floatTime = ((float) longTime) - / ((float) MICRO_SECONDS_PER_DAY); - float floatDateTime = floatTime + longDate; - return new DATE(floatDateTime); + return new DATE(javaDate); } public static class _VARIANT extends Structure { @@ -621,7 +623,7 @@ protected List getFieldOrder() { // DOUBLE VT_R8 public Double dblVal; // VARIANT_BOOL VT_BOOL - public BOOL boolVal; + public VARIANT_BOOL boolVal; // SCODE VT_ERROR public SCODE scode; // CY VT_CY diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WTypes.java b/contrib/platform/src/com/sun/jna/platform/win32/WTypes.java index 247dd09aa0..7f8762f739 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WTypes.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WTypes.java @@ -243,4 +243,28 @@ public VARTYPE(int value) { super(value); } } + + public static class VARTYPEByReference extends ByReference { + public VARTYPEByReference() { + super(VARTYPE.SIZE); + } + + public VARTYPEByReference(VARTYPE type) { + super(VARTYPE.SIZE); + setValue(type); + } + + public VARTYPEByReference(short type) { + super(VARTYPE.SIZE); + getPointer().setShort(0, type); + } + + public void setValue(VARTYPE value) { + getPointer().setShort(0, value.shortValue()); + } + + public VARTYPE getValue() { + return new VARTYPE(getPointer().getShort(0)); + } + } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinDef.java b/contrib/platform/src/com/sun/jna/platform/win32/WinDef.java index e38674fa59..ccae0a0ef5 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinDef.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinDef.java @@ -1404,6 +1404,7 @@ public BOOL(boolean value) { */ public BOOL(long value) { super(SIZE, value, false); + assert value == 0 || value == 1; } public boolean booleanValue() { @@ -1597,7 +1598,7 @@ public CHAR() { * * @param ch The {@code char} value */ - public CHAR(char ch) { + public CHAR(byte ch) { this(ch & 0xFF); } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/OleAutoTest.java b/contrib/platform/test/com/sun/jna/platform/win32/OleAutoTest.java index e37d447d62..1f8f758c38 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/OleAutoTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/OleAutoTest.java @@ -16,8 +16,6 @@ import com.sun.jna.WString; import com.sun.jna.platform.win32.Guid.CLSID; -import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; -import com.sun.jna.platform.win32.Variant.VARIANT; import com.sun.jna.platform.win32.WTypes.BSTR; import com.sun.jna.platform.win32.WinDef.LCID; import com.sun.jna.platform.win32.WinNT.HRESULT; @@ -29,47 +27,39 @@ */ public class OleAutoTest extends TestCase { - public static void main(String[] args) { - junit.textui.TestRunner.run(OleAutoTest.class); - } + public static void main(String[] args) { + junit.textui.TestRunner.run(OleAutoTest.class); + } - public OleAutoTest() { - } + public OleAutoTest() { + } - public void testSysAllocString() { - assertEquals(null, OleAuto.INSTANCE.SysAllocString(null)); - BSTR p = OleAuto.INSTANCE.SysAllocString("hello world"); - assertEquals("hello world", p.getValue()); - OleAuto.INSTANCE.SysFreeString(p); - } + public void testSysAllocString() { + assertEquals(null, OleAuto.INSTANCE.SysAllocString(null)); + BSTR p = OleAuto.INSTANCE.SysAllocString("hello world"); + assertEquals("hello world", p.getValue()); + OleAuto.INSTANCE.SysFreeString(p); + } - public void testSysFreeString() { - OleAuto.INSTANCE.SysFreeString(null); - } + public void testSysFreeString() { + OleAuto.INSTANCE.SysFreeString(null); + } - public void testDISPPARAMS() { - // Build DISPPARAMS - SAFEARRAY.ByReference safeArg = OleAutoUtil.createVarArray(1); - OleAutoUtil.SafeArrayPutElement(safeArg, 0, new VARIANT( - Variant.VARIANT_TRUE)); - //System.out.println(safeArg.toString(true)); - } + public void testLoadRegTypeLib() { + CLSID.ByReference clsid = new CLSID.ByReference(); + // get CLSID from string, Microsoft Scripting Engine + HRESULT hr = Ole32.INSTANCE.CLSIDFromString(new WString( + "{420B2830-E718-11CF-893D-00A0C9054228}"), clsid); + COMUtils.checkRC(hr); + assertEquals(0, hr.intValue()); - public void testLoadRegTypeLib() { - CLSID.ByReference clsid = new CLSID.ByReference(); - // get CLSID from string, Microsoft Scripting Engine - HRESULT hr = Ole32.INSTANCE.CLSIDFromString(new WString( - "{420B2830-E718-11CF-893D-00A0C9054228}"), clsid); - COMUtils.checkRC(hr); - assertEquals(0, hr.intValue()); - - // get user default lcid - LCID lcid = Kernel32.INSTANCE.GetUserDefaultLCID(); - PointerByReference pWordTypeLib = new PointerByReference(); - // get typelib version 1.0 - hr = OleAuto.INSTANCE.LoadRegTypeLib(clsid, 1, 0, lcid, pWordTypeLib); - COMUtils.checkRC(hr); - assertEquals(0, hr.intValue()); - } + // get user default lcid + LCID lcid = Kernel32.INSTANCE.GetUserDefaultLCID(); + PointerByReference pWordTypeLib = new PointerByReference(); + // get typelib version 1.0 + hr = OleAuto.INSTANCE.LoadRegTypeLib(clsid, 1, 0, lcid, pWordTypeLib); + COMUtils.checkRC(hr); + assertEquals(0, hr.intValue()); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/OleAutoUtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/OleAutoUtilTest.java deleted file mode 100644 index 62d4ae9deb..0000000000 --- a/contrib/platform/test/com/sun/jna/platform/win32/OleAutoUtilTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2013 Tobias Wolf, All Rights Reserved - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ -package com.sun.jna.platform.win32; - -import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; -import com.sun.jna.platform.win32.Variant.VARIANT; -import com.sun.jna.platform.win32.WinDef.SHORT; - -import junit.framework.TestCase; - -/** - * @author Tobias Wolf, wolf.tobias@gmx.net - */ -public class OleAutoUtilTest extends TestCase { - - public static void main(String[] args) { - junit.textui.TestRunner.run(OleAutoUtilTest.class); - } - - public void testCreateVarArray() { - SAFEARRAY varArray = OleAutoUtil.createVarArray(1); - assertTrue(varArray != null); - } - - public void testSafeArrayPutGetElement() throws Exception { - SAFEARRAY varArray = OleAutoUtil.createVarArray(10); - - for (int i = 0; i < 10; i++) { - VARIANT variant = new VARIANT(new SHORT(i + i*100)); - //System.out.println(variant.toString(true)); - OleAutoUtil.SafeArrayPutElement(varArray, i, variant); - } - - assertTrue(varArray != null); - - for (int i = 0; i < 10; i++) { - VARIANT element = OleAutoUtil.SafeArrayGetElement(varArray, i); - /* - System.out.println(element.toString(true)); - System.out.println("variant type: " + element.getVarType()); - System.out.println("value: " + element.getValue()); - */ - } - } -} diff --git a/contrib/platform/test/com/sun/jna/platform/win32/SAFEARRAYTest.java b/contrib/platform/test/com/sun/jna/platform/win32/SAFEARRAYTest.java new file mode 100644 index 0000000000..426a1d1aac --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/SAFEARRAYTest.java @@ -0,0 +1,833 @@ +/* Copyright (c) 2013 Tobias Wolf, All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package com.sun.jna.platform.win32; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.COMException; +import com.sun.jna.platform.win32.COM.COMUtils; +import com.sun.jna.platform.win32.COM.util.Factory; +import com.sun.jna.platform.win32.COM.util.IComEnum; +import com.sun.jna.platform.win32.COM.util.IConnectionPoint; +import com.sun.jna.platform.win32.COM.util.IUnknown; +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.DATE; +import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; +import static com.sun.jna.platform.win32.OaIdlUtil.toPrimitiveArray; +import com.sun.jna.platform.win32.Variant.VARIANT; +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_DATE; +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_INT; +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_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_UINT; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WinDef.LONGByReference; +import com.sun.jna.platform.win32.WinDef.SCODE; +import com.sun.jna.platform.win32.WinDef.UINT; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import static junit.framework.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import org.junit.After; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import org.junit.Before; + +public class SAFEARRAYTest { + static { + ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); + } + + @Before + public void setup() { + Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); + } + + @After + public void teardown() { + Ole32.INSTANCE.CoUninitialize(); + } + + @Test + public void testCreateVarArray() { + SAFEARRAY varArray = SAFEARRAY.createSafeArray(1); + Assert.assertTrue(varArray != null); + } + + @Test + public void testSafeArrayPutGetElement() throws Exception { + int rowCount = 2; + int colCount = 10; + + SAFEARRAY varArray = SAFEARRAY.createSafeArray(rowCount, colCount); + + assertThat(varArray.getDimensionCount(), is(2)); + + assertThat(varArray.getUBound(0), equalTo(rowCount - 1)); + assertThat(varArray.getUBound(1), equalTo(colCount - 1)); + + for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { + for (int colIdx = 0; colIdx < colCount; colIdx++) { + VARIANT variant = new VARIANT(rowIdx + "#" + colIdx); + varArray.putElement(variant, rowIdx, colIdx); + } + } + + for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { + for (int colIdx = 0; colIdx < colCount; colIdx++) { + VARIANT element = (VARIANT) varArray.getElement(rowIdx, colIdx); + assertEquals(rowIdx + "#" + colIdx, element.stringValue()); + OleAuto.INSTANCE.VariantClear(element); + } + } + } + + @Ignore("Only for live testing") + @Test + public void testPerformance() { + Factory fact = new Factory(); + + // Open a record set with a sample search (basicly get the first five + // entries from the search index + Connection conn = fact.createObject(Connection.class); + conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1); + + Recordset recordset = fact.createObject(Recordset.class); + recordset.Open("SELECT TOP 500 System.ItemPathDisplay, System.ItemName, System.ItemUrl, System.DateCreated FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); + + SAFEARRAY wrap = recordset.GetRows(); + + assertThat(wrap.getDimensionCount(), is(2)); + + long timeDirect = 0; + long timeGetElement = 0; + long timePointer = 0; + long timeHelper = 0; + + long start, end; + + for (int i = 0; i < 4 * 10; i++) { + if (i % 4 == 0) { + start = System.currentTimeMillis(); + toArrayPtrToElement(wrap); + end = System.currentTimeMillis(); + timePointer += (end - start); + } else if (i % 4 == 1) { + start = System.currentTimeMillis(); + toArrayGetElement(wrap); + end = System.currentTimeMillis(); + timeGetElement += (end - start); + } else if (i % 4 == 2) { + start = System.currentTimeMillis(); + toArrayDirect(wrap); + end = System.currentTimeMillis(); + timeDirect += (end - start); + } else if (i % 4 == 3) { + start = System.currentTimeMillis(); + OaIdlUtil.toPrimitiveArray(wrap, false); + end = System.currentTimeMillis(); + timeHelper += (end - start); + } + } + + System.out.println("Direct: " + timeDirect + " ms"); + System.out.println("GetElement: " + timeGetElement + " ms"); + System.out.println("Pointer: " + timePointer + " ms"); + System.out.println("Helper: " + timeHelper + " ms"); + + recordset.Close(); + conn.Close(); + + fact.disposeAll(); + } + + private Object[] toArrayGetElement(SAFEARRAY wrap) { + wrap.lock(); + int rowMax = wrap.getUBound(2); + int columnMax = wrap.getUBound(1); + Object[][] result = new Object[(int) (rowMax + 1)][(int) (columnMax + 1)]; + for(int i = 0; i <= rowMax; i++) { + for(int j = 0; j <= columnMax; j++) { + VARIANT cell = (VARIANT) wrap.getElement(i, j); + result[(int)i][(int) j] = cell.getValue(); + OleAuto.INSTANCE.VariantClear(cell); + } + } + wrap.unlock(); + return result; + } + + private Object[] toArrayPtrToElement(SAFEARRAY wrap) { + wrap.lock(); + int rowMax = wrap.getUBound(2); + int columnMax = wrap.getUBound(1); + Object[][] result = new Object[(int) (rowMax + 1)][(int) (columnMax + 1)]; + for(int i = 0; i <= rowMax; i++) { + for(int j = 0; j <= columnMax; j++) { + VARIANT cell = new VARIANT(wrap.ptrOfIndex(i, j)); + result[(int)i][(int) j] = cell.getValue(); + } + } + wrap.unlock(); + return result; + } + + private Object[] toArrayDirect(SAFEARRAY wrap) { + Pointer dataPointer = wrap.accessData(); + long rowMax = wrap.getUBound(2); + long columnMax = wrap.getUBound(1); + VARIANT[] variantData = (VARIANT[]) new VARIANT(dataPointer).toArray((int) ((rowMax + 1) * (columnMax + 1))); + Object[][] result = new Object[(int) (rowMax + 1)][(int) (columnMax + 1)]; + for(long i = 0; i <= rowMax; i++) { + long rowOffset = i * columnMax; + for(long j = 0; j <= columnMax; j++) { + VARIANT cell = variantData[(int) (rowOffset + j)]; + result[(int)i][(int) j] = cell.getValue(); + } + } + wrap.unaccessData(); + return result; + } + + @Test + public void testDataTypes() { + int idx = 1; + Pointer dataPointer; + SAFEARRAY sa; + long elementSize; + + Object[] objectResult; + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BOOL), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(2L)); + dataPointer = sa.accessData(); + sa.putElement(true, idx); + short[] shortResult = dataPointer.getShortArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Boolean) sa.getElement(idx), equalTo(true)); + assertThat(shortResult[idx], equalTo((short) 0xFFFF)); + assertThat((Short) dataPointer.getShort(idx * elementSize), equalTo((short) 0xFFFF)); + assertThat((Boolean) objectResult[idx], equalTo(true)); + sa.unaccessData(); + sa.destroy(); + + byte testByte = 67; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI1), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(1L)); + dataPointer = sa.accessData(); + sa.putElement(testByte, idx); + byte[] byteResult = dataPointer.getByteArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Byte) sa.getElement(idx), equalTo(testByte)); + assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte)); + assertThat(byteResult[idx], equalTo(testByte)); + assertThat((Byte) objectResult[idx], equalTo(testByte)); + sa.unaccessData(); + sa.destroy(); + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I1), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(1L)); + dataPointer = sa.accessData(); + sa.putElement(testByte, idx); + byteResult = dataPointer.getByteArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Byte) sa.getElement(idx), equalTo(testByte)); + assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte)); + assertThat(byteResult[idx], equalTo(testByte)); + assertThat((Byte) objectResult[idx], equalTo(testByte)); + sa.unaccessData(); + sa.destroy(); + + short testShort = Short.MAX_VALUE - 1; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI2), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(2L)); + dataPointer = sa.accessData(); + sa.putElement(testShort, idx); + shortResult = dataPointer.getShortArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Short) sa.getElement(idx), equalTo(testShort)); + assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort)); + assertThat(shortResult[idx], equalTo(testShort)); + assertThat((Short) objectResult[idx], equalTo(testShort)); + sa.unaccessData(); + sa.destroy(); + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I2), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(2L)); + dataPointer = sa.accessData(); + sa.putElement(testShort, idx); + shortResult = dataPointer.getShortArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Short) sa.getElement(idx), equalTo(testShort)); + assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort)); + assertThat(shortResult[idx], equalTo(testShort)); + assertThat((Short) objectResult[idx], equalTo(testShort)); + sa.unaccessData(); + sa.destroy(); + + int testInt = Integer.MAX_VALUE - 1; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI4), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testInt, idx); + int[] intResult = dataPointer.getIntArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Integer) sa.getElement(idx), equalTo(testInt)); + assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); + assertThat(intResult[idx], equalTo(testInt)); + assertThat((Integer) objectResult[idx], equalTo(testInt)); + sa.unaccessData(); + sa.destroy(); + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I4), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testInt, idx); + intResult = dataPointer.getIntArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Integer) sa.getElement(idx), equalTo(testInt)); + assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); + assertThat(intResult[idx], equalTo(testInt)); + assertThat((Integer) objectResult[idx], equalTo(testInt)); + sa.unaccessData(); + sa.destroy(); + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UINT), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testInt, idx); + intResult = dataPointer.getIntArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Integer) sa.getElement(idx), equalTo(testInt)); + assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); + assertThat(intResult[idx], equalTo(testInt)); + assertThat((Integer) objectResult[idx], equalTo(testInt)); + sa.unaccessData(); + sa.destroy(); + + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_INT), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testInt, idx); + intResult = dataPointer.getIntArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Integer) sa.getElement(idx), equalTo(testInt)); + assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); + assertThat(intResult[idx], equalTo(testInt)); + assertThat((Integer) objectResult[idx], equalTo(testInt)); + sa.unaccessData(); + sa.destroy(); + + SCODE testSCODE = new SCODE(47); + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_ERROR), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testSCODE, idx); + intResult = dataPointer.getIntArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((SCODE) sa.getElement(idx), equalTo(testSCODE)); + assertThat(dataPointer.getInt(idx * elementSize), equalTo(47)); + assertThat(intResult[idx], equalTo(47)); + assertThat((SCODE) objectResult[idx], equalTo(testSCODE)); + sa.unaccessData(); + sa.destroy(); + + float testFloat = 42.23f; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R4), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(4L)); + dataPointer = sa.accessData(); + sa.putElement(testFloat, idx); + float[] floatResult = dataPointer.getFloatArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Float) sa.getElement(idx), equalTo(testFloat)); + assertThat((Float) dataPointer.getFloat(idx * elementSize), equalTo(testFloat)); + assertThat(floatResult[idx], equalTo(testFloat)); + assertThat((Float) objectResult[idx], equalTo(testFloat)); + sa.unaccessData(); + sa.destroy(); + + double testDouble = 42.23d; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R8), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(8L)); + dataPointer = sa.accessData(); + sa.putElement(testDouble, idx); + double[] doubleResult = dataPointer.getDoubleArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat((Double) sa.getElement(idx), equalTo(testDouble)); + assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDouble)); + assertThat(doubleResult[idx], equalTo(testDouble)); + assertThat((Double) objectResult[idx], equalTo(testDouble)); + sa.unaccessData(); + sa.destroy(); + + Date testDate = new Date(1923, 1, 1, 5, 0, 0); + DATE testDATE = new DATE(testDate); + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_DATE), 2); + elementSize = sa.getElemsize(); + assertThat(elementSize, equalTo(8L)); + dataPointer = sa.accessData(); + sa.putElement(testDATE, idx); + doubleResult = dataPointer.getDoubleArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat(((DATE) sa.getElement(idx)).date, equalTo(testDATE.date)); + assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDATE.date)); + assertThat(((DATE) sa.getElement(idx)).getAsJavaDate(), equalTo(testDate)); + assertThat(doubleResult[idx], equalTo(testDATE.date)); + assertThat((Date) objectResult[idx], equalTo(testDate)); + sa.unaccessData(); + sa.destroy(); + + String testString = "äöüßAE!"; + sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BSTR), 2); + elementSize = sa.getElemsize(); + dataPointer = sa.accessData(); + sa.putElement(testString, idx); + Pointer[] pointerResult = dataPointer.getPointerArray(0, 2); + objectResult = (Object[]) toPrimitiveArray(sa, false); + assertThat(((String) sa.getElement(idx)), equalTo(testString)); + assertThat(new BSTR(dataPointer.getPointer(idx * elementSize)).getValue(), equalTo(testString)); + assertThat(new BSTR(pointerResult[idx]).getValue(), equalTo(testString)); + assertThat((String) objectResult[idx], equalTo(testString)); + sa.unaccessData(); + sa.destroy(); + + // VT_VARIANT is tested in testADODB + + // untested: VT_UNKNOWN + // untested: VT_DISPATCH + // untested: VT_CY + // untested: VT_DECIMAL + // unsupported: VT_RECORD + } + + + /** + * Test assumption: The windows search provider is present and holds at least + * five entries. If this assumption is not met, this test fails. + */ + @Test + public void testADODB() { + Factory fact = new Factory(); + + // Open a record set with a sample search (basicly get the first five + // entries from the search index + Connection conn = fact.createObject(Connection.class); + conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1); + + Recordset recordset = fact.createObject(Recordset.class); + recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl, System.DateCreated FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); + + // Save complete list for comparison with subscript list + List urls = new ArrayList(5); + List names = new ArrayList(5); + + while (!recordset.getEOF()) { + WinNT.HRESULT hr; + + // Fetch (all) five rows and extract SAFEARRAY + SAFEARRAY sa = recordset.GetRows(5); + + assertThat(sa.getDimensionCount(), is(2)); + + // Test getting bounds via automation functions SafeArrayGetLBound + // and SafeArrayGetUBound + + // 5 rows (dimension 1) and 4 (dimension 2) columns should be + // returned, the indices are zero-based, the lower bounds are + // always zero + // + // Dimensions are inverted between SafeArray and java, so + // in this case row dimension 2 retrieves row count, + // dimension 1 retrieves column count + WinDef.LONGByReference res = new WinDef.LONGByReference(); + + hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), res); + assert COMUtils.SUCCEEDED(hr); + assertThat(res.getValue().intValue(), is(0)); + + hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), res); + assert COMUtils.SUCCEEDED(hr); + assertThat(res.getValue().intValue(), is(4)); + + hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(1), res); + assert COMUtils.SUCCEEDED(hr); + assertThat(res.getValue().intValue(), is(0)); + + hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(1), res); + assert COMUtils.SUCCEEDED(hr); + assertThat(res.getValue().intValue(), is(3)); + + // Get dimensions directly from structure + // lLbound contains lowerBound (first index) + // cElements contains count of elements + int row_lower = sa.rgsabound[0].lLbound.intValue(); + int row_count = sa.rgsabound[0].cElements.intValue(); + int column_lower = sa.rgsabound[1].lLbound.intValue(); + int column_count = sa.rgsabound[1].cElements.intValue(); + assertThat(row_lower, is(0)); + assertThat(row_count, is(5)); + assertThat(column_lower, is(0)); + assertThat(column_count, is(4)); + + // Use Wrapper methods + assertThat(sa.getLBound(0), is(0)); + assertThat(sa.getUBound(0), is(4)); + assertThat(sa.getLBound(1), is(0)); + assertThat(sa.getUBound(1), is(3)); + + // Iterate over resultset and fetch via SafeArrayGetElement + // Columns 1 - 3 return Strings, Column 4 returns a date + for (int rowIdx = row_lower; rowIdx < row_lower + row_count; rowIdx++) { + for (int colIdx = column_lower; colIdx < column_lower + column_count; colIdx++) { + VARIANT result = (VARIANT) sa.getElement(rowIdx, colIdx); + Pointer pv = sa.ptrOfIndex(rowIdx, colIdx); + VARIANT result2 = new VARIANT(pv); + COMUtils.checkRC(hr); + if(colIdx == 3) { + assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; + assert (result2.getVarType().intValue() & Variant.VT_DATE) > 0; + } else { + assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; + assert (result2.getVarType().intValue() & Variant.VT_BSTR) > 0; + } + // Only clear result, as SafeArrayGetElement creates a copy + // result2 is a view into the SafeArray + OleAuto.INSTANCE.VariantClear(result); + } + } + + // Access SafeArray directly + sa.lock(); + try { + // Map the returned array to java + VARIANT[] variantArray = (VARIANT[]) (new VARIANT(sa.pvData.getPointer()).toArray(row_count * column_count)); + + for (int rowIdx = 0; rowIdx < row_count; rowIdx++) { + for (int colIdx = 0; colIdx < column_count; colIdx++) { + int index = rowIdx * column_count + colIdx; + VARIANT result = variantArray[index]; + if (colIdx == 3) { + assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; + } else { + assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; + } + // see comment for urls + if(colIdx == 2) { + urls.add(result.stringValue()); + } else if (colIdx == 1) { + names.add(result.stringValue()); + } + } + } + } finally { + sa.unlock(); + } + + // Access SafeArray directly - Variant 2 + Pointer data = sa.accessData(); + try { + // Map the returned array to java + VARIANT[] variantArray = (VARIANT[]) (new VARIANT(data).toArray(row_count * column_count)); + + for (int rowIdx = 0; rowIdx < row_count; rowIdx++) { + for (int colIdx = 0; colIdx < column_count; colIdx++) { + int index = rowIdx * column_count + colIdx; + VARIANT result = variantArray[index]; + if (colIdx == 3) { + assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; + } else { + assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; + } + // see comment for urls + if(colIdx == 2) { + urls.add(result.stringValue()); + } else if (colIdx == 1) { + names.add(result.stringValue()); + } + } + } + } finally { + sa.unaccessData(); + } + + sa.destroy(); + } + + recordset.Close(); + + // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl" + recordset = fact.createObject(Recordset.class); + recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); + + + // Create SAFEARRAY and wrap it into a VARIANT + // Array is initialized to one element and then redimmed. This is done + // to test SafeArrayRedim, in normal usage it is more efficient to + // intitialize directly to the correct size + SAFEARRAY arr = SAFEARRAY.createSafeArray(1); + arr.putElement(new VARIANT("System.ItemUrl"), 0); + boolean exceptionCaught = false; + try { + arr.putElement(new VARIANT("System.ItemName"), 1); + } catch (COMException ex) { + exceptionCaught = true; + } + assertTrue("Array is initialized to a size of one - it can't hold a second item.", exceptionCaught); + arr.redim(2, 0); + arr.putElement(new VARIANT("System.ItemName"), 1); + + assertThat(arr.getDimensionCount(), is(1)); + + VARIANT columnList = new VARIANT(); + columnList.setValue(Variant.VT_ARRAY | Variant.VT_VARIANT, arr); + + assert !(recordset.getEOF()); + + while (!recordset.getEOF()) { + SAFEARRAY sa = recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList); + + assertThat(sa.getDimensionCount(), is(2)); + + assertThat(sa.getVarType().intValue(), is(Variant.VT_VARIANT)); + LONGByReference longRef = new LONGByReference(); + + OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), longRef); + int lowerBound = longRef.getValue().intValue(); + assertThat(sa.getLBound(0), equalTo(lowerBound)); + + OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), longRef); + int upperBound = longRef.getValue().intValue(); + assertThat(sa.getUBound(0), equalTo(upperBound)); + + // 5 rows are expected + assertThat(upperBound - lowerBound + 1, is(5)); + + for (int rowIdx = lowerBound; rowIdx <= upperBound; rowIdx++) { + VARIANT variantItemUrl = (VARIANT) sa.getElement(rowIdx, 0); + VARIANT variantItemName = (VARIANT) sa.getElement(rowIdx, 1); + assertThat(variantItemUrl.stringValue(), is(urls.get(rowIdx))); + assertThat(variantItemName.stringValue(), is(names.get(rowIdx))); + OleAuto.INSTANCE.VariantClear(variantItemUrl); + OleAuto.INSTANCE.VariantClear(variantItemName); + } + + sa.destroy(); + } + + recordset.Close(); + + // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl" + recordset = fact.createObject(Recordset.class); + recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); + + assert !(recordset.getEOF()); + + while (!recordset.getEOF()) { + Object[][] data = (Object[][]) OaIdlUtil.toPrimitiveArray(recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList), true); + + assertThat(data.length, is(5)); + assertThat(data[0].length, is(2)); + + for (int rowIdx = 0; rowIdx < data.length; rowIdx++) { + assertThat((String) data[rowIdx][0], is(urls.get(rowIdx))); + assertThat((String) data[rowIdx][1], is(names.get(rowIdx))); + } + } + + recordset.Close(); + + conn.Close(); + + fact.disposeAll(); + } + + // ------------- Helper classes / interfaces + + /** + *

+ * guid({00000514-0000-0010-8000-00AA006D2EA4})

+ *

+ * source(ConnectionEvents)

+ */ + @ComObject(clsId = "{00000514-0000-0010-8000-00AA006D2EA4}", progId = "{B691E011-1797-432E-907A-4D8C69339129}") + public static interface Connection extends + _Connection, + IConnectionPoint, + IUnknown { + + } + + /** + *

+ * guid({0000051B-0000-0010-8000-00AA006D2EA4})

+ */ + public static enum CursorTypeEnum implements IComEnum { + adOpenUnspecified(-1), + adOpenForwardOnly(0), + adOpenKeyset(1), + adOpenDynamic(2), + adOpenStatic(3),; + + private CursorTypeEnum(long value) { + this.value = value; + } + private long value; + + public long getValue() { + return this.value; + } + } + + /** + *

+ * guid({0000051D-0000-0010-8000-00AA006D2EA4})

+ */ + public static enum LockTypeEnum implements IComEnum { + adLockUnspecified(-1), + adLockReadOnly(1), + adLockPessimistic(2), + adLockOptimistic(3), + adLockBatchOptimistic(4),; + + private LockTypeEnum(long value) { + this.value = value; + } + private long value; + + public long getValue() { + return this.value; + } + } + + /** + *

+ * guid({00000535-0000-0010-8000-00AA006D2EA4})

+ */ + @ComObject(clsId = "{00000535-0000-0010-8000-00AA006D2EA4}", progId = "{00000300-0000-0010-8000-00AA006D2EA4}") + public static interface Recordset extends + _Recordset { + + } + + /** + *

+ * guid({00001550-0000-0010-8000-00AA006D2EA4})

+ */ + @ComInterface(iid = "{00001550-0000-0010-8000-00AA006D2EA4}") + public static interface _Connection { + + /** + *

+ * memberId(5)

+ */ + @ComMethod(name = "Close") + void Close(); + + /** + *

+ * memberId(10)

+ */ + @ComMethod(name = "Open") + void Open(String ConnectionString, + String UserID, + String Password, + int Options); + } + + /** + * + * + *

+ * guid({00000556-0000-0010-8000-00AA006D2EA4})

+ */ + @ComInterface(iid = "{00000556-0000-0010-8000-00AA006D2EA4}") + public static interface _Recordset { + + /** + *

+ * memberId(1006)

+ */ + @ComProperty(name = "EOF") + Boolean getEOF(); + + /** + *

+ * memberId(1016)

+ */ + @ComMethod(name = "GetRows") + SAFEARRAY GetRows(int Rows, + Object Start, + Object Fields); + + /** + *

+ * memberId(1016)

+ */ + @ComMethod(name = "GetRows") + SAFEARRAY GetRows(int Rows); + + /** + *

+ * memberId(1016)

+ */ + @ComMethod(name = "GetRows") + SAFEARRAY GetRows(); + + /** + *

+ * memberId(1014)

+ */ + @ComMethod(name = "Close") + void Close(); + + /** + *

+ * memberId(1022)

+ */ + @ComMethod(name = "Open") + void Open(Object Source, + Object ActiveConnection, + CursorTypeEnum CursorType, + LockTypeEnum LockType, + int Options); + } +} diff --git a/contrib/platform/test/com/sun/jna/platform/win32/VariantTest.java b/contrib/platform/test/com/sun/jna/platform/win32/VariantTest.java index 55bdad57eb..8cb9b34f3d 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/VariantTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/VariantTest.java @@ -3,14 +3,25 @@ import junit.framework.TestCase; import com.sun.jna.platform.win32.OaIdl.DATE; +import com.sun.jna.platform.win32.OaIdl.VARIANT_BOOL; import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes.BSTR; import com.sun.jna.platform.win32.WinBase.SYSTEMTIME; +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.LONGLONG; import com.sun.jna.platform.win32.WinDef.SHORT; +import com.sun.jna.platform.win32.WinDef.USHORT; import com.sun.jna.platform.win32.WinNT.HRESULT; import com.sun.jna.ptr.DoubleByReference; - import java.util.Date; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertThat; + public class VariantTest extends TestCase { public static void main(String[] args) { @@ -23,7 +34,7 @@ public VariantTest() { public void testVariantClear() { VARIANT variant = new VARIANT(new SHORT(33333)); - HRESULT hr = OleAuto.INSTANCE.VariantClear(variant.getPointer()); + HRESULT hr = OleAuto.INSTANCE.VariantClear(variant); assertTrue("hr: " + hr.intValue(), hr.intValue() == 0); } @@ -68,17 +79,164 @@ public void testVariantRecord() { pvRecord2 = (VARIANT._VARIANT.__VARIANT.BRECORD)variant.getValue(); } + + public void testDATECalculation() { + // Samples from MSDN to ensure correct implementation + // Definition is to be found here: https://msdn.microsoft.com/de-de/library/82ab7w69.aspx + + // From Date to DATE + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 27, 0, 0, 0)).date, equalTo(-3.00d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 28, 12, 0, 0)).date, equalTo(-2.50d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 28, 0, 0, 0)).date, equalTo(-2.00d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 29, 0, 0, 0)).date, equalTo(-1.00d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 30, 0, 0, 0)).date, equalTo(0.00d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 30, 6, 0, 0)).date, equalTo(0.25d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 30, 12, 0, 0)).date, equalTo(0.50d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 30, 18, 0, 0)).date, equalTo(0.75d)); + assertThat(new DATE(new Date(1899 - 1900, 12 - 1, 31, 0, 0, 0)).date, equalTo(1.0d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 1, 0, 0, 0)).date, equalTo(2.00d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 1, 12, 0, 0)).date, equalTo(2.50d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 2, 0, 0, 0)).date, equalTo(3.00d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 4, 0, 0, 0)).date, equalTo(5.00d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 4, 6, 0, 0)).date, equalTo(5.25d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 4, 12, 0, 0)).date, equalTo(5.50d)); + assertThat(new DATE(new Date(1900 - 1900, 1 - 1, 4, 21, 0, 0)).date, equalTo(5.875d)); + + // From DATE to Date + assertThat(new DATE(-3.00d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 27, 0, 0, 0))); + assertThat(new DATE(-2.50d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 28, 12, 0, 0))); + assertThat(new DATE(-2.00d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 28, 0, 0, 0))); + assertThat(new DATE(-1.00d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 29, 0, 0, 0))); + assertThat(new DATE(-0.75d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 18, 0, 0))); + assertThat(new DATE(-0.50d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 12, 0, 0))); + assertThat(new DATE(-0.25d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 6, 0, 0))); + assertThat(new DATE(0.00d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 0, 0, 0))); + assertThat(new DATE(0.25d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 6, 0, 0))); + assertThat(new DATE(0.50d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 12, 0, 0))); + assertThat(new DATE(0.75d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 30, 18, 0, 0))); + assertThat(new DATE(1.0d).getAsJavaDate(), equalTo(new Date(1899 - 1900, 12 - 1, 31, 0, 0, 0))); + assertThat(new DATE(2.00d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 1, 0, 0, 0))); + assertThat(new DATE(2.50d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 1, 12, 0, 0))); + assertThat(new DATE(3.00d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 2, 0, 0, 0))); + assertThat(new DATE(5.00d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 4, 0, 0, 0))); + assertThat(new DATE(5.25d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 4, 6, 0, 0))); + assertThat(new DATE(5.50d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 4, 12, 0, 0))); + assertThat(new DATE(5.875d).getAsJavaDate(), equalTo(new Date(1900 - 1900, 1 - 1, 4, 21, 0, 0))); + + + } public void testVariantConstructors() { - VARIANT variant = new VARIANT((short) 1); - variant = new VARIANT((byte) 1); - variant = new VARIANT('1'); - variant = new VARIANT(1); - variant = new VARIANT((long) 1); - variant = new VARIANT((float) 1); - variant = new VARIANT((double) 1); - variant = new VARIANT("1"); - variant = new VARIANT(true); - variant = new VARIANT(new Date()); + VARIANT variant; + + // skipped: BSTRByReference constructor + // skipped: empty constructor + // skipped: pointer constructor + // skipped: IDispatch constructor + String testString = "TeST$ö"; + BSTR bstr = OleAuto.INSTANCE.SysAllocString(testString); + + variant = new VARIANT(bstr); + assertThat(variant.getValue(), instanceOf(BSTR.class)); + assertThat(((BSTR)variant.getValue()).getValue(), equalTo(testString)); + assertThat(variant.stringValue(), equalTo(testString)); + + variant = new VARIANT(testString); + assertThat(variant.getValue(), instanceOf(BSTR.class)); + assertThat(((BSTR)variant.getValue()).getValue(), equalTo(testString)); + assertThat(variant.stringValue(), equalTo(testString)); + + OleAuto.INSTANCE.SysFreeString(bstr); + OleAuto.INSTANCE.SysFreeString((BSTR) variant.getValue()); + + BOOL boolTrue = new WinDef.BOOL(true); + + variant = new VARIANT(Variant.VARIANT_TRUE); + assertThat(variant.getValue(), instanceOf(VARIANT_BOOL.class)); + assertThat(((VARIANT_BOOL) variant.getValue()).shortValue(), equalTo((short) 0xFFFF)); + assertThat(variant.booleanValue(), equalTo(true)); + + variant = new VARIANT(boolTrue); + assertThat(variant.getValue(), instanceOf(VARIANT_BOOL.class)); + assertThat(((VARIANT_BOOL) variant.getValue()).shortValue(), equalTo((short) 0xFFFF)); + assertThat(variant.booleanValue(), equalTo(true)); + + int testInt = 4223; + LONG testIntWin = new LONG(testInt); + variant = new VARIANT(testIntWin); + assertThat(variant.getValue(), instanceOf(LONG.class)); + assertThat(((LONG) variant.getValue()).intValue(), equalTo(testInt)); + assertThat(variant.intValue(), equalTo(testInt)); + + variant = new VARIANT(testInt); + assertThat(variant.getValue(), instanceOf(LONG.class)); + assertThat(((LONG) variant.getValue()).intValue(), equalTo(testInt)); + assertThat(variant.intValue(), equalTo(testInt)); + + short testShort = 23; + SHORT testShortWin = new SHORT(testShort); + variant = new VARIANT(testShortWin); + assertThat(variant.getValue(), instanceOf(SHORT.class)); + assertThat(((SHORT) variant.getValue()).shortValue(), equalTo(testShort)); + assertThat(variant.shortValue(), equalTo(testShort)); + + variant = new VARIANT(testShort); + assertThat(variant.getValue(), instanceOf(SHORT.class)); + assertThat(((SHORT) variant.getValue()).shortValue(), equalTo(testShort)); + assertThat(variant.shortValue(), equalTo(testShort)); + + long testLong = 4223L + Integer.MAX_VALUE; + + variant = new VARIANT(testLong); + assertThat(variant.getValue(), instanceOf(LONGLONG.class)); + assertThat(((LONGLONG) variant.getValue()).longValue(), equalTo(testLong)); + assertThat(variant.longValue(), equalTo(testLong)); + + Date testDate = new Date(2042 - 1900, 2, 3, 23, 0, 0); + variant = new VARIANT(testDate); + assertThat(variant.getValue(), instanceOf(DATE.class)); + assertThat(variant.dateValue(), equalTo(testDate)); + + byte testByte = 42; + BYTE testByteWin = new BYTE(testByte); + CHAR testByteWin2 = new CHAR(testByte); + variant = new VARIANT(testByte); + assertThat(variant.getValue(), instanceOf(BYTE.class)); + assertThat(((BYTE) variant.getValue()).byteValue(), equalTo(testByte)); + assertThat(variant.byteValue(), equalTo(testByte)); + + variant = new VARIANT(testByteWin); + assertThat(variant.getValue(), instanceOf(BYTE.class)); + assertThat(((BYTE) variant.getValue()).byteValue(), equalTo(testByte)); + assertThat(variant.byteValue(), equalTo(testByte)); + + variant = new VARIANT(testByteWin2); + assertThat(variant.getValue(), instanceOf(CHAR.class)); + assertThat(((CHAR) variant.getValue()).byteValue(), equalTo(testByte)); + assertThat(variant.byteValue(), equalTo(testByte)); + + variant = new VARIANT(testByteWin2); + assertThat(variant.getValue(), instanceOf(CHAR.class)); + assertThat(((CHAR) variant.getValue()).byteValue(), equalTo(testByte)); + assertThat(variant.byteValue(), equalTo(testByte)); + + double testDouble = 42.23; + variant = new VARIANT(testDouble); + assertThat(variant.getValue(), instanceOf(Double.class)); + // If this fails introduce comparison with range + assertThat(variant.doubleValue(), equalTo(testDouble)); + + float testFloat = 42.23f; + variant = new VARIANT(testFloat); + assertThat(variant.getValue(), instanceOf(Float.class)); + // If this fails introduce comparison with range + assertThat(variant.floatValue(), equalTo(testFloat)); + + char testChar = 42 + Short.MAX_VALUE; + + variant = new VARIANT(testChar); + assertThat(variant.getValue(), instanceOf(USHORT.class)); + assertThat(((USHORT) variant.getValue()).intValue(), equalTo((int) testChar)); + assertThat(variant.intValue(), equalTo((int) testChar)); } }