diff --git a/docs/ReleaseNotes.html b/docs/ReleaseNotes.html index 907e765..e43e019 100644 --- a/docs/ReleaseNotes.html +++ b/docs/ReleaseNotes.html @@ -20,15 +20,15 @@ Bugs - - 1775889 - (M4) Fixed leak SafeArray setString(int[],value) and other setString() methods - 1793362 (M5) ERROR_MORE_DATA causes failures. Fix submitted for defect found while porting Jameleon to use current release of Jacob. + + 1775889 + (M4) Fixed leak SafeArray setString(int[],value) and other setString() methods +     @@ -37,11 +37,12 @@ Patches - 1709841 - (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. - + 1794811 + (M5) Support Unicode strings in COM failure messages + + + 1793346 + (M5) Replaced use of deprecated API and removed unused variables. 1701995 @@ -51,8 +52,11 @@ - 1793346 - (M5) Replaced use of deprecated API and removed unused variables. + 1709841 + (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. +   @@ -61,15 +65,15 @@ Feature Requests + + 1772783 + (M4) Added VT_DECIMAL support for BigDecimals whose scale less than 28 + 1761727 (M3) unittest directory test programs converted to JUnit 3.8.1. New ANT target created to run all unit tests. - - 1772783 - (M4) Added VT_DECIMAL support for BigDecimals whose scale less than 28 -     diff --git a/jni/Dispatch.cpp b/jni/Dispatch.cpp index 285427d..81868e6 100644 --- a/jni/Dispatch.cpp +++ b/jni/Dispatch.cpp @@ -322,28 +322,41 @@ static char* BasicToCharString(const BSTR inBasicString) size_t convertedSize; ::wcstombs_s(&convertedSize, charString, charStrSize, inBasicString, charStrSize); } - else + else + { charString = ::_strdup(""); - + } return charString; } -static char* CreateErrorMsgFromResult(HRESULT inResult) +static wchar_t* CreateErrorMsgFromResult(HRESULT inResult) { - char* msg = NULL; - ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + wchar_t* msg = NULL; + ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, inResult,MAKELANGID(LANG_NEUTRAL, - SUBLANG_DEFAULT), (LPTSTR) &msg, 0, NULL); + SUBLANG_DEFAULT), (LPWSTR) &msg, 0, NULL); if (msg == NULL) - msg = ::_strdup("An unknown COM error has occured."); + { + const wchar_t* message_text = L"An unknown COM error has occured."; + size_t bufferLength = (wcslen(message_text) + 1) * sizeof(wchar_t); + msg = (wchar_t*) ::LocalAlloc(LPTR, bufferLength); + wcscpy_s(msg, bufferLength, message_text); + } return msg; } -static char* CreateErrorMsgFromInfo(HRESULT inResult, EXCEPINFO* ioInfo, +static wchar_t* CreateErrorMsgFromInfo(HRESULT inResult, EXCEPINFO* ioInfo, const char* methName) { - char* msg = NULL; + wchar_t* msg = NULL; + size_t methNameWSize = 0; + + mbstowcs_s(&methNameWSize, NULL, 0, methName, _TRUNCATE); + + wchar_t* methNameW = new wchar_t[methNameWSize]; + + mbstowcs_s(NULL, methNameW, methNameWSize, methName, _TRUNCATE); // If this is a dispatch exception (triggered by an Invoke message), // then we have to take some additional steps to process the error @@ -356,35 +369,34 @@ static char* CreateErrorMsgFromInfo(HRESULT inResult, EXCEPINFO* ioInfo, (*(ioInfo->pfnDeferredFillIn))(ioInfo); // Build the error message from exception information content. - char* source = ::BasicToCharString(ioInfo->bstrSource); - char* desc = ::BasicToCharString(ioInfo->bstrDescription); - const size_t MSG_LEN = ::strlen(methName) + ::strlen(source) + ::strlen(desc) + 128; - msg = new char[MSG_LEN]; - ::strncpy_s(msg, MSG_LEN, "Invoke of: ", strlen("Invoke of: ")); - ::strncat_s(msg, MSG_LEN, methName, strlen(methName)); - ::strncat_s(msg, MSG_LEN, "\nSource: ", strlen("\nSource: ")); - ::strncat_s(msg, MSG_LEN, source, strlen(source)); - ::strncat_s(msg, MSG_LEN, "\nDescription: ", strlen("\nDescription: ")); - ::strncat_s(msg, MSG_LEN, desc, strlen(desc)); - ::strncat_s(msg, MSG_LEN, "\n", strlen("\n")); - delete source; - delete desc; + int sourceLen = SysStringLen(ioInfo->bstrSource); + int descLen = SysStringLen(ioInfo->bstrDescription); + const size_t MSG_LEN = ::wcslen(methNameW) + sourceLen + descLen + 128; + msg = new wchar_t[MSG_LEN]; + ::wcsncpy_s(msg, MSG_LEN, L"Invoke of: ", wcslen(L"Invoke of: ")); + ::wcsncat_s(msg, MSG_LEN, methNameW, wcslen(methNameW)); + ::wcsncat_s(msg, MSG_LEN, L"\nSource: ", wcslen(L"\nSource: ")); + ::wcsncat_s(msg, MSG_LEN, ioInfo->bstrSource, sourceLen); + ::wcsncat_s(msg, MSG_LEN, L"\nDescription: ", wcslen(L"\nDescription: ")); + ::wcsncat_s(msg, MSG_LEN, ioInfo->bstrDescription, descLen); + ::wcsncat_s(msg, MSG_LEN, L"\n", wcslen(L"\n")); } else { - char* msg2 = CreateErrorMsgFromResult(inResult); - const size_t MSG_LEN = ::strlen(methName) + ::strlen(msg2) + 128; - msg = new char[MSG_LEN]; - ::strncpy_s(msg, MSG_LEN, - "A COM exception has been encountered:\nAt Invoke of: ", - strlen("A COM exception has been encountered:\nAt Invoke of: ")); - ::strncat_s(msg, MSG_LEN, methName, strlen(methName)); - ::strncat_s(msg, MSG_LEN, "\nDescription: ", strlen("\nDescription: ")); - ::strncat_s(msg, MSG_LEN, msg2, strlen(msg2)); + wchar_t* msg2 = CreateErrorMsgFromResult(inResult); + const size_t MSG_LEN = ::wcslen(methNameW) + ::wcslen(msg2) + 256; + msg = new wchar_t[MSG_LEN]; + ::wcsncpy_s(msg, MSG_LEN, + L"A COM exception has been encountered:\nAt Invoke of: ", + wcslen(L"A COM exception has been encountered:\nAt Invoke of: ")); + ::wcsncat_s(msg, MSG_LEN, methNameW, wcslen(methNameW)); + ::wcsncat_s(msg, MSG_LEN, L"\nDescription: ", wcslen(L"\nDescription: ")); + ::wcsncat_s(msg, MSG_LEN, msg2, wcslen(msg2)); // jacob-msg 1075 - SF 1053872 : Documentation says "use LocalFree"!! //delete msg2; LocalFree(msg2); } + delete methNameW; return msg; } @@ -505,7 +517,7 @@ JNIEXPORT jobject JNICALL Java_com_jacob_com_Dispatch_invokev // check for error and display a somewhat verbose error message if (!SUCCEEDED(hr)) { // two buffers that may have to be freed later - char *buf = NULL; + wchar_t *buf = NULL; char *dispIdAsName = NULL; // this method can get called with a name or a dispatch id // we need to handle both SF 1114159 @@ -534,7 +546,7 @@ JNIEXPORT jobject JNICALL Java_com_jacob_com_Dispatch_invokev } } - ThrowComFail(env, buf, hr); + ThrowComFailUnicode(env, buf, hr); if (buf) delete buf; if (dispIdAsName) delete dispIdAsName; return NULL; diff --git a/jni/util.cpp b/jni/util.cpp index 268bd27..3ef1e5c 100644 --- a/jni/util.cpp +++ b/jni/util.cpp @@ -36,12 +36,28 @@ void ThrowComFail(JNIEnv *env, const char* desc, jint hr) // call the constructor that takes hr and message jmethodID failCons = env->GetMethodID(failClass, "", "(ILjava/lang/String;)V"); - if (!desc) desc = "Java/COM Error"; + if (!desc) { + desc = "Java/COM Error"; + } jstring js = env->NewStringUTF(desc); jthrowable fail = (jthrowable)env->NewObject(failClass, failCons, hr, js); env->Throw(fail); } +void ThrowComFailUnicode(JNIEnv *env, const wchar_t* desc, jint hr) +{ + if (!desc) { + ThrowComFail(env, "Java/COM Error", hr); + } + jclass failClass = env->FindClass("com/jacob/com/ComFailException"); + // call the constructor that takes hr and message + jmethodID failCons = + env->GetMethodID(failClass, "", "(ILjava/lang/String;)V"); + jstring js = env->NewString((const jchar *) desc, wcslen(desc)); + jthrowable fail = (jthrowable)env->NewObject(failClass, failCons, hr, js); + env->Throw(fail); +} + // if env's are different throw on the 1st env int CheckEnv(JNIEnv *env1, JNIEnv *env2) { diff --git a/jni/util.h b/jni/util.h index 2fc570c..863ef0e 100644 --- a/jni/util.h +++ b/jni/util.h @@ -21,6 +21,7 @@ extern "C" { VARIANT *extractVariant(JNIEnv *env, jobject arg); void ThrowComFail(JNIEnv *env, const char* desc, jint hr); + void ThrowComFailUnicode(JNIEnv *env, const wchar_t* desc, jint hr); IDispatch *extractDispatch(JNIEnv *env, jobject arg); SAFEARRAY *extractSA(JNIEnv *env, jobject arg); void setSA(JNIEnv *env, jobject arg, SAFEARRAY *sa, int copy); diff --git a/unittest/com/jacob/test/errors/UnicodeErrorTest.java b/unittest/com/jacob/test/errors/UnicodeErrorTest.java new file mode 100644 index 0000000..d6f60a7 --- /dev/null +++ b/unittest/com/jacob/test/errors/UnicodeErrorTest.java @@ -0,0 +1,28 @@ +package com.jacob.test.errors; + +import com.jacob.test.BaseTestCase; +import com.jacob.activeX.ActiveXComponent; +import com.jacob.com.ComException; + +/** + * This test verifies patch SF 1794811 . + * It shows how unicode filenames throw exceptions in 1.13M4 and earlier. + * @author justme84 + * + */ +public class UnicodeErrorTest extends BaseTestCase { + + public void testUnicodeCharactersInErrorMessage() { + ActiveXComponent application = new ActiveXComponent("Word.Application"); + ActiveXComponent documents = application.getPropertyAsComponent("Documents"); + String fileName = "abc\u0411\u0412\u0413\u0414def"; + try { + documents.invoke("Open", fileName); + fail("Should have thrown an exception"); + } catch (ComException e) { + assertTrue("Error message should contain file name with unicode " + + "characters in it. "+e.getMessage(), + e.getMessage().indexOf(fileName) > 0); + } + } +} \ No newline at end of file