Skip to content

Commit

Permalink
Implement SAFEARRAY access
Browse files Browse the repository at this point in the history
- Bind more functions from OleAut32.dll
- Fix bug in function bindings from OleAut32.dll (long vs. LONG)
- Ensure SAFEARRAY.rgsabound is completely accessible
- Modify Variant#getValue and Variant#setValue to allow flexible access
  to data
- Add object oriented helper methods to SAFEARRAY and move
  functionality from limited methods in OleAutoUtil to SAFEARRAY
- Add unittests based on windows search provider that excercise SAFEARRAY functions
- Added a helper for optimized conversion from SAFEARRAY to Object[]

Closes #522
  • Loading branch information
matthiasblaesing committed Mar 27, 2016
1 parent 6b0d9ba commit 56b1f33
Show file tree
Hide file tree
Showing 10 changed files with 1,870 additions and 215 deletions.
477 changes: 477 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/OaIdl.java

Large diffs are not rendered by default.

266 changes: 266 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/win32/OaIdlUtil.java
Original file line number Diff line number Diff line change
@@ -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!
*
* <p>
* Supported types:</p>
* <ul>
* <li>VT_BOOL</li>
* <li>VT_UI1</li>
* <li>VT_I1</li>
* <li>VT_UI2</li>
* <li>VT_I2</li>
* <li>VT_UI4</li>
* <li>VT_UINT</li>
* <li>VT_I4</li>
* <li>VT_INT</li>
* <li>VT_ERROR</li>
* <li>VT_R4</li>
* <li>VT_R8</li>
* <li>VT_DATE</li>
* <li>VT_BSTR</li>
* <li>VT_VARIANT (Onle the following VARTYPES):
* <ul>
* <li>VT_EMPTY (converted to NULL)</li>
* <li>VT_NULL</li>
* <li>VT_BOOL</li>
* <li>VT_UI1</li>
* <li>VT_I1</li>
* <li>VT_UI2</li>
* <li>VT_I2</li>
* <li>VT_UI4</li>
* <li>VT_UINT</li>
* <li>VT_I4</li>
* <li>VT_INT</li>
* <li>VT_ERROR</li>
* <li>VT_R4</li>
* <li>VT_R8</li>
* <li>VT_DATE</li>
* <li>VT_BSTR</li>
* </ul>
* </li>
* </ul>
*
* @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);
}
}
}
}
Loading

0 comments on commit 56b1f33

Please sign in to comment.