From a8758521ad595dfdeff683f595ba0d5dea7b3a4a Mon Sep 17 00:00:00 2001 From: clay_shooter Date: Mon, 27 Aug 2007 03:34:56 +0000 Subject: [PATCH] SF 1775889 - There was a like when passing arrays of indexes as part of set or get methods. 1775889 mentioned setString() but there were others also. --- docs/ReleaseNotes.html | 12 +- jni/SafeArray.cpp | 62 ++++-- src/com/jacob/com/SafeArray.java | 4 +- unittest/com/jacob/test/BaseTestCase.java | 17 +- .../jacob/test/safearray/SafeArrayLeak.java | 193 ++++++++++++++++++ 5 files changed, 254 insertions(+), 34 deletions(-) create mode 100644 unittest/com/jacob/test/safearray/SafeArrayLeak.java diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 9197973..b8fbba9 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -21,6 +21,14 @@ Bugs +   +   + + + 1775889 + (M4) Fixed leak setString(int[],value) and other setString() methods + +     @@ -29,7 +37,8 @@ 1709841 - (M1) Compiled with Visual Studio 2005. + (M1) Compiled with Visual Studio 2005. Jacob now requires + 2005 or later libraries. See the UsingJacob.html file for impact this has on NT, 2000 and Server 2003 users. @@ -40,7 +49,6 @@ backwards compatible by default. -     diff --git a/jni/SafeArray.cpp b/jni/SafeArray.cpp index 9b191c6..0f7599a 100644 --- a/jni/SafeArray.cpp +++ b/jni/SafeArray.cpp @@ -92,7 +92,7 @@ SAFEARRAY *copySA(SAFEARRAY *psa) VARIANT v1, v2; VariantInit(&v1); - VariantInit(&v2); + VariantInit(&v2); V_VT(&v1) = VT_ARRAY | vt; V_ARRAY(&v1) = psa; VariantCopy(&v2, &v1); @@ -142,11 +142,11 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_init // build the sa according to params if ( nDims > 0 ) { - SAFEARRAY *sa = makeArray(vt, nDims, lbounds, celems); - env->ReleaseIntArrayElements(lb, lbounds, 0); - env->ReleaseIntArrayElements(cel, celems, 0); - jclass clazz = env->GetObjectClass(_this); - setSA(env, _this, sa, 0); + SAFEARRAY *sa = makeArray(vt, nDims, lbounds, celems); + env->ReleaseIntArrayElements(lb, lbounds, 0); + env->ReleaseIntArrayElements(cel, celems, 0); + jclass clazz = env->GetObjectClass(_this); + setSA(env, _this, sa, 0); } } @@ -565,7 +565,7 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_fromStringArray // GetStringUTFChars() replaced with GetStringChars() // (Variant modified in previous report) const jchar *str = env->GetStringChars(s, NULL); - CComBSTR bs((LPCOLESTR)str); // SR cast SF 1689061 + CComBSTR bs((LPCOLESTR)str); // SR cast SF 1689061 V_VT(&v) = VT_BSTR; V_BSTR(&v) = bs.Copy(); long x = i; @@ -1629,7 +1629,7 @@ JNIEXPORT jstring JNICALL Java_com_jacob_com_SafeArray_getString__I BSTR bs = V_BSTR(&v); jstring js = env->NewString((jchar*)bs, SysStringLen(bs)); // SR cast SF 1689061 // jacob report 1224219 - // SafeArrayGetElement memory must be freed http://www.canaimasoft.com/f90VB/OnlineManuals/Reference/TH_31.htm + // SafeArrayGetElement memory must be freed http://www.canaimasoft.com/f90VB/OnlineManuals/Reference/TH_31.htm VariantClear(&v); return js; } else if (vt == VT_BSTR) { @@ -1637,8 +1637,8 @@ JNIEXPORT jstring JNICALL Java_com_jacob_com_SafeArray_getString__I SafeArrayGetElement(psa, &idx, &bs); jstring js = env->NewString((jchar*)bs, SysStringLen(bs)); // SR cast SF 1689061 // jacob report 1224219 - // SafeArrayGetElement memory must be freed http://www.canaimasoft.com/f90VB/OnlineManuals/Reference/TH_31.htm - if (bs) SysFreeString(bs); + // SafeArrayGetElement memory must be freed http://www.canaimasoft.com/f90VB/OnlineManuals/Reference/TH_31.htm + if (bs) SysFreeString(bs); return js; } ThrowComFail(env, "safearray cannot get string", 0); @@ -2456,10 +2456,12 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setVariants } /* PLEASE NOTE THE LINE: -jint *jIndices = env->GetIntArrayElements(indices, NULL); -which I added to replace "long idx[2] = {i,j};" from the 2D case. -Not sure if this is correct. Also, check that I am doing the null test and -dimension test correctly. + jint *jIndices = env->GetIntArrayElements(indices, NULL); + which I added to replace "long idx[2] = {i,j};" from the 2D case. + Not sure if this is correct. Also, check that I am doing the null test and + dimension test correctly. + Would really like to call env->ReleaseIntArrayElements(indices, jIndices, NULL); + in here but I can't get it to work */ #define GETNDCODE(varType, varAccess, jtyp) \ @@ -2500,10 +2502,12 @@ dimension test correctly. //--------------------------------- /* PLEASE NOTE THE LINE: -jint *jIndices = env->GetIntArrayElements(indices, NULL); -which I added to replace "long idx[2] = {i,j};" from the 2D case. -Not sure if this is correct. Also, check that I am doing the null test and -dimension test correctly. + jint *jIndices = env->GetIntArrayElements(indices, NULL); + which I added to replace "long idx[2] = {i,j};" from the 2D case. + Not sure if this is correct. Also, check that I am doing the null test and + dimension test correctly. + Would really like to call env->ReleaseIntArrayElements(indices, jIndices, NULL); + in here but I can't get it to work */ #define SETNDCODE(varType, varAccess) \ @@ -2586,6 +2590,7 @@ JNIEXPORT jobject JNICALL Java_com_jacob_com_SafeArray_getVariant___3I } else { ThrowComFail(env, "safearray type is not variant/dispatch", -1); } + env->ReleaseIntArrayElements(indices, jIndices, NULL); return newVariant; } @@ -2633,6 +2638,7 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setVariant___3ILcom_jacob_co } else { ThrowComFail(env, "safearray type is not variant/dispatch", -1); } + env->ReleaseIntArrayElements(indices, jIndices, NULL); } /* @@ -2756,6 +2762,7 @@ JNIEXPORT jstring JNICALL Java_com_jacob_com_SafeArray_getString___3I VARIANT v; VariantInit(&v); SafeArrayGetElement(psa, jIndices, &v); + env->ReleaseIntArrayElements(indices, jIndices, NULL); if (FAILED(VariantChangeType(&v, &v, 0, VT_BSTR))) { return NULL; } @@ -2765,18 +2772,22 @@ JNIEXPORT jstring JNICALL Java_com_jacob_com_SafeArray_getString___3I } else if (vt == VT_BSTR) { BSTR bs = NULL; SafeArrayGetElement(psa, jIndices, &bs); + env->ReleaseIntArrayElements(indices, jIndices, NULL); jstring js = env->NewString((jchar*)bs, SysStringLen(bs)); // SR cast SF 1689061 return js; + } else { + ThrowComFail(env, "safearray cannot get string", 0); + env->ReleaseIntArrayElements(indices, jIndices, NULL); + return NULL; } - ThrowComFail(env, "safearray cannot get string", 0); - return NULL; } /* * Class: com_jacob_com_SafeArray - * Method: setString - * Signature: ([ILjava/lang/String;)V + * Method: setStringjIndices, NULL);ILjava/lang/String;)V + * + * This method has a leak in it somewhere. */ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setString___3ILjava_lang_String_2 (JNIEnv *env, jobject _this, jintArray indices, jstring s) @@ -2788,6 +2799,7 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setString___3ILjava_lang_Str } // ??? Not sure if this is correct type for call to SafeArrayGetElement() + // could have individually retrieved the indicies jint *jIndices = env->GetIntArrayElements(indices, NULL); if (!jIndices) { @@ -2819,6 +2831,8 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setString___3ILjava_lang_Str } else { ThrowComFail(env, "safearray cannot set string", 0); } + // need to unpin the copied array SF: 1775889 + env->ReleaseIntArrayElements(indices, jIndices, NULL); } /* @@ -2898,6 +2912,7 @@ JNIEXPORT jboolean JNICALL Java_com_jacob_com_SafeArray_getBoolean___3I VARIANT v; VariantInit(&v); SafeArrayGetElement(sa, jIndices, (void*) &v); + env->ReleaseIntArrayElements(indices, jIndices, NULL); if (FAILED(VariantChangeType(&v, &v, 0, VT_BOOL))) { ThrowComFail(env, "safearray change type failed", -1); return NULL; @@ -2907,9 +2922,11 @@ JNIEXPORT jboolean JNICALL Java_com_jacob_com_SafeArray_getBoolean___3I } else if (vt == VT_BOOL) { VARIANT_BOOL vb; SafeArrayGetElement(sa, jIndices, (void*) &vb); + env->ReleaseIntArrayElements(indices, jIndices, NULL); jboolean jb = vb == VARIANT_TRUE ? JNI_TRUE: JNI_FALSE; return jb; } else { + env->ReleaseIntArrayElements(indices, jIndices, NULL); return NULL; } } @@ -2954,6 +2971,7 @@ JNIEXPORT void JNICALL Java_com_jacob_com_SafeArray_setBoolean___3IZ } else { ThrowComFail(env, "safearray type mismatch", -1); } + env->ReleaseIntArrayElements(indices, jIndices, NULL); } diff --git a/src/com/jacob/com/SafeArray.java b/src/com/jacob/com/SafeArray.java index 8ea4e15..83839c6 100644 --- a/src/com/jacob/com/SafeArray.java +++ b/src/com/jacob/com/SafeArray.java @@ -484,14 +484,14 @@ public class SafeArray extends JacobObject { public native String getString(int sa_idx1, int sa_idx2); /** - * string access + * puts a string into an element in a single dimensional safe array * @param sa_idx * @param c */ public native void setString(int sa_idx, String c); /** - * string access + * puts a string into an element in a two dimensional array. * @param sa_idx1 * @param sa_idx2 * @param c diff --git a/unittest/com/jacob/test/BaseTestCase.java b/unittest/com/jacob/test/BaseTestCase.java index 60823ea..cbd2cd7 100644 --- a/unittest/com/jacob/test/BaseTestCase.java +++ b/unittest/com/jacob/test/BaseTestCase.java @@ -70,19 +70,20 @@ public class BaseTestCase extends TestCase { // change all "." to ^ for later conversion to "/" so we can append resource names with "." classPackageName = classPackageName.replace('.', '^'); - System.out.println("classPackageName:" + classPackageName); - - String fullPathToResource = (classPackageName.length() > 0 ? classPackageName - + "^" - : "") - + resourceName; + System.out.println("classPackageName: " + classPackageName); + String fullPathToResource; + if (classPackageName.length()> 0){ + fullPathToResource = classPackageName + "^" + resourceName; + } else { + fullPathToResource = resourceName; + } fullPathToResource = fullPathToResource.replace('^', '/'); - System.out.println("fullPathToResource:" + fullPathToResource); + System.out.println("fullPathToResource: " + fullPathToResource); URL urlToLibrary = classInSamePackageAsResource.getClassLoader().getResource(fullPathToResource); - assertNotNull(urlToLibrary); + assertNotNull("URL to resource "+resourceName+" should not be null",urlToLibrary); String fullPathToResourceAsFile = urlToLibrary.getFile(); System.out.println("url to library: "+urlToLibrary); System.out.println("fullPathToResourceAsFile: "+fullPathToResourceAsFile); diff --git a/unittest/com/jacob/test/safearray/SafeArrayLeak.java b/unittest/com/jacob/test/safearray/SafeArrayLeak.java new file mode 100644 index 0000000..deabe04 --- /dev/null +++ b/unittest/com/jacob/test/safearray/SafeArrayLeak.java @@ -0,0 +1,193 @@ +package com.jacob.test.safearray; + +import com.jacob.activeX.ActiveXComponent; +import com.jacob.com.ComThread; +import com.jacob.com.Dispatch; +import com.jacob.com.JacobObject; +import com.jacob.com.SafeArray; +import com.jacob.com.Variant; +import com.jacob.test.BaseTestCase; + +/** + * This test program demonstrates a weak in the setString(int[],String) method in SafeArray. + * To see the leak: + * + * You should see the Page File Usage History graph rise at te end of every cycle. + * Running the same program with setString(r,c,String) does not show the same symptoms + */ +public class SafeArrayLeak extends BaseTestCase { + + /** + * ---------------------------------------------------------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------------------------------------------------------- + */ + public void testLeakWithSetString() { + + ActiveXComponent xl = null; + Dispatch workbooks = null; + Dispatch workbook = null; + Dispatch workSheets = null; + Dispatch sheet = null; + Dispatch tabCells = null; + SafeArray sa = null; + + //-Dcom.jacob.autogc=true + System.out.println("Jacob version: "+JacobObject.getBuildVersion()); + + for(int t = 0; t < 10; t++) { + // look at a large range of cells + String position = "A7:DM8934"; + + try { + xl = new ActiveXComponent("Excel.Application"); + System.out.println("Excel version=" + xl.getProperty("Version")); + + xl.setProperty("Visible", new Variant(false)); + workbooks = xl.getProperty("Workbooks").toDispatch(); + + workbook = Dispatch.get(workbooks,"Add").toDispatch(); + + workSheets = Dispatch.get(workbook, "Worksheets").toDispatch(); + + sheet = Dispatch.get(workbook, "ActiveSheet").toDispatch(); + // grab the whole range specified above. + tabCells = Dispatch.invoke(sheet, "Range", Dispatch.Get, + new Object[] { position }, + new int[1]).toDispatch(); + + sa = Dispatch.get(tabCells, "Value").toSafeArray(true); + + System.out.println("Ub0=" + sa.getUBound(1)); // nbCol + System.out.println("Ub1=" + sa.getUBound(2)); // nbLgn + + // number of rows + int nbLgn = sa.getUBound(2); + // number of columns + int nbCol = sa.getUBound(1); + + int[] colLgn = new int[] { 0, 0 }; + + // now set a value on every cell in the range we retrieved + for(int i = 1; i <= nbLgn; i++) { + colLgn[1] = i; + + for(int j = 1; j <= nbCol; j++) { + colLgn[0] = j; + // this one works with out a leak 1.13-M3 + //sa.setString(j, i, "test"); + // This one leaks with 1.13-M3 and earlier + sa.setString(colLgn, "test"); + } + } + + Dispatch.put(tabCells, "Value", sa); + + Variant f = new Variant(false); + Dispatch.call(workbook, "Close", f); + System.out.println("Close"); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + + if (sa != null) { + try { + sa.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + sa = null; + } + } + + if (tabCells != null) { + try { + tabCells.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + tabCells = null; + } + } + + if (sheet != null) { + try { + sheet.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + sheet = null; + } + } + + if (workSheets != null) { + try { + workSheets.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + workSheets = null; + } + } + + if (workbook != null) { + try { + workbook.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + workbook = null; + } + } + + if (workbooks != null) { + try { + workbooks.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + workbooks = null; + } + } + + if (xl != null) { + try { + xl.invoke("Quit", new Variant[] {}); + } + catch(Exception e) { + e.printStackTrace(); + } + + try { + xl.safeRelease(); + } + catch(Exception e) { + e.printStackTrace(); + } + finally { + xl = null; + } + } + + ComThread.Release(); + } + } + } +}