1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-08-30 11:43:33 +02:00

Bug 572749: Try to always show error in English in exception

The ThrowNew JNI method requires the message to be encoded in
"modified UTF-8". The FormatMessage WinAPI method can return a string
using any encoding, so it needs to be converted to UTF-8 in order
to have it visible in the exception message.
To further help, try to extract the message in English and fall back
to the Windows installation language as a last resort.

Prefix the error message with the error code from the GetLastError()
function.

Contributed by STMicroelectronics

Change-Id: Id76ffd83e2d3ad1f061780c7ee0892c9b378649b
Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@st.com>
This commit is contained in:
Torbjörn SVENSSON 2021-04-08 17:32:53 +02:00 committed by Torbjörn Svensson
parent 549b9d00c0
commit 450e0cac52
10 changed files with 89 additions and 59 deletions

View file

@ -62,7 +62,7 @@ typedef struct _procInfo {
static int procCounter = 0; // Number of running processes static int procCounter = 0; // Number of running processes
// This is a VM helper // This is a VM helper
void ThrowByName(JNIEnv *env, const char *name, const char *msg); void ThrowByName(JNIEnv *env, const char *name, const wchar_t *msg);
// Creates _procInfo block for every launched process // Creates _procInfo block for every launched process
pProcInfo_t createProcInfo(); pProcInfo_t createProcInfo();
@ -213,7 +213,7 @@ static bool createCommandLine(JNIEnv *env, jobjectArray cmdarray, wchar_t **cmdL
} else { } else {
// malloc failed, clean up and return // malloc failed, clean up and return
va_end(ap); va_end(ap);
ThrowByName(env, "java/io/IOException", "Not enough memory"); ThrowByName(env, "java/io/IOException", L"Not enough memory");
return false; return false;
} }
} while (size <= required); } while (size <= required);
@ -229,7 +229,7 @@ static bool createCommandLine(JNIEnv *env, jobjectArray cmdarray, wchar_t **cmdL
required = nPos + len + 2; // 2 => space + \0 required = nPos + len + 2; // 2 => space + \0
if (required > 32 * 1024) { if (required > 32 * 1024) {
free(buffer); free(buffer);
ThrowByName(env, "java/io/IOException", "Command line too long"); ThrowByName(env, "java/io/IOException", L"Command line too long");
return false; return false;
} }
@ -248,7 +248,7 @@ static bool createCommandLine(JNIEnv *env, jobjectArray cmdarray, wchar_t **cmdL
} else { } else {
// Failed to realloc memory // Failed to realloc memory
free(buffer); free(buffer);
ThrowByName(env, "java/io/IOException", "Not enough memory"); ThrowByName(env, "java/io/IOException", L"Not enough memory");
return false; return false;
} }
} }
@ -270,7 +270,7 @@ static bool createCommandLine(JNIEnv *env, jobjectArray cmdarray, wchar_t **cmdL
(*env)->ReleaseStringChars(env, item, str); (*env)->ReleaseStringChars(env, item, str);
} else { } else {
free(buffer); free(buffer);
ThrowByName(env, "java/io/IOException", "Command line contained null string"); ThrowByName(env, "java/io/IOException", L"Command line contained null string");
return false; return false;
} }
} }
@ -307,7 +307,7 @@ static bool createEnvironmentBlock(JNIEnv *env, jobjectArray envp, wchar_t **blo
} else { } else {
free(buffer); free(buffer);
(*env)->ReleaseStringChars(env, item, str); (*env)->ReleaseStringChars(env, item, str);
ThrowByName(env, "java/io/IOException", "Not enough memory"); ThrowByName(env, "java/io/IOException", L"Not enough memory");
return false; return false;
} }
if (isTraceEnabled(CDT_TRACE_SPAWNER)) { if (isTraceEnabled(CDT_TRACE_SPAWNER)) {
@ -338,29 +338,29 @@ extern "C"
Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv *env, jobject process, jobjectArray cmdarray, Java_org_eclipse_cdt_utils_spawner_Spawner_exec0(JNIEnv *env, jobject process, jobjectArray cmdarray,
jobjectArray envp, jstring dir, jobjectArray channels) { jobjectArray envp, jstring dir, jobjectArray channels) {
if (!channels) { if (!channels) {
ThrowByName(env, "java/io/IOException", "Channels can't be null"); ThrowByName(env, "java/io/IOException", L"Channels can't be null");
return 0; return 0;
} }
jclass channelClass = (*env)->FindClass(env, "org/eclipse/cdt/utils/spawner/Spawner$WinChannel"); jclass channelClass = (*env)->FindClass(env, "org/eclipse/cdt/utils/spawner/Spawner$WinChannel");
if (!channelClass) { if (!channelClass) {
ThrowByName(env, "java/io/IOException", "Unable to find channel class"); ThrowByName(env, "java/io/IOException", L"Unable to find channel class");
return 0; return 0;
} }
jmethodID channelConstructor = (*env)->GetMethodID(env, channelClass, "<init>", "(J)V"); jmethodID channelConstructor = (*env)->GetMethodID(env, channelClass, "<init>", "(J)V");
if (!channelConstructor) { if (!channelConstructor) {
ThrowByName(env, "java/io/IOException", "Unable to find channel constructor"); ThrowByName(env, "java/io/IOException", L"Unable to find channel constructor");
return 0; return 0;
} }
if ((HIBYTE(LOWORD(GetVersion()))) & 0x80) { if ((HIBYTE(LOWORD(GetVersion()))) & 0x80) {
ThrowByName(env, "java/io/IOException", "Does not support Windows 3.1/95/98/Me"); ThrowByName(env, "java/io/IOException", L"Does not support Windows 3.1/95/98/Me");
return 0; return 0;
} }
if (!cmdarray) { if (!cmdarray) {
ThrowByName(env, "java/lang/NullPointerException", "No command line specified"); ThrowByName(env, "java/lang/NullPointerException", L"No command line specified");
return 0; return 0;
} }
@ -376,14 +376,14 @@ extern "C"
!createStandardNamedPipe(&stdHandles[1], STD_OUTPUT_HANDLE, pid, nLocalCounter) || !createStandardNamedPipe(&stdHandles[1], STD_OUTPUT_HANDLE, pid, nLocalCounter) ||
!createStandardNamedPipe(&stdHandles[2], STD_ERROR_HANDLE, pid, nLocalCounter)) { !createStandardNamedPipe(&stdHandles[2], STD_ERROR_HANDLE, pid, nLocalCounter)) {
CLOSE_HANDLES(stdHandles); CLOSE_HANDLES(stdHandles);
ThrowByName(env, "java/io/IOException", "CreatePipe"); ThrowByName(env, "java/io/IOException", L"CreatePipe");
return 0; return 0;
} }
pProcInfo_t pCurProcInfo = createProcInfo(); pProcInfo_t pCurProcInfo = createProcInfo();
if (!pCurProcInfo) { if (!pCurProcInfo) {
CLOSE_HANDLES(stdHandles); CLOSE_HANDLES(stdHandles);
ThrowByName(env, "java/io/IOException", "Too many processes"); ThrowByName(env, "java/io/IOException", L"Too many processes");
return 0; return 0;
} }
@ -395,7 +395,7 @@ extern "C"
!createNamedEvent(&pCurProcInfo->eventCtrlc, FALSE, L"SACtrlc", pid, nLocalCounter)) { !createNamedEvent(&pCurProcInfo->eventCtrlc, FALSE, L"SACtrlc", pid, nLocalCounter)) {
cleanUpProcBlock(pCurProcInfo); cleanUpProcBlock(pCurProcInfo);
CLOSE_HANDLES(stdHandles); CLOSE_HANDLES(stdHandles);
ThrowByName(env, "java/io/IOException", "Cannot create event"); ThrowByName(env, "java/io/IOException", L"Cannot create event");
return 0; return 0;
} }
@ -463,6 +463,8 @@ extern "C"
&si, /* (in) startup information */ &si, /* (in) startup information */
&pi); /* (out) process information */ &pi); /* (out) process information */
const DWORD error_CreateProcessW = GetLastError();
free(cwd); free(cwd);
free(envBlock); free(envBlock);
free(cmdLine); free(cmdLine);
@ -483,7 +485,7 @@ extern "C"
} }
cleanUpProcBlock(pCurProcInfo); cleanUpProcBlock(pCurProcInfo);
CLOSE_HANDLES(stdHandles); CLOSE_HANDLES(stdHandles);
ThrowByName(env, "java/io/IOException", "Launching failed"); ThrowByName(env, "java/io/IOException", L"Launching failed");
if (isTraceEnabled(CDT_TRACE_SPAWNER)) { if (isTraceEnabled(CDT_TRACE_SPAWNER)) {
cdtTrace(L"Process failed\n"); cdtTrace(L"Process failed\n");
} }
@ -508,14 +510,12 @@ extern "C"
} }
LeaveCriticalSection(&cs); LeaveCriticalSection(&cs);
} else { // Launching error } else { // Launching error
char *lpMsgBuf; wchar_t *lpMsgBuf;
CLOSE_HANDLES(stdHandles); CLOSE_HANDLES(stdHandles);
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, lpMsgBuf = formatWinErrorCode(error_CreateProcessW);
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(char *)&lpMsgBuf, 0, NULL);
ThrowByName(env, "java/io/IOException", lpMsgBuf); ThrowByName(env, "java/io/IOException", lpMsgBuf);
// Free the buffer. // Free the buffer.
LocalFree(lpMsgBuf); free(lpMsgBuf);
cleanUpProcBlock(pCurProcInfo); cleanUpProcBlock(pCurProcInfo);
ret = -1; ret = -1;
} }
@ -580,6 +580,7 @@ extern "C"
cwd, /* change to the new current directory */ cwd, /* change to the new current directory */
&si, /* (in) startup information */ &si, /* (in) startup information */
&pi); /* (out) process information */ &pi); /* (out) process information */
const DWORD error_CreateProcessW = GetLastError();
free(cwd); free(cwd);
free(cmdLine); free(cmdLine);
@ -591,14 +592,10 @@ extern "C"
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
ret = (long)pi.dwProcessId; // hProcess; ret = (long)pi.dwProcessId; // hProcess;
} else { // error } else { // error
char *lpMsgBuf; wchar_t *lpMsgBuf = formatWinErrorCode(error_CreateProcessW);
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(wchar_t *)&lpMsgBuf, 0, NULL);
ThrowByName(env, "java/io/IOException", lpMsgBuf); ThrowByName(env, "java/io/IOException", lpMsgBuf);
// Free the buffer. // Free the buffer.
LocalFree(lpMsgBuf); free(lpMsgBuf);
ret = -1; ret = -1;
} }
@ -730,13 +727,23 @@ extern "C"
// Throws Java exception (will be trapped by VM). // Throws Java exception (will be trapped by VM).
// Arguments: // Arguments:
// [in] name - name of exception class // [in] name - name of exception class
// [in] message to assign thi event // [in] message to assign the event
///////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////
void ThrowByName(JNIEnv *env, const char *name, const char *msg) { void ThrowByName(JNIEnv *env, const char *name, const wchar_t *msg) {
jclass cls = (*env)->FindClass(env, name); jclass cls = (*env)->FindClass(env, name);
if (cls) { /* Otherwise an exception has already been thrown */ if (cls) { /* Otherwise an exception has already been thrown */
(*env)->ThrowNew(env, cls, msg); size_t msgLen = wcslen(msg);
int nChars = WideCharToMultiByte(CP_UTF8, 0, msg, msgLen, NULL, 0, NULL, NULL);
if (nChars == 0) {
(*env)->ThrowNew(env, cls, "");
} else {
// ThrowNew expects message to be encoded in "modified UTF-8"
char *buf = (char *)calloc(nChars + 1, sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, msg, msgLen, buf, nChars, NULL, NULL);
(*env)->ThrowNew(env, cls, buf);
free(buf);
}
} }
/* It's a good practice to clean up the local references. */ /* It's a good practice to clean up the local references. */

View file

@ -25,25 +25,25 @@
#include <org_eclipse_cdt_utils_spawner_Spawner.h> #include <org_eclipse_cdt_utils_spawner_Spawner.h>
void ThrowByName(JNIEnv *env, const char *name, const char *msg); void ThrowByName(JNIEnv *env, const char *name, const wchar_t *msg);
#define BUFF_SIZE (1024) #define BUFF_SIZE (1024)
static HANDLE channelToHandle(JNIEnv *env, jobject channel) { static HANDLE channelToHandle(JNIEnv *env, jobject channel) {
if (!channel) { if (!channel) {
ThrowByName(env, "java/io/IOException", "Invalid channel object"); ThrowByName(env, "java/io/IOException", L"Invalid channel object");
return NULL; return NULL;
} }
jclass cls = (*env)->GetObjectClass(env, channel); jclass cls = (*env)->GetObjectClass(env, channel);
if (!cls) { if (!cls) {
ThrowByName(env, "java/io/IOException", "Unable to get channel class"); ThrowByName(env, "java/io/IOException", L"Unable to get channel class");
return NULL; return NULL;
} }
jfieldID fid = (*env)->GetFieldID(env, cls, "handle", "J"); jfieldID fid = (*env)->GetFieldID(env, cls, "handle", "J");
if (!fid) { if (!fid) {
ThrowByName(env, "java/io/IOException", "Unable to find handle"); ThrowByName(env, "java/io/IOException", L"Unable to find handle");
return NULL; return NULL;
} }
@ -70,14 +70,10 @@ extern "C"
NULL); // unnamed event object NULL); // unnamed event object
if (!overlapped.hEvent) { if (!overlapped.hEvent) {
char *lpMsgBuf; wchar_t *lpMsgBuf = formatWinErrorCode(GetLastError());
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(wchar_t *)&lpMsgBuf, 0, NULL);
ThrowByName(env, "java/io/IOException", lpMsgBuf); ThrowByName(env, "java/io/IOException", lpMsgBuf);
// Free the buffer. // Free the buffer.
LocalFree(lpMsgBuf); free(lpMsgBuf);
} }
if (isTraceEnabled(CDT_TRACE_SPAWNER) && isTraceEnabled(CDT_TRACE_SPAWNER_READ_REPORT)) { if (isTraceEnabled(CDT_TRACE_SPAWNER) && isTraceEnabled(CDT_TRACE_SPAWNER_READ_REPORT)) {
@ -103,19 +99,14 @@ extern "C"
break; break;
} }
if (err != 0) { if (err != 0) {
char *lpMsgBuf;
if (isTraceEnabled(CDT_TRACE_SPAWNER)) { if (isTraceEnabled(CDT_TRACE_SPAWNER)) {
cdtTrace(L"Read failed - %p, error %i\n", handle, err); cdtTrace(L"Read failed - %p, error %i\n", handle, err);
} }
if (err != if (err !=
ERROR_MORE_DATA) { // Otherwise error means just that there are more data than buffer can accept ERROR_MORE_DATA) { // Otherwise error means just that there are more data than buffer can accept
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | wchar_t *lpMsgBuf = formatWinErrorCode(err);
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(wchar_t *)&lpMsgBuf, 0, NULL);
ThrowByName(env, "java/io/IOException", lpMsgBuf); ThrowByName(env, "java/io/IOException", lpMsgBuf);
LocalFree(lpMsgBuf); free(lpMsgBuf);
nBuffOffset = 0; nBuffOffset = 0;
break; break;
} else { } else {
@ -200,13 +191,9 @@ extern "C"
DWORD nNumberOfBytesWritten; DWORD nNumberOfBytesWritten;
(*env)->GetByteArrayRegion(env, buf, nBuffOffset, nNumberOfBytesToWrite, tmpBuf); (*env)->GetByteArrayRegion(env, buf, nBuffOffset, nNumberOfBytesToWrite, tmpBuf);
if (0 == WriteFile(handle, tmpBuf, nNumberOfBytesToWrite, &nNumberOfBytesWritten, NULL)) { if (0 == WriteFile(handle, tmpBuf, nNumberOfBytesToWrite, &nNumberOfBytesWritten, NULL)) {
char *lpMsgBuf; wchar_t *lpMsgBuf = formatWinErrorCode(GetLastError());
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(wchar_t *)&lpMsgBuf, 0, NULL);
ThrowByName(env, "java/io/IOException", lpMsgBuf); ThrowByName(env, "java/io/IOException", lpMsgBuf);
LocalFree(lpMsgBuf); free(lpMsgBuf);
return 0; return 0;
} }
nBuffOffset += nNumberOfBytesWritten; nBuffOffset += nNumberOfBytesWritten;

View file

@ -479,12 +479,9 @@ int main() {
} }
void DisplayErrorMessage() { void DisplayErrorMessage() {
wchar_t *lpMsgBuf; wchar_t *lpMsgBuf = formatWinErrorCode(GetLastError());
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(wchar_t *)&lpMsgBuf, 0, NULL);
OutputDebugStringW(lpMsgBuf); OutputDebugStringW(lpMsgBuf);
// Free the buffer. // Free the buffer.
LocalFree(lpMsgBuf); free(lpMsgBuf);
} }
//////////////////////////////// End of File ////////////////////////////////// //////////////////////////////// End of File //////////////////////////////////

View file

@ -151,3 +151,39 @@ int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace
return j; return j;
} }
wchar_t *formatWinErrorCode(DWORD messageId) {
const wchar_t *NULL_STR = L"(null)";
size_t size = 0;
wchar_t *msg = NULL;
DWORD langId[] = {
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // US English
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), // Any English
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // User default language
0 // Let FormatMessage lookup the right language
};
wchar_t *winBuf = NULL;
/* Format the message */
for (size_t i = 0; i < sizeof(langId) / sizeof(langId[0]); i++) {
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, messageId, langId[i], (wchar_t *)&winBuf, 0, NULL) == 0) {
winBuf = NULL;
}
if (winBuf != NULL) {
break;
}
}
/* Prefix the message */
size = 100 + wcslen(winBuf ? winBuf : NULL_STR);
msg = (wchar_t *)calloc(size + 1, sizeof(wchar_t));
if (msg) {
snwprintf(msg, size, L"Code 0x%lx: %s", (unsigned long)messageId, (winBuf ? winBuf : NULL_STR));
}
LocalFree(winBuf);
return msg;
}

View file

@ -48,4 +48,7 @@ void cdtTrace(const wchar_t *fmt, ...);
int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace); int copyTo(wchar_t *target, const wchar_t *source, int cpyLength, int availSpace);
// Returned pointer shall be freed with free().
wchar_t *formatWinErrorCode(DWORD messageId);
#endif /* UTIL_H */ #endif /* UTIL_H */

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: %fragmentName.win32.x86_64 Bundle-Name: %fragmentName.win32.x86_64
Bundle-SymbolicName: org.eclipse.cdt.core.win32.x86_64;singleton:=true Bundle-SymbolicName: org.eclipse.cdt.core.win32.x86_64;singleton:=true
Bundle-Version: 6.0.100.qualifier Bundle-Version: 6.0.200.qualifier
Fragment-Host: org.eclipse.cdt.core.native;bundle-version="[6.0.0,7.0.0)" Fragment-Host: org.eclipse.cdt.core.native;bundle-version="[6.0.0,7.0.0)"
Eclipse-PlatformFilter: (&(osgi.os=win32)(osgi.arch=x86_64)) Eclipse-PlatformFilter: (&(osgi.os=win32)(osgi.arch=x86_64))
Bundle-Vendor: %providerName Bundle-Vendor: %providerName

View file

@ -21,7 +21,7 @@
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<version>6.0.100-SNAPSHOT</version> <version>6.0.200-SNAPSHOT</version>
<artifactId>org.eclipse.cdt.core.win32.x86_64</artifactId> <artifactId>org.eclipse.cdt.core.win32.x86_64</artifactId>
<packaging>eclipse-plugin</packaging> <packaging>eclipse-plugin</packaging>