1 /* Copyright (c) 2013 Tobias Wolf, All Rights Reserved 2 * 3 * The contents of this file is dual-licensed under 2 4 * alternative Open Source/Free licenses: LGPL 2.1 or later and 5 * Apache License 2.0. (starting with JNA version 4.0.0). 6 * 7 * You can freely decide which license you want to apply to 8 * the project. 9 * 10 * You may obtain a copy of the LGPL License at: 11 * 12 * http://www.gnu.org/licenses/licenses.html 13 * 14 * A copy is also included in the downloadable source code package 15 * containing JNA, in file "LGPL2.1". 16 * 17 * You may obtain a copy of the Apache License at: 18 * 19 * http://www.apache.org/licenses/ 20 * 21 * A copy is also included in the downloadable source code package 22 * containing JNA, in file "AL2.0". 23 */ 24 package com.sun.jna.platform.win32; 25 26 import com.sun.jna.Pointer; 27 import com.sun.jna.Structure; 28 import static com.sun.jna.platform.win32.AbstractWin32TestSupport.checkCOMRegistered; 29 import com.sun.jna.platform.win32.COM.COMException; 30 import com.sun.jna.platform.win32.COM.COMUtils; 31 import com.sun.jna.platform.win32.COM.util.ObjectFactory; 32 import com.sun.jna.platform.win32.COM.util.IComEnum; 33 import com.sun.jna.platform.win32.COM.util.IConnectionPoint; 34 import com.sun.jna.platform.win32.COM.util.IUnknown; 35 import com.sun.jna.platform.win32.COM.util.annotation.ComInterface; 36 import com.sun.jna.platform.win32.COM.util.annotation.ComMethod; 37 import com.sun.jna.platform.win32.COM.util.annotation.ComObject; 38 import com.sun.jna.platform.win32.COM.util.annotation.ComProperty; 39 import com.sun.jna.platform.win32.OaIdl.DATE; 40 import com.sun.jna.platform.win32.OaIdl.SAFEARRAY; 41 import com.sun.jna.platform.win32.Variant.VARIANT; 42 import static com.sun.jna.platform.win32.Variant.VT_BOOL; 43 import static com.sun.jna.platform.win32.Variant.VT_BSTR; 44 import static com.sun.jna.platform.win32.Variant.VT_DATE; 45 import static com.sun.jna.platform.win32.Variant.VT_ERROR; 46 import static com.sun.jna.platform.win32.Variant.VT_I1; 47 import static com.sun.jna.platform.win32.Variant.VT_I2; 48 import static com.sun.jna.platform.win32.Variant.VT_I4; 49 import static com.sun.jna.platform.win32.Variant.VT_INT; 50 import static com.sun.jna.platform.win32.Variant.VT_R4; 51 import static com.sun.jna.platform.win32.Variant.VT_R8; 52 import static com.sun.jna.platform.win32.Variant.VT_UI1; 53 import static com.sun.jna.platform.win32.Variant.VT_UI2; 54 import static com.sun.jna.platform.win32.Variant.VT_UI4; 55 import static com.sun.jna.platform.win32.Variant.VT_UINT; 56 import com.sun.jna.platform.win32.WTypes.BSTR; 57 import com.sun.jna.platform.win32.WinDef.LONGByReference; 58 import com.sun.jna.platform.win32.WinDef.SCODE; 59 import com.sun.jna.platform.win32.WinDef.UINT; 60 import java.util.ArrayList; 61 import java.util.Date; 62 import java.util.List; 63 64 import org.junit.Assert; 65 import org.junit.Ignore; 66 import org.junit.Test; 67 import static junit.framework.Assert.assertTrue; 68 import static org.hamcrest.CoreMatchers.equalTo; 69 import static org.hamcrest.CoreMatchers.is; 70 import org.junit.After; 71 import static org.junit.Assert.assertEquals; 72 import static org.junit.Assert.assertThat; 73 import org.junit.Before; 74 import static com.sun.jna.platform.win32.OaIdlUtil.toPrimitiveArray; 75 import com.sun.jna.platform.win32.WTypes.VARTYPE; 76 import com.sun.jna.platform.win32.WinDef.LONG; 77 import java.lang.reflect.Field; 78 import org.junit.Assume; 79 80 public class SAFEARRAYTest { 81 static { 82 ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true); 83 } 84 85 @Before setup()86 public void setup() { 87 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); 88 } 89 90 @After teardown()91 public void teardown() { 92 Ole32.INSTANCE.CoUninitialize(); 93 } 94 95 @Test testCreateVarArray()96 public void testCreateVarArray() { 97 SAFEARRAY varArray = SAFEARRAY.createSafeArray(1); 98 Assert.assertTrue(varArray != null); 99 } 100 101 @Test testCreateEmpty()102 public void testCreateEmpty() throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 103 Field f = Structure.class.getDeclaredField("PLACEHOLDER_MEMORY"); 104 f.setAccessible(true); 105 Pointer PLACEHOLDER_MEMORY = (Pointer) f.get(null); 106 SAFEARRAY sa = Structure.newInstance(SAFEARRAY.class, PLACEHOLDER_MEMORY); 107 Assert.assertTrue(sa != null); 108 } 109 110 @Test testSafeArrayPutGetElement()111 public void testSafeArrayPutGetElement() throws Exception { 112 int rowCount = 2; 113 int colCount = 10; 114 115 SAFEARRAY varArray = SAFEARRAY.createSafeArray(rowCount, colCount); 116 117 assertThat(varArray.getDimensionCount(), is(2)); 118 119 assertThat(varArray.getUBound(0), equalTo(rowCount - 1)); 120 assertThat(varArray.getUBound(1), equalTo(colCount - 1)); 121 122 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 123 for (int colIdx = 0; colIdx < colCount; colIdx++) { 124 VARIANT variant = new VARIANT(rowIdx + "#" + colIdx); 125 varArray.putElement(variant, rowIdx, colIdx); 126 } 127 } 128 129 for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) { 130 for (int colIdx = 0; colIdx < colCount; colIdx++) { 131 VARIANT element = (VARIANT) varArray.getElement(rowIdx, colIdx); 132 assertEquals(rowIdx + "#" + colIdx, element.stringValue()); 133 OleAuto.INSTANCE.VariantClear(element); 134 } 135 } 136 } 137 138 @Ignore("Only for live testing") 139 @Test testPerformance()140 public void testPerformance() { 141 ObjectFactory fact = new ObjectFactory(); 142 143 // Open a record set with a sample search (basicly get the first five 144 // entries from the search index 145 Connection conn = fact.createObject(Connection.class); 146 conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1); 147 148 Recordset recordset = fact.createObject(Recordset.class); 149 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); 150 151 SAFEARRAY wrap = recordset.GetRows(); 152 153 assertThat(wrap.getDimensionCount(), is(2)); 154 155 long timeDirect = 0; 156 long timeGetElement = 0; 157 long timePointer = 0; 158 long timeHelper = 0; 159 160 long start, end; 161 162 for (int i = 0; i < 4 * 10; i++) { 163 if (i % 4 == 0) { 164 start = System.currentTimeMillis(); 165 toArrayPtrToElement(wrap); 166 end = System.currentTimeMillis(); 167 timePointer += (end - start); 168 } else if (i % 4 == 1) { 169 start = System.currentTimeMillis(); 170 toArrayGetElement(wrap); 171 end = System.currentTimeMillis(); 172 timeGetElement += (end - start); 173 } else if (i % 4 == 2) { 174 start = System.currentTimeMillis(); 175 toArrayDirect(wrap); 176 end = System.currentTimeMillis(); 177 timeDirect += (end - start); 178 } else if (i % 4 == 3) { 179 start = System.currentTimeMillis(); 180 OaIdlUtil.toPrimitiveArray(wrap, false); 181 end = System.currentTimeMillis(); 182 timeHelper += (end - start); 183 } 184 } 185 186 System.out.println("Direct: " + timeDirect + " ms"); 187 System.out.println("GetElement: " + timeGetElement + " ms"); 188 System.out.println("Pointer: " + timePointer + " ms"); 189 System.out.println("Helper: " + timeHelper + " ms"); 190 191 recordset.Close(); 192 conn.Close(); 193 194 fact.disposeAll(); 195 } 196 toArrayGetElement(SAFEARRAY wrap)197 private Object[] toArrayGetElement(SAFEARRAY wrap) { 198 wrap.lock(); 199 int rowMax = wrap.getUBound(2); 200 int columnMax = wrap.getUBound(1); 201 Object[][] result = new Object[rowMax + 1][columnMax + 1]; 202 for(int i = 0; i <= rowMax; i++) { 203 for(int j = 0; j <= columnMax; j++) { 204 VARIANT cell = (VARIANT) wrap.getElement(i, j); 205 result[i][j] = cell.getValue(); 206 OleAuto.INSTANCE.VariantClear(cell); 207 } 208 } 209 wrap.unlock(); 210 return result; 211 } 212 toArrayPtrToElement(SAFEARRAY wrap)213 private Object[] toArrayPtrToElement(SAFEARRAY wrap) { 214 wrap.lock(); 215 int rowMax = wrap.getUBound(2); 216 int columnMax = wrap.getUBound(1); 217 Object[][] result = new Object[rowMax + 1][columnMax + 1]; 218 for(int i = 0; i <= rowMax; i++) { 219 for(int j = 0; j <= columnMax; j++) { 220 VARIANT cell = new VARIANT(wrap.ptrOfIndex(i, j)); 221 result[i][j] = cell.getValue(); 222 } 223 } 224 wrap.unlock(); 225 return result; 226 } 227 toArrayDirect(SAFEARRAY wrap)228 private Object[] toArrayDirect(SAFEARRAY wrap) { 229 Pointer dataPointer = wrap.accessData(); 230 long rowMax = wrap.getUBound(2); 231 long columnMax = wrap.getUBound(1); 232 VARIANT[] variantData = (VARIANT[]) new VARIANT(dataPointer).toArray((int) ((rowMax + 1) * (columnMax + 1))); 233 Object[][] result = new Object[(int) (rowMax + 1)][(int) (columnMax + 1)]; 234 for(long i = 0; i <= rowMax; i++) { 235 long rowOffset = i * columnMax; 236 for(long j = 0; j <= columnMax; j++) { 237 VARIANT cell = variantData[(int) (rowOffset + j)]; 238 result[(int)i][(int) j] = cell.getValue(); 239 } 240 } 241 wrap.unaccessData(); 242 return result; 243 } 244 245 @Test testMultidimensionalNotNullBased()246 public void testMultidimensionalNotNullBased() { 247 // create a basic SAFEARRAY 248 SAFEARRAY sa = SAFEARRAY.createSafeArray(new VARTYPE(VT_I4), 2, 2); 249 sa.putElement(1, 0, 0); 250 sa.putElement(2, 0, 1); 251 sa.putElement(3, 1, 0); 252 sa.putElement(4, 1, 1); 253 254 // query the plain SAFEARRAY 255 Object[][] basic = (Object[][]) OaIdlUtil.toPrimitiveArray(sa, false); 256 257 // Virtually move the bounds 258 sa.rgsabound[0].lLbound = new LONG(2); 259 sa.rgsabound[1].lLbound = new LONG(5); 260 sa.write(); 261 262 // Validate new bounds 263 Assert.assertEquals(2, sa.getLBound(0)); 264 Assert.assertEquals(3, sa.getUBound(0)); 265 Assert.assertEquals(5, sa.getLBound(1)); 266 Assert.assertEquals(6, sa.getUBound(1)); 267 268 // requery the moved array and compare with basic array 269 Object[][] relocated = (Object[][]) OaIdlUtil.toPrimitiveArray(sa, false); 270 Assert.assertArrayEquals( basic, relocated); 271 } 272 273 @Test testDataTypes()274 public void testDataTypes() { 275 int idx = 1; 276 Pointer dataPointer; 277 SAFEARRAY sa; 278 long elementSize; 279 280 Object[] objectResult; 281 282 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BOOL), 2); 283 elementSize = sa.getElemsize(); 284 assertThat(elementSize, equalTo(2L)); 285 dataPointer = sa.accessData(); 286 sa.putElement(true, idx); 287 short[] shortResult = dataPointer.getShortArray(0, 2); 288 objectResult = (Object[]) toPrimitiveArray(sa, false); 289 assertThat((Boolean) sa.getElement(idx), equalTo(true)); 290 assertThat(shortResult[idx], equalTo((short) 0xFFFF)); 291 assertThat((Short) dataPointer.getShort(idx * elementSize), equalTo((short) 0xFFFF)); 292 assertThat((Boolean) objectResult[idx], equalTo(true)); 293 sa.unaccessData(); 294 sa.destroy(); 295 296 byte testByte = 67; 297 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI1), 2); 298 elementSize = sa.getElemsize(); 299 assertThat(elementSize, equalTo(1L)); 300 dataPointer = sa.accessData(); 301 sa.putElement(testByte, idx); 302 byte[] byteResult = dataPointer.getByteArray(0, 2); 303 objectResult = (Object[]) toPrimitiveArray(sa, false); 304 assertThat((Byte) sa.getElement(idx), equalTo(testByte)); 305 assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte)); 306 assertThat(byteResult[idx], equalTo(testByte)); 307 assertThat((Byte) objectResult[idx], equalTo(testByte)); 308 sa.unaccessData(); 309 sa.destroy(); 310 311 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I1), 2); 312 elementSize = sa.getElemsize(); 313 assertThat(elementSize, equalTo(1L)); 314 dataPointer = sa.accessData(); 315 sa.putElement(testByte, idx); 316 byteResult = dataPointer.getByteArray(0, 2); 317 objectResult = (Object[]) toPrimitiveArray(sa, false); 318 assertThat((Byte) sa.getElement(idx), equalTo(testByte)); 319 assertThat(dataPointer.getByte(idx * elementSize), equalTo(testByte)); 320 assertThat(byteResult[idx], equalTo(testByte)); 321 assertThat((Byte) objectResult[idx], equalTo(testByte)); 322 sa.unaccessData(); 323 sa.destroy(); 324 325 short testShort = Short.MAX_VALUE - 1; 326 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI2), 2); 327 elementSize = sa.getElemsize(); 328 assertThat(elementSize, equalTo(2L)); 329 dataPointer = sa.accessData(); 330 sa.putElement(testShort, idx); 331 shortResult = dataPointer.getShortArray(0, 2); 332 objectResult = (Object[]) toPrimitiveArray(sa, false); 333 assertThat((Short) sa.getElement(idx), equalTo(testShort)); 334 assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort)); 335 assertThat(shortResult[idx], equalTo(testShort)); 336 assertThat((Short) objectResult[idx], equalTo(testShort)); 337 sa.unaccessData(); 338 sa.destroy(); 339 340 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I2), 2); 341 elementSize = sa.getElemsize(); 342 assertThat(elementSize, equalTo(2L)); 343 dataPointer = sa.accessData(); 344 sa.putElement(testShort, idx); 345 shortResult = dataPointer.getShortArray(0, 2); 346 objectResult = (Object[]) toPrimitiveArray(sa, false); 347 assertThat((Short) sa.getElement(idx), equalTo(testShort)); 348 assertThat(dataPointer.getShort(idx * elementSize), equalTo(testShort)); 349 assertThat(shortResult[idx], equalTo(testShort)); 350 assertThat((Short) objectResult[idx], equalTo(testShort)); 351 sa.unaccessData(); 352 sa.destroy(); 353 354 int testInt = Integer.MAX_VALUE - 1; 355 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UI4), 2); 356 elementSize = sa.getElemsize(); 357 assertThat(elementSize, equalTo(4L)); 358 dataPointer = sa.accessData(); 359 sa.putElement(testInt, idx); 360 int[] intResult = dataPointer.getIntArray(0, 2); 361 objectResult = (Object[]) toPrimitiveArray(sa, false); 362 assertThat((Integer) sa.getElement(idx), equalTo(testInt)); 363 assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); 364 assertThat(intResult[idx], equalTo(testInt)); 365 assertThat((Integer) objectResult[idx], equalTo(testInt)); 366 sa.unaccessData(); 367 sa.destroy(); 368 369 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_I4), 2); 370 elementSize = sa.getElemsize(); 371 assertThat(elementSize, equalTo(4L)); 372 dataPointer = sa.accessData(); 373 sa.putElement(testInt, idx); 374 intResult = dataPointer.getIntArray(0, 2); 375 objectResult = (Object[]) toPrimitiveArray(sa, false); 376 assertThat((Integer) sa.getElement(idx), equalTo(testInt)); 377 assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); 378 assertThat(intResult[idx], equalTo(testInt)); 379 assertThat((Integer) objectResult[idx], equalTo(testInt)); 380 sa.unaccessData(); 381 sa.destroy(); 382 383 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_UINT), 2); 384 elementSize = sa.getElemsize(); 385 assertThat(elementSize, equalTo(4L)); 386 dataPointer = sa.accessData(); 387 sa.putElement(testInt, idx); 388 intResult = dataPointer.getIntArray(0, 2); 389 objectResult = (Object[]) toPrimitiveArray(sa, false); 390 assertThat((Integer) sa.getElement(idx), equalTo(testInt)); 391 assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); 392 assertThat(intResult[idx], equalTo(testInt)); 393 assertThat((Integer) objectResult[idx], equalTo(testInt)); 394 sa.unaccessData(); 395 sa.destroy(); 396 397 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_INT), 2); 398 elementSize = sa.getElemsize(); 399 assertThat(elementSize, equalTo(4L)); 400 dataPointer = sa.accessData(); 401 sa.putElement(testInt, idx); 402 intResult = dataPointer.getIntArray(0, 2); 403 objectResult = (Object[]) toPrimitiveArray(sa, false); 404 assertThat((Integer) sa.getElement(idx), equalTo(testInt)); 405 assertThat(dataPointer.getInt(idx * elementSize), equalTo(testInt)); 406 assertThat(intResult[idx], equalTo(testInt)); 407 assertThat((Integer) objectResult[idx], equalTo(testInt)); 408 sa.unaccessData(); 409 sa.destroy(); 410 411 SCODE testSCODE = new SCODE(47); 412 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_ERROR), 2); 413 elementSize = sa.getElemsize(); 414 assertThat(elementSize, equalTo(4L)); 415 dataPointer = sa.accessData(); 416 sa.putElement(testSCODE, idx); 417 intResult = dataPointer.getIntArray(0, 2); 418 objectResult = (Object[]) toPrimitiveArray(sa, false); 419 assertThat((SCODE) sa.getElement(idx), equalTo(testSCODE)); 420 assertThat(dataPointer.getInt(idx * elementSize), equalTo(47)); 421 assertThat(intResult[idx], equalTo(47)); 422 assertThat((SCODE) objectResult[idx], equalTo(testSCODE)); 423 sa.unaccessData(); 424 sa.destroy(); 425 426 float testFloat = 42.23f; 427 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R4), 2); 428 elementSize = sa.getElemsize(); 429 assertThat(elementSize, equalTo(4L)); 430 dataPointer = sa.accessData(); 431 sa.putElement(testFloat, idx); 432 float[] floatResult = dataPointer.getFloatArray(0, 2); 433 objectResult = (Object[]) toPrimitiveArray(sa, false); 434 assertThat((Float) sa.getElement(idx), equalTo(testFloat)); 435 assertThat((Float) dataPointer.getFloat(idx * elementSize), equalTo(testFloat)); 436 assertThat(floatResult[idx], equalTo(testFloat)); 437 assertThat((Float) objectResult[idx], equalTo(testFloat)); 438 sa.unaccessData(); 439 sa.destroy(); 440 441 double testDouble = 42.23d; 442 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_R8), 2); 443 elementSize = sa.getElemsize(); 444 assertThat(elementSize, equalTo(8L)); 445 dataPointer = sa.accessData(); 446 sa.putElement(testDouble, idx); 447 double[] doubleResult = dataPointer.getDoubleArray(0, 2); 448 objectResult = (Object[]) toPrimitiveArray(sa, false); 449 assertThat((Double) sa.getElement(idx), equalTo(testDouble)); 450 assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDouble)); 451 assertThat(doubleResult[idx], equalTo(testDouble)); 452 assertThat((Double) objectResult[idx], equalTo(testDouble)); 453 sa.unaccessData(); 454 sa.destroy(); 455 456 Date testDate = new Date(1923, 1, 1, 5, 0, 0); 457 DATE testDATE = new DATE(testDate); 458 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_DATE), 2); 459 elementSize = sa.getElemsize(); 460 assertThat(elementSize, equalTo(8L)); 461 dataPointer = sa.accessData(); 462 sa.putElement(testDATE, idx); 463 doubleResult = dataPointer.getDoubleArray(0, 2); 464 objectResult = (Object[]) toPrimitiveArray(sa, false); 465 assertThat(((DATE) sa.getElement(idx)).date, equalTo(testDATE.date)); 466 assertThat((Double) dataPointer.getDouble(idx * elementSize), equalTo(testDATE.date)); 467 assertThat(((DATE) sa.getElement(idx)).getAsJavaDate(), equalTo(testDate)); 468 assertThat(doubleResult[idx], equalTo(testDATE.date)); 469 assertThat((Date) objectResult[idx], equalTo(testDate)); 470 sa.unaccessData(); 471 sa.destroy(); 472 473 String testString = "äöüßAE!"; 474 sa = SAFEARRAY.createSafeArray(new WTypes.VARTYPE(VT_BSTR), 2); 475 elementSize = sa.getElemsize(); 476 dataPointer = sa.accessData(); 477 sa.putElement(testString, idx); 478 Pointer[] pointerResult = dataPointer.getPointerArray(0, 2); 479 objectResult = (Object[]) toPrimitiveArray(sa, false); 480 assertThat(((String) sa.getElement(idx)), equalTo(testString)); 481 assertThat(new BSTR(dataPointer.getPointer(idx * elementSize)).getValue(), equalTo(testString)); 482 assertThat(new BSTR(pointerResult[idx]).getValue(), equalTo(testString)); 483 assertThat((String) objectResult[idx], equalTo(testString)); 484 sa.unaccessData(); 485 sa.destroy(); 486 487 // VT_VARIANT is tested in testADODB 488 489 // untested: VT_UNKNOWN 490 // untested: VT_DISPATCH 491 // untested: VT_CY 492 // untested: VT_DECIMAL 493 // unsupported: VT_RECORD 494 } 495 496 497 /** 498 * Test assumption: The windows search provider is present and holds at least 499 * five entries. If this assumption is not met, this test fails. 500 */ 501 @Ignore("Assumes windows search provider present with five entries") 502 @Test testADODB()503 public void testADODB() { 504 ObjectFactory fact = new ObjectFactory(); 505 506 // Open a record set with a sample search (basicly get the first five 507 // entries from the search index 508 Connection conn = fact.createObject(Connection.class); 509 try { 510 conn.Open("Provider=Search.CollatorDSO;Extended Properties='Application=Windows';", "", "", -1); 511 } catch (COMException ex) { 512 Assume.assumeNoException(ex); 513 } 514 515 Recordset recordset = fact.createObject(Recordset.class); 516 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); 517 518 // Save complete list for comparison with subscript list 519 List<String> urls = new ArrayList<String>(5); 520 List<String> names = new ArrayList<String>(5); 521 522 while (!recordset.getEOF()) { 523 WinNT.HRESULT hr; 524 525 // Fetch (all) five rows and extract SAFEARRAY 526 SAFEARRAY sa = recordset.GetRows(5); 527 528 assertThat(sa.getDimensionCount(), is(2)); 529 530 // Test getting bounds via automation functions SafeArrayGetLBound 531 // and SafeArrayGetUBound 532 533 // 5 rows (dimension 1) and 4 (dimension 2) columns should be 534 // returned, the indices are zero-based, the lower bounds are 535 // always zero 536 // 537 // Dimensions are inverted between SafeArray and java, so 538 // in this case row dimension 2 retrieves row count, 539 // dimension 1 retrieves column count 540 WinDef.LONGByReference res = new WinDef.LONGByReference(); 541 542 hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), res); 543 assert COMUtils.SUCCEEDED(hr); 544 assertThat(res.getValue().intValue(), is(0)); 545 546 hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), res); 547 assert COMUtils.SUCCEEDED(hr); 548 assertThat(res.getValue().intValue(), is(4)); 549 550 hr = OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(1), res); 551 assert COMUtils.SUCCEEDED(hr); 552 assertThat(res.getValue().intValue(), is(0)); 553 554 hr = OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(1), res); 555 assert COMUtils.SUCCEEDED(hr); 556 assertThat(res.getValue().intValue(), is(3)); 557 558 // Get dimensions directly from structure 559 // lLbound contains lowerBound (first index) 560 // cElements contains count of elements 561 int row_lower = sa.rgsabound[0].lLbound.intValue(); 562 int row_count = sa.rgsabound[0].cElements.intValue(); 563 int column_lower = sa.rgsabound[1].lLbound.intValue(); 564 int column_count = sa.rgsabound[1].cElements.intValue(); 565 assertThat(row_lower, is(0)); 566 assertThat(row_count, is(5)); 567 assertThat(column_lower, is(0)); 568 assertThat(column_count, is(4)); 569 570 // Use Wrapper methods 571 assertThat(sa.getLBound(0), is(0)); 572 assertThat(sa.getUBound(0), is(4)); 573 assertThat(sa.getLBound(1), is(0)); 574 assertThat(sa.getUBound(1), is(3)); 575 576 // Iterate over resultset and fetch via SafeArrayGetElement 577 // Columns 1 - 3 return Strings, Column 4 returns a date 578 for (int rowIdx = row_lower; rowIdx < row_lower + row_count; rowIdx++) { 579 for (int colIdx = column_lower; colIdx < column_lower + column_count; colIdx++) { 580 VARIANT result = (VARIANT) sa.getElement(rowIdx, colIdx); 581 Pointer pv = sa.ptrOfIndex(rowIdx, colIdx); 582 VARIANT result2 = new VARIANT(pv); 583 COMUtils.checkRC(hr); 584 if(colIdx == 3) { 585 assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; 586 assert (result2.getVarType().intValue() & Variant.VT_DATE) > 0; 587 } else { 588 assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; 589 assert (result2.getVarType().intValue() & Variant.VT_BSTR) > 0; 590 } 591 // Only clear result, as SafeArrayGetElement creates a copy 592 // result2 is a view into the SafeArray 593 OleAuto.INSTANCE.VariantClear(result); 594 } 595 } 596 597 // Access SafeArray directly 598 sa.lock(); 599 try { 600 // Map the returned array to java 601 VARIANT[] variantArray = (VARIANT[]) (new VARIANT(sa.pvData.getPointer()).toArray(row_count * column_count)); 602 603 for (int rowIdx = 0; rowIdx < row_count; rowIdx++) { 604 for (int colIdx = 0; colIdx < column_count; colIdx++) { 605 int index = rowIdx * column_count + colIdx; 606 VARIANT result = variantArray[index]; 607 if (colIdx == 3) { 608 assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; 609 } else { 610 assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; 611 } 612 // see comment for urls 613 if(colIdx == 2) { 614 urls.add(result.stringValue()); 615 } else if (colIdx == 1) { 616 names.add(result.stringValue()); 617 } 618 } 619 } 620 } finally { 621 sa.unlock(); 622 } 623 624 // Access SafeArray directly - Variant 2 625 Pointer data = sa.accessData(); 626 try { 627 // Map the returned array to java 628 VARIANT[] variantArray = (VARIANT[]) (new VARIANT(data).toArray(row_count * column_count)); 629 630 for (int rowIdx = 0; rowIdx < row_count; rowIdx++) { 631 for (int colIdx = 0; colIdx < column_count; colIdx++) { 632 int index = rowIdx * column_count + colIdx; 633 VARIANT result = variantArray[index]; 634 if (colIdx == 3) { 635 assert (result.getVarType().intValue() & Variant.VT_DATE) > 0; 636 } else { 637 assert (result.getVarType().intValue() & Variant.VT_BSTR) > 0; 638 } 639 // see comment for urls 640 if(colIdx == 2) { 641 urls.add(result.stringValue()); 642 } else if (colIdx == 1) { 643 names.add(result.stringValue()); 644 } 645 } 646 } 647 } finally { 648 sa.unaccessData(); 649 } 650 651 sa.destroy(); 652 } 653 654 recordset.Close(); 655 656 // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl" 657 recordset = fact.createObject(Recordset.class); 658 recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); 659 660 661 // Create SAFEARRAY and wrap it into a VARIANT 662 // Array is initialized to one element and then redimmed. This is done 663 // to test SafeArrayRedim, in normal usage it is more efficient to 664 // intitialize directly to the correct size 665 SAFEARRAY arr = SAFEARRAY.createSafeArray(1); 666 arr.putElement(new VARIANT("System.ItemUrl"), 0); 667 boolean exceptionCaught = false; 668 try { 669 arr.putElement(new VARIANT("System.ItemName"), 1); 670 } catch (COMException ex) { 671 exceptionCaught = true; 672 } 673 assertTrue("Array is initialized to a size of one - it can't hold a second item.", exceptionCaught); 674 arr.redim(2, 0); 675 arr.putElement(new VARIANT("System.ItemName"), 1); 676 677 assertThat(arr.getDimensionCount(), is(1)); 678 679 VARIANT columnList = new VARIANT(); 680 columnList.setValue(Variant.VT_ARRAY | Variant.VT_VARIANT, arr); 681 682 assert !(recordset.getEOF()); 683 684 while (!recordset.getEOF()) { 685 SAFEARRAY sa = recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList); 686 687 assertThat(sa.getDimensionCount(), is(2)); 688 689 assertThat(sa.getVarType().intValue(), is(Variant.VT_VARIANT)); 690 LONGByReference longRef = new LONGByReference(); 691 692 OleAuto.INSTANCE.SafeArrayGetLBound(sa, new UINT(2), longRef); 693 int lowerBound = longRef.getValue().intValue(); 694 assertThat(sa.getLBound(0), equalTo(lowerBound)); 695 696 OleAuto.INSTANCE.SafeArrayGetUBound(sa, new UINT(2), longRef); 697 int upperBound = longRef.getValue().intValue(); 698 assertThat(sa.getUBound(0), equalTo(upperBound)); 699 700 // 5 rows are expected 701 assertThat(upperBound - lowerBound + 1, is(5)); 702 703 for (int rowIdx = lowerBound; rowIdx <= upperBound; rowIdx++) { 704 VARIANT variantItemUrl = (VARIANT) sa.getElement(rowIdx, 0); 705 VARIANT variantItemName = (VARIANT) sa.getElement(rowIdx, 1); 706 assertThat(variantItemUrl.stringValue(), is(urls.get(rowIdx))); 707 assertThat(variantItemName.stringValue(), is(names.get(rowIdx))); 708 OleAuto.INSTANCE.VariantClear(variantItemUrl); 709 OleAuto.INSTANCE.VariantClear(variantItemName); 710 } 711 712 sa.destroy(); 713 } 714 715 recordset.Close(); 716 717 // Requery and fetch only columns "System.ItemUrl", "System.ItemName" and "System.ItemUrl" 718 recordset = fact.createObject(Recordset.class); 719 recordset.Open("SELECT TOP 5 System.ItemPathDisplay, System.ItemName, System.ItemUrl FROM SYSTEMINDEX ORDER BY System.ItemUrl", conn, CursorTypeEnum.adOpenUnspecified, LockTypeEnum.adLockUnspecified, -1); 720 721 assert !(recordset.getEOF()); 722 723 while (!recordset.getEOF()) { 724 Object[][] data = (Object[][]) OaIdlUtil.toPrimitiveArray(recordset.GetRows(5, VARIANT.VARIANT_MISSING, columnList), true); 725 726 assertThat(data.length, is(5)); 727 assertThat(data[0].length, is(2)); 728 729 for (int rowIdx = 0; rowIdx < data.length; rowIdx++) { 730 assertThat((String) data[rowIdx][0], is(urls.get(rowIdx))); 731 assertThat((String) data[rowIdx][1], is(names.get(rowIdx))); 732 } 733 } 734 735 recordset.Close(); 736 737 conn.Close(); 738 739 fact.disposeAll(); 740 } 741 742 // ------------- Helper classes / interfaces 743 744 /** 745 * <p> 746 * guid({00000514-0000-0010-8000-00AA006D2EA4})</p> 747 * <p> 748 * source(ConnectionEvents)</p> 749 */ 750 @ComObject(clsId = "{00000514-0000-0010-8000-00AA006D2EA4}", progId = "{B691E011-1797-432E-907A-4D8C69339129}") 751 public static interface Connection extends 752 _Connection, 753 IConnectionPoint, 754 IUnknown { 755 756 } 757 758 /** 759 * <p> 760 * guid({0000051B-0000-0010-8000-00AA006D2EA4})</p> 761 */ 762 public static enum CursorTypeEnum implements IComEnum { 763 adOpenUnspecified(-1), 764 adOpenForwardOnly(0), 765 adOpenKeyset(1), 766 adOpenDynamic(2), 767 adOpenStatic(3),; 768 CursorTypeEnum(long value)769 private CursorTypeEnum(long value) { 770 this.value = value; 771 } 772 private long value; 773 774 @Override getValue()775 public long getValue() { 776 return this.value; 777 } 778 } 779 780 /** 781 * <p> 782 * guid({0000051D-0000-0010-8000-00AA006D2EA4})</p> 783 */ 784 public static enum LockTypeEnum implements IComEnum { 785 adLockUnspecified(-1), 786 adLockReadOnly(1), 787 adLockPessimistic(2), 788 adLockOptimistic(3), 789 adLockBatchOptimistic(4),; 790 LockTypeEnum(long value)791 private LockTypeEnum(long value) { 792 this.value = value; 793 } 794 private long value; 795 796 @Override getValue()797 public long getValue() { 798 return this.value; 799 } 800 } 801 802 /** 803 * <p> 804 * guid({00000535-0000-0010-8000-00AA006D2EA4})</p> 805 */ 806 @ComObject(clsId = "{00000535-0000-0010-8000-00AA006D2EA4}", progId = "{00000300-0000-0010-8000-00AA006D2EA4}") 807 public static interface Recordset extends 808 _Recordset { 809 810 } 811 812 /** 813 * <p> 814 * guid({00001550-0000-0010-8000-00AA006D2EA4})</p> 815 */ 816 @ComInterface(iid = "{00001550-0000-0010-8000-00AA006D2EA4}") 817 public static interface _Connection { 818 819 /** 820 * <p> 821 * memberId(5)</p> 822 */ 823 @ComMethod(name = "Close") Close()824 void Close(); 825 826 /** 827 * <p> 828 * memberId(10)</p> 829 */ 830 @ComMethod(name = "Open") Open(String ConnectionString, String UserID, String Password, int Options)831 void Open(String ConnectionString, 832 String UserID, 833 String Password, 834 int Options); 835 } 836 837 /** 838 * 839 * 840 * <p> 841 * guid({00000556-0000-0010-8000-00AA006D2EA4})</p> 842 */ 843 @ComInterface(iid = "{00000556-0000-0010-8000-00AA006D2EA4}") 844 public static interface _Recordset { 845 846 /** 847 * <p> 848 * memberId(1006)</p> 849 */ 850 @ComProperty(name = "EOF") getEOF()851 Boolean getEOF(); 852 853 /** 854 * <p> 855 * memberId(1016)</p> 856 */ 857 @ComMethod(name = "GetRows") GetRows(int Rows, Object Start, Object Fields)858 SAFEARRAY GetRows(int Rows, 859 Object Start, 860 Object Fields); 861 862 /** 863 * <p> 864 * memberId(1016)</p> 865 */ 866 @ComMethod(name = "GetRows") GetRows(int Rows)867 SAFEARRAY GetRows(int Rows); 868 869 /** 870 * <p> 871 * memberId(1016)</p> 872 */ 873 @ComMethod(name = "GetRows") GetRows()874 SAFEARRAY GetRows(); 875 876 /** 877 * <p> 878 * memberId(1014)</p> 879 */ 880 @ComMethod(name = "Close") Close()881 void Close(); 882 883 /** 884 * <p> 885 * memberId(1022)</p> 886 */ 887 @ComMethod(name = "Open") Open(Object Source, Object ActiveConnection, CursorTypeEnum CursorType, LockTypeEnum LockType, int Options)888 void Open(Object Source, 889 Object ActiveConnection, 890 CursorTypeEnum CursorType, 891 LockTypeEnum LockType, 892 int Options); 893 } 894 } 895