From ea3d51e9dce14c9b081d3d4049fcb4223dc5b253 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sat, 29 Dec 2018 22:58:37 +0100 Subject: [PATCH 01/36] Handle both an ASCII (UTF8) and a WIDE (UTF16) version of image/vm name/path Note: the Microsoft windows API mostly uses the W version (for enabling internationalized image name/path) the image uses UTF8 encoded bytes string for communication with the VM (this is best for compatibility with Unix/Mac) The idea here is that the implementation maintains both versions of the UTF8 and UTF16 path/name The appropriate macro returning a TCHAR * are also provided. This is in order to support the generic version using TCHAR, which are normally used to ease transition to UNICODE. Note about string length: No effort has been made so far to support long path names for image and VM. The path is limited to MAX_PATH in UTF16. UTF8 can eventually consume more characters than UTF16 (not necessarily more bytes). Thus, the ASCII version has been made longer (MAX_PATH_UTF8) in order to avoid an even more restrictive limit. --- platforms/win32/vm/sqWin32.h | 45 +++++++++++--- platforms/win32/vm/sqWin32Main.c | 31 +++++----- platforms/win32/vm/sqWin32Prefs.c | 12 ++-- platforms/win32/vm/sqWin32Service.c | 19 +++--- platforms/win32/vm/sqWin32Window.c | 96 +++++++++++++++++------------ 5 files changed, 124 insertions(+), 79 deletions(-) diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index e48fcfd04d..3fbec49ebe 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -259,21 +259,46 @@ int reverse_image_words(unsigned int *dst, unsigned int *src,int depth, int widt /********************************************************/ /* Declarations we may need by other modules */ /********************************************************/ -extern char imageName[]; /* full path and name to image */ -extern TCHAR imagePath[]; /* full path to image */ -extern TCHAR vmPath[]; /* full path to interpreter's directory */ -extern TCHAR vmName[]; /* name of the interpreter's executable */ -extern char windowTitle[]; /* window title string */ -extern char vmBuildString[]; /* the vm build string */ -extern TCHAR windowClassName[]; /* class name for the window */ + +/* Note: a character can require up to 4 bytes in UTF8 encoding + But the expansion from UTF16 -> UTF8 is never more than 3 bytes for 1 short + U+ 0000-U+ 007F - 1byte in utf8, 1 short in utf16. + U+ 0080-U+ 07FF - 2byte in utf8, 1 short in utf16. + U+ 0800-U+ FFFF - 3byte in utf8, 1 short in utf16. + U+10000-U+10FFFF - 4byte in utf8, 2 short in utf16. +*/ +#define MAX_PATH_UTF8 (MAX_PATH*3) + +extern char imageName []; /* full path and name to image - UTF8 */ +extern WCHAR imageNameW[]; /* full path and name to image - UTF16 */ +extern char imagePathA[]; /* full path to image - UTF8 */ +extern WCHAR imagePathW[]; /* full path to image - UTF16 */ +extern char vmPathA[]; /* full path to interpreter's directory - UTF8 */ +extern WCHAR vmPathW[]; /* full path to interpreter's directory - UTF16 */ +extern char vmNameA[]; /* name of the interpreter's executable - UTF8 */ +extern WCHAR vmNameW[]; /* name of the interpreter's executable - UTF16 */ +extern char windowTitle[]; /* window title string - UTF8 */ +extern char vmBuildString[]; /* the vm build string */ +extern TCHAR windowClassName[]; /* class name for the window */ + +#ifdef UNICODE +#define imageNameT imageNameW /* define the generic TCHAR* version */ +#define imagePath imagePathW +#define vmName vmNameW +#define vmPath vmPathW +#else +#define imageNameT imageName +#define imagePath imagePathA +#define vmName vmNameA +#define vmPath vmPathA +#endif extern UINT SQ_LAUNCH_DROP; extern const TCHAR U_ON[]; extern const TCHAR U_OFF[]; extern const TCHAR U_GLOBAL[]; -extern const TCHAR U_SLASH[]; -extern const TCHAR U_BACKSLASH[]; +extern const WCHAR W_BACKSLASH[]; #ifndef NO_PREFERENCES extern HMENU vmPrefsMenu; /* preferences menu */ @@ -380,7 +405,7 @@ void vprintLastError(TCHAR *fmt, ...); /******************************************************/ /* Misc functions */ /******************************************************/ -DWORD SqueakImageLength(TCHAR *fileName); +DWORD SqueakImageLength(WCHAR *fileName); int isLocalFileName(TCHAR *fileName); #ifndef NO_PLUGIN_SUPPORT diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 7f292669eb..31266e1211 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -130,7 +130,7 @@ TCHAR consoleBuffer[4096]; char stderrName[MAX_PATH+1]; char stdoutName[MAX_PATH+1]; -static char vmLogDirA[MAX_PATH]; +static char vmLogDirA[MAX_PATH_UTF8]; static WCHAR vmLogDirW[MAX_PATH]; TCHAR *logName = TEXT(""); /* full path and name to log file */ @@ -462,7 +462,7 @@ char *ioGetLogDirectory(void) { } sqInt ioSetLogDirectoryOfSize(void* lblIndex, sqInt sz) { - if (sz >= MAX_PATH) return 0; + if (sz >= MAX_PATH_UTF8) return 0; strncpy(vmLogDirA, lblIndex, sz); vmLogDirA[sz] = 0; MultiByteToWideChar(CP_UTF8, 0, vmLogDirA, -1, vmLogDirW, MAX_PATH); @@ -1405,7 +1405,7 @@ sqImageFile findEmbeddedImage(void) { int start; int length; - f = sqImageFileOpen(vmName, "rb"); + f = sqImageFileOpen(vmNameA, "rb"); if(!f) { MessageBox(0,"Error opening VM",VM_NAME,MB_OK); return 0; @@ -1441,6 +1441,7 @@ sqImageFile findEmbeddedImage(void) { /* Gotcha! */ sqImageFileSeek(f, sqImageFilePosition(f) - 4); strcpy(imageName, name); + MultiByteToWideChar(CP_UTF8,0,imageName,-1,imageNameW,MAX_PATH,NULL,NULL); imageSize = endMarker - sqImageFilePosition(f); return f; } @@ -1577,7 +1578,7 @@ sqMain(int argc, char *argv[]) } SetupFilesAndPath(); - ioSetLogDirectoryOfSize(vmPath, strlen(vmPath)); + ioSetLogDirectoryOfSize(vmPathA, strlen(vmPathA)); /* release resources on exit */ atexit(Cleanup); @@ -1620,7 +1621,7 @@ sqMain(int argc, char *argv[]) if(!imageFile) { - imageSize = SqueakImageLength(toUnicode(imageName)); + imageSize = SqueakImageLength(imageNameW); if(imageSize == 0) printUsage(2); } @@ -1650,13 +1651,13 @@ sqMain(int argc, char *argv[]) #if !NewspeakVM /* set the CWD to the image location */ - if(*imageName) { - char path[MAX_PATH+1], *ptr; - strcpy(path,imageName); - ptr = strrchr(path, '\\'); + if(*imageNameW) { + WCHAR path[MAX_PATH+1], *ptr; + wcsncpy(path,imageNameW,MAX_PATH); + ptr = wcsrchr(path, '\\'); if(ptr) { - *ptr = 0; - SetCurrentDirectory(path); + *ptr = 0; + SetCurrentDirectoryW(path); } } #endif /* !NewspeakVM */ @@ -1723,9 +1724,8 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) /* fetch us the name of the executable */ { - WCHAR vmNameW[MAX_PATH]; GetModuleFileNameW(hInst, vmNameW, MAX_PATH); - WideCharToMultiByte(CP_UTF8, 0, vmNameW, -1, vmName, MAX_PATH, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, vmNameW, -1, vmNameA, MAX_PATH_UTF8, NULL, NULL); } /* parse the command line into the unix-style argc, argv, converting to * UTF-8 on the way. */ @@ -2086,7 +2086,7 @@ SubsystemType() IMAGE_OPTIONAL_HEADER image_optional_header; /* Open the reference file. */ - hImage = CreateFile(vmName, + hImage = CreateFileW(vmNameW, GENERIC_READ, FILE_SHARE_READ, NULL, @@ -2150,7 +2150,8 @@ parseGenericArgs(int argc, char *argv[]) if (*imageName == 0) { /* only try to use image name if none is provided */ if (*argv[0] && IsImage(argv[0])) { - strcpy(imageName, argv[0]); + strncpy(imageName, argv[0],MAX_PATH_UTF8); + MultiByteToWideChar(CP_UTF8, 0, imageName, -1, imageNameW, MAX_PATH); /* if provided, the image is a vm argument. */ vmOptions[numOptionsVM++] = argv[0]; } diff --git a/platforms/win32/vm/sqWin32Prefs.c b/platforms/win32/vm/sqWin32Prefs.c index b6339803a4..dd2ceb917c 100644 --- a/platforms/win32/vm/sqWin32Prefs.c +++ b/platforms/win32/vm/sqWin32Prefs.c @@ -194,15 +194,17 @@ void LoadPreferences() /* get image file name from ini file */ size = GetPrivateProfileString(U_GLOBAL, TEXT("ImageFile"), - TEXT(""), imageName, MAX_PATH, squeakIniName); + TEXT(""), imageNameT, MAX_PATH, squeakIniName); if(size > 0) { if( !(imageName[0] == '\\' && imageName[1] == '\\') && !(imageName[1] == ':' && imageName[2] == '\\')) { /* make the path relative to VM directory */ - lstrcpy(imageName, vmName); - (lstrrchr(imageName,U_BACKSLASH[0]))[1] = 0; - size = lstrlen(imageName); + strcpy(imageName , vmNameA); + wcscpy(imageNameW, vmNameW); + (strrchr(imageName ,'\\' ))[1] = 0; + (wcsrchr(imageNameW, W_BACKSLASH[0]))[1] = 0; + size = lstrlen(imageNameT); size = GetPrivateProfileString(U_GLOBAL, TEXT("ImageFile"), - TEXT(""), imageName + size, MAX_PATH - size, squeakIniName); + TEXT(""), imageNameT + size, MAX_PATH - size, squeakIniName); } } diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index 95470d8ae5..5ba48464bc 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -413,12 +413,12 @@ void sqServiceInstall(void) return; } /* Add the image name to the subkey. */ - ok = RegSetValueEx(hk, /* subkey handle */ - TEXT("Image"), /* value name */ - 0, /* must be zero */ - REG_EXPAND_SZ, /* value type */ - (LPBYTE) imageName, /* address of value data */ - strlen(imageName) + 1); /* length of value data */ + ok = RegSetValueExW(hk, /* subkey handle */ + L"Image", /* value name */ + 0, /* must be zero */ + REG_EXPAND_SZ, /* value type */ + (LPBYTE) imageNameW, /* address of value data */ + (wcslen(imageNameW)+1)*sizeof(WCHAR)); /* length of value data */ if(ok != ERROR_SUCCESS) { printLastError(TEXT("RegSetValueEX failed")); @@ -517,14 +517,15 @@ DWORD WINAPI sqThreadMain(DWORD ignored) TerminateThread(GetCurrentThread(), 0); } /* Read the image name from the subkey. */ - dwSize = MAX_PATH; - ok = RegQueryValueEx(hk,TEXT("Image"),NULL, &dwType, (LPBYTE) imageName, &dwSize); + dwSize = MAX_PATH*sizeof(WCHAR); + ok = RegQueryValueExW(hk,L"Image",NULL, &dwType, (LPBYTE) imageNameW, &dwSize); if(ok != ERROR_SUCCESS || dwType != REG_EXPAND_SZ) { sqStopService(TEXT("Failed to read the image name from registry")); TerminateThread(GetCurrentThread(), 0); } - imageName[dwSize] = 0; + imageNameW[(dwSize+1)/2] = 0; + WideCharToMultiByte(CP_UTF8, 0, imageNameW, -1, imageName, MAX_PATH_UTF8, NULL, NULL); /* Read the log file name from the subkey. */ dwSize = MAX_PATH; ok = RegQueryValueEx(hk,TEXT("Log"),NULL, &dwType, (LPBYTE) &tmpString, &dwSize); diff --git a/platforms/win32/vm/sqWin32Window.c b/platforms/win32/vm/sqWin32Window.c index fc5f9084e0..741fff4972 100644 --- a/platforms/win32/vm/sqWin32Window.c +++ b/platforms/win32/vm/sqWin32Window.c @@ -56,21 +56,30 @@ extern sqInt deferDisplayUpdates; /*** Variables -- image and path names ***/ -#define IMAGE_NAME_SIZE MAX_PATH +#define IMAGE_NAME_SIZE MAX_PATH_UTF8 -char imageName[MAX_PATH+1]; /* full path and name to image */ -TCHAR imagePath[MAX_PATH+1]; /* full path to image */ -TCHAR vmPath[MAX_PATH+1]; /* full path to interpreter's directory */ -TCHAR vmName[MAX_PATH+1]; /* name of the interpreter's executable */ -char windowTitle[MAX_PATH]; /* what should we display in the title? */ -TCHAR squeakIniName[MAX_PATH+1]; /* full path and name to ini file */ -TCHAR windowClassName[MAX_PATH+1]; /* Window class name */ +/* IMPLEMENTATION NOTE: +- both UTF8 and UTF16 versions are maintained in parallel +- UTF8 version is for interaction with image +- UTF16 version is for interaction with WIN32 API +whichever code modifies one version is responsible for updating the other +*/ +char imageName [MAX_PATH_UTF8 + 1]; /* full path and name to image */ +WCHAR imageNameW[MAX_PATH + 1]; /* full path and name to image */ +char imagePathA[MAX_PATH_UTF8 + 1]; /* full path to image */ +WCHAR imagePathW[MAX_PATH + 1]; /* full path to image */ +char vmPathA[MAX_PATH_UTF8 + 1]; /* full path to interpreter's directory */ +WCHAR vmPathW[MAX_PATH + 1]; /* full path to interpreter's directory */ +char vmNameA[MAX_PATH_UTF8 + 1]; /* name of the interpreter's executable UTF8 */ +WCHAR vmNameW[MAX_PATH + 1]; /* name of the interpreter's executable UTF16 */ +char windowTitle[MAX_PATH]; /* what should we display in the title? */ +TCHAR squeakIniName[MAX_PATH+1]; /* full path and name to ini file */ +TCHAR windowClassName[MAX_PATH+1]; /* Window class name */ const TCHAR U_ON[] = TEXT("1"); const TCHAR U_OFF[] = TEXT("0"); const TCHAR U_GLOBAL[] = TEXT("Global"); -const TCHAR U_SLASH[] = TEXT("/"); -const TCHAR U_BACKSLASH[] = TEXT("\\"); +const WCHAR W_BACKSLASH[] = L"\\"; /*** Variables -- Event Recording ***/ int inputSemaphoreIndex = 0;/* if non-zero the event semaphore index */ @@ -195,7 +204,7 @@ extern int sqAskSecurityYesNoQuestion(const char *question) extern const char *sqGetCurrentImagePath(void) { - return imagePath; + return imagePathA; } /****************************************************************************/ @@ -2753,7 +2762,7 @@ sqInt clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) { sqInt vmPathSize(void) { - return lstrlen(vmPath); + return strlen(vmPathA); } sqInt vmPathGetLength(sqInt sqVMPathIndex, sqInt length) @@ -2761,12 +2770,12 @@ sqInt vmPathGetLength(sqInt sqVMPathIndex, sqInt length) char *stVMPath= (char *)sqVMPathIndex; int count, i; - count= lstrlen(vmPath); + count= strlen(vmPathA); count= (length < count) ? length : count; /* copy the file name into the Squeak string */ for (i= 0; i < count; i++) - stVMPath[i]= (char) vmPath[i]; /* will remove leading zeros from unicode */ + stVMPath[i]= vmPathA[i]; return count; } @@ -2799,7 +2808,7 @@ sqInt imageNameGetLength(sqInt sqImageNameIndex, sqInt length) sqInt imageNamePutLength(sqInt sqImageNameIndex, sqInt length) { char *sqImageName= (char *)sqImageNameIndex; - char tmpImageName[MAX_PATH+1]; + char tmpImageName[IMAGE_NAME_SIZE +1]; char *tmp; int count, i; @@ -2831,6 +2840,7 @@ sqInt imageNamePutLength(sqInt sqImageNameIndex, sqInt length) strcat(imageName,tmpImageName); } } + MultiByteToWideChar(CP_UTF8, 0, imageName, -1, imageNameW, MAX_PATH); SetWindowTitle(); return 1; } @@ -2856,7 +2866,7 @@ char * GetAttributeString(sqInt id) { could be reported this way as well. */ /* id == 0 : return the full name of the VM */ - if(id == 0) return fromUnicode(vmName); + if(id == 0) return vmNameA; /* 0 < id <= 1000 : return options of the image (e.g. given *after* the image name) */ if(id > 0 && id <= 1000) return GetImageOption(id-1); @@ -3001,34 +3011,45 @@ int isLocalFileName(TCHAR *fileName) void SetupFilesAndPath(){ char *tmp; - lstrcpy(imagePath, imageName); - tmp = lstrrchr(imagePath,'\\'); + WCHAR *wtmp; + strcpy(imagePathA, imageNameA); + wcscpy(imagePathW, imageNameW); + tmp = strrchr(imagePathA,'\\'); if(tmp) tmp[1] = 0; + wtmp = wcsrchr(imagePathW, '\\'); + if (wtmp) wtmp[1] = 0; } #else /* defined(_WIN32_WCE) */ void SetupFilesAndPath() { char *tmp; - WCHAR tmpName[MAX_PATH]; - WCHAR imageNameW[MAX_PATH]; + WCHAR *wtmp; + WCHAR tmpName[MAX_PATH+1]; /* get the full path for the image */ - MultiByteToWideChar(CP_UTF8, 0, imageName, -1, tmpName, MAX_PATH); + MultiByteToWideChar(CP_UTF8, 0, imageName , -1, tmpName, MAX_PATH); GetFullPathNameW(tmpName, MAX_PATH, imageNameW, NULL); /* and copy back to a UTF-8 string */ - WideCharToMultiByte(CP_UTF8, 0, imageNameW,-1,imageName,MAX_PATH,NULL,NULL); + WideCharToMultiByte(CP_UTF8, 0, imageNameW,-1,imageName ,MAX_PATH_UTF8,NULL,NULL); /* get the VM directory */ - lstrcpy(vmPath, vmName); - tmp = lstrrchr(vmPath,U_BACKSLASH[0]); + strcpy(vmPathA, vmNameA); + wcscpy(vmPathW, vmNameW); + tmp = strrchr(vmPathA, '\\'); + wtmp = wcsrchr(vmPathW, W_BACKSLASH[0]); if(tmp) *tmp = 0; - lstrcat(vmPath,U_BACKSLASH); - - lstrcpy(imagePath, imageName); - tmp = lstrrchr(imagePath,U_BACKSLASH[0]); + if (wtmp) *wtmp = 0; + strcat(vmPathA,"\\"); + wcscat(vmPathW, W_BACKSLASH); + + strcpy(imagePathA, imageName ); + wcscpy(imagePathW, imageNameW); + tmp = strrchr(imagePathA,'\\'); + wtmp = wcsrchr(imagePathW, W_BACKSLASH[0]); if(tmp) tmp[1] = 0; + if (wtmp) wtmp[1] = 0; } #endif /* !defined(_WIN32_WCE) */ @@ -3056,20 +3077,13 @@ DWORD SqueakImageLengthFromHandle(HANDLE hFile) { return 0; } -DWORD SqueakImageLength(TCHAR *fileName) { +DWORD SqueakImageLength(WCHAR *fileName) { DWORD dwSize; HANDLE hFile; /* open image file */ -#ifdef UNICODE hFile = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#else - WCHAR wideName[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, fileName, -1, wideName, MAX_PATH); - hFile = CreateFileW(wideName, GENERIC_READ, FILE_SHARE_READ, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); -#endif if (hFile == INVALID_HANDLE_VALUE) return 0; dwSize = SqueakImageLengthFromHandle(hFile); CloseHandle(hFile); @@ -3094,8 +3108,9 @@ int findImageFile(void) { nextFound = FindNextFileW(findHandle,&findData); FindClose(findHandle); if(nextFound) return 0; /* more than one entry */ - WideCharToMultiByte(CP_UTF8, 0, findData.cFileName, -1, - imageName, MAX_PATH, NULL, NULL); + wcsncpy(imageNameW,findData.cFileName,MAX_PATH); + WideCharToMultiByte(CP_UTF8, 0, imageNameW, -1, + imageName, MAX_PATH_UTF8, NULL, NULL); return 1; } @@ -3121,7 +3136,8 @@ int openImageFile(void) { ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; ofn.lpstrDefExt = L"image"; if (!GetOpenFileNameW(&ofn)) return 0; - WideCharToMultiByte(CP_UTF8, 0, path, -1, + wcsncpy(imageNameW,path, MAX_PATH); + WideCharToMultiByte(CP_UTF8, 0, imageNameW, -1, imageName, MAX_PATH, NULL, NULL); return 1; } @@ -3317,7 +3333,7 @@ int printUsage(int level) TEXT("There are several ways to open an image file. You can:\n") TEXT(" 1. Double-click on the desired image file.\n") TEXT(" 2. Drop the image file onto the application.\n") - TEXT("Aborting...\n"), toUnicode(imageName)); + TEXT("Aborting...\n"), imageNameT); } return -1; } From 8bdd0b6afd5997ec3e29d10ce7602b21f6739e54 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 17:14:06 +0100 Subject: [PATCH 02/36] DropPlugin: modernize OpenFile/_lwrite/_lclose API OK, OK, they are compatible with 16 bits windows ;) But these are not recommended any more especially if we want to go toward UNICODE paths everywhere. --- .../win32/plugins/DropPlugin/sqWin32Drop.c | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/platforms/win32/plugins/DropPlugin/sqWin32Drop.c b/platforms/win32/plugins/DropPlugin/sqWin32Drop.c index 9606691aa7..12f09fbe7d 100644 --- a/platforms/win32/plugins/DropPlugin/sqWin32Drop.c +++ b/platforms/win32/plugins/DropPlugin/sqWin32Drop.c @@ -212,19 +212,26 @@ HANDLE DibFromBitmap ( } BOOL WriteDIB ( - LPSTR szFile, + LPCSTR szFile, HANDLE hdib) { BITMAPFILEHEADER hdr; LPBITMAPINFOHEADER lpbi; - HFILE fh; - OFSTRUCT of; + HANDLE fh; + DWORD nbwritten; if (!hdib) return FALSE; - fh = OpenFile(szFile, &of, (UINT)OF_CREATE|OF_READWRITE); - if (fh == -1) + fh = CreateFileA(szFile, + (GENERIC_READ | GENERIC_WRITE) , + FILE_SHARE_READ , + NULL, /* No security descriptor */ + OPEN_ALWAYS , + FILE_ATTRIBUTE_NORMAL, + NULL /* No template */); + + if (fh == INVALID_HANDLE_VALUE) return FALSE; lpbi = (VOID FAR *)GlobalLock (hdib); @@ -240,16 +247,16 @@ BOOL WriteDIB ( /* Write the file header */ /* write bfType*/ - _lwrite(fh, (LPSTR)&hdr.bfType, (UINT)sizeof (WORD)); + WriteFile(fh, (LPSTR)&hdr.bfType, (UINT)sizeof (WORD),&nbwritten,NULL); /* now pass over extra word, and only write next 3 DWORDS!*/ - _lwrite(fh, (LPSTR)&hdr.bfSize, sizeof(DWORD) * 3); + WriteFile(fh, (LPSTR)&hdr.bfSize, sizeof(DWORD) * 3, &nbwritten, NULL); /* this struct already DWORD aligned!*/ /* Write the DIB header and the bits */ - _lwrite (fh, (LPSTR)lpbi, GlobalSize (hdib)); + WriteFile(fh, (LPSTR)lpbi, GlobalSize (hdib), &nbwritten, NULL); GlobalUnlock (hdib); - _lclose(fh); + CloseHandle(fh); return TRUE; } From cb32e410a6ae4102a96d7110a562a25140d1be30 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 18:06:07 +0100 Subject: [PATCH 03/36] Drop plugin: let tempPathName be Wide This is for the generated temporary file $$squeak$$.bmp The path to temporary directory could be non ASCII, so let's be more robust to UNICODE. --- .../win32/plugins/DropPlugin/sqWin32Drop.c | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/platforms/win32/plugins/DropPlugin/sqWin32Drop.c b/platforms/win32/plugins/DropPlugin/sqWin32Drop.c index 12f09fbe7d..9962f7a15d 100644 --- a/platforms/win32/plugins/DropPlugin/sqWin32Drop.c +++ b/platforms/win32/plugins/DropPlugin/sqWin32Drop.c @@ -212,7 +212,7 @@ HANDLE DibFromBitmap ( } BOOL WriteDIB ( - LPCSTR szFile, + LPCWSTR szFile, HANDLE hdib) { BITMAPFILEHEADER hdr; @@ -223,7 +223,7 @@ BOOL WriteDIB ( if (!hdib) return FALSE; - fh = CreateFileA(szFile, + fh = CreateFileW(szFile, (GENERIC_READ | GENERIC_WRITE) , FILE_SHARE_READ , NULL, /* No security descriptor */ @@ -516,7 +516,7 @@ STDMETHODIMP DropTarget_Drop(DropTarget *dt, DWORD i; DPRINTF(("Success\n")); - numDropFiles = DragQueryFile(hDrop, -1, NULL, 0); + numDropFiles = DragQueryFileW(hDrop, -1, NULL, 0); dropFiles = calloc(numDropFiles, sizeof(char*)); for(i=0; ilpVtbl->GetData(ido, &fmtetc, &medium); if(hRes == S_OK) { - TCHAR tmpName[MAX_PATH+1]; + WCHAR tmpNameW[MAX_PATH+1]; HANDLE hDib = medium.hGlobal; DPRINTF(("Success\n")); - GetTempPath(MAX_PATH,tmpName); - strcat(tmpName,"$$squeak$$.bmp"); - if(WriteDIB(tmpName, hDib)) { - numDropFiles = 1; - dropFiles = calloc(1, sizeof(void*)); - dropFiles[0] = _strdup(tmpName); + GetTempPathW(MAX_PATH,tmpNameW); + wcscat(tmpNameW,L"$$squeak$$.bmp"); + if(WriteDIB(tmpNameW, hDib)) { + char tmpNameA[MAX_PATH_UTF8 + 1]; + if(WideCharToMultiByte(CP_UTF8, 0, tmpNameW, -1, tmpNameA, MAX_PATH_UTF8, NULL, NULL)) { + numDropFiles = 1; + dropFiles = calloc(1, sizeof(void*)); + dropFiles[0] = _strdup(tmpNameA); + } } if(medium.pUnkForRelease == NULL) { GlobalFree(hDib); @@ -587,20 +590,23 @@ STDMETHODIMP DropTarget_Drop(DropTarget *dt, hRes = ido->lpVtbl->GetData(ido, &fmtetc, &medium); if(hRes == S_OK) { - TCHAR tmpName[MAX_PATH+1]; + WCHAR tmpNameW[MAX_PATH+1]; HANDLE hDib; HBITMAP hBM = medium.hBitmap; DPRINTF(("Success\n")); - GetTempPath(MAX_PATH,tmpName); - strcat(tmpName,"$$squeak$$.bmp"); + GetTempPathW(MAX_PATH,tmpNameW); + wcscat(tmpNameW,L"$$squeak$$.bmp"); hDib = DibFromBitmap(hBM, BI_RGB, 0, NULL); if(hDib) { - if(WriteDIB(tmpName, hDib)) { - numDropFiles = 1; - dropFiles = calloc(1, sizeof(void*)); - dropFiles[0] = _strdup(tmpName); + if(WriteDIB(tmpNameW, hDib)) { + char tmpNameA[MAX_PATH_UTF8 + 1]; + if (WideCharToMultiByte(CP_UTF8, 0, tmpNameW, -1, tmpNameA, MAX_PATH_UTF8, NULL, NULL)) { + numDropFiles = 1; + dropFiles = calloc(1, sizeof(void*)); + dropFiles[0] = _strdup(tmpNameA); + } } DeleteObject(hDib); } @@ -625,7 +631,7 @@ STDMETHODIMP DropTarget_Drop(DropTarget *dt, hRes = ido->lpVtbl->GetData(ido, &fmtetc, &medium); if(hRes == S_OK) { - TCHAR tmpName[MAX_PATH+1]; + WCHAR tmpNameW[MAX_PATH+1]; HANDLE hMF = medium.hGlobal; HANDLE hDib; BITMAPINFO bmi; @@ -668,12 +674,15 @@ STDMETHODIMP DropTarget_Drop(DropTarget *dt, DeleteObject(hBM); } - GetTempPath(MAX_PATH,tmpName); - strcat(tmpName,"$$squeak$$.bmp"); - if(WriteDIB(tmpName, hDib)) { - numDropFiles = 1; - dropFiles = calloc(1, sizeof(void*)); - dropFiles[0] = _strdup(tmpName); + GetTempPathW(MAX_PATH,tmpNameW); + wcscat(tmpNameW,L"$$squeak$$.bmp"); + if(WriteDIB(tmpNameW, hDib)) { + char tmpNameA[MAX_PATH_UTF8 + 1]; + if (WideCharToMultiByte(CP_UTF8, 0, tmpNameW, -1, tmpNameA, MAX_PATH_UTF8, NULL, NULL)) { + numDropFiles = 1; + dropFiles = calloc(1, sizeof(void*)); + dropFiles[0] = _strdup(tmpNameA); + } } GlobalFree(hDib); if(medium.pUnkForRelease == NULL) { From 85e08830d9ee73100ebf6195a1da7a30ca811428 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 18:22:44 +0100 Subject: [PATCH 04/36] Fix 2 potential buffer overrun in sqWin32Service.c --- platforms/win32/vm/sqWin32Service.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index 5ba48464bc..1372f215cd 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -338,7 +338,7 @@ sqInstallService95(LPTSTR serviceName) /* sqStartService95: start the named service on a Windows 95 system */ int sqStartService95(LPTSTR serviceName) -{ TCHAR tmpString[1024]; +{ TCHAR tmpString[1025]; STARTUPINFO sInfo; PROCESS_INFORMATION pInfo; DWORD dwSize, dwType; @@ -499,7 +499,7 @@ int sqServiceMain(void) DWORD WINAPI sqThreadMain(DWORD ignored) { DWORD dwSize, dwType, ok; HKEY hk; - static TCHAR tmpString[256]; + static TCHAR tmpString[MAX_PATH+1]; static TCHAR lbuf[50]; char *cmd; From f6891456a8740e1b6ef2bcac6dc1527c2a31b7c6 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 00:38:50 +0100 Subject: [PATCH 05/36] Setting fp flags _MCW_PC and _MCW_IC is not supported on ARM nor X64 See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/control87-controlfp-control87-2?view=vs-2017 Setting these flags triggers an assertion failure when compiled with MSVC in debug mode Assertion failure (mask&~(_MCW_DN|_MCW_EM|_MCW_RC))==0 --- platforms/win32/vm/sqWin32Main.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 31266e1211..ce3a2a7b54 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -159,8 +159,17 @@ void SetSystemTrayIcon(BOOL on); _RC_NEAR: round to nearest _PC_53 : double precision arithmetic (instead of extended) _EM_XXX: silent operations (no signals please) + NOTE: precision control and infinity control are not defined on ARM or X64 (SSE oblige), only X86 */ +# if defined(_M_IX86) || defined(X86) || defined(_M_I386) || defined(_X86_) \ + || defined(i386) || defined(i486) || defined(i586) || defined(i686) \ + || defined(__i386__) || defined(__386__) || defined(I386) #define FPU_DEFAULT (_RC_NEAR + _PC_53 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL) +#define FPU_MASK (_MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC) +#else +#define FPU_DEFAULT (_RC_NEAR + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL) +#define FPU_MASK (_MCW_EM | _MCW_RC) +#endif /****************************************************************************/ /* Exception handling */ @@ -191,7 +200,7 @@ extern sqInt primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc); DWORD code = exp->ExceptionRecord->ExceptionCode; if ((code >= EXCEPTION_FLT_DENORMAL_OPERAND) && (code <= EXCEPTION_FLT_UNDERFLOW)) { /* turn on the default masking of exceptions in the FPU and proceed */ - _controlfp(FPU_DEFAULT, _MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC); + _controlfp(FPU_DEFAULT, FPU_MASK); result = EXCEPTION_CONTINUE_EXECUTION; } } @@ -1491,7 +1500,7 @@ sqMain(int argc, char *argv[]) int virtualMemory; /* set default fpu control word */ - _controlfp(FPU_DEFAULT, _MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC); + _controlfp(FPU_DEFAULT, FPU_MASK); LoadPreferences(); From 49eff31f03725185549bb736ad970818b37da612 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 02:03:59 +0100 Subject: [PATCH 06/36] The pageSize and pageMask are too short on WIN64 Due to this, roundUpToPage is truncating addresses > 2^32 With MSVC, minAddress is > 2^32, and it then takes ages to generate a VirtualAlloc > minAddress (several minutes). --- platforms/win32/vm/sqWin32SpurAlloc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/win32/vm/sqWin32SpurAlloc.c b/platforms/win32/vm/sqWin32SpurAlloc.c index 9b65a39bb8..cc725d1c79 100644 --- a/platforms/win32/vm/sqWin32SpurAlloc.c +++ b/platforms/win32/vm/sqWin32SpurAlloc.c @@ -29,8 +29,8 @@ LONG CALLBACK sqExceptionFilter(LPEXCEPTION_POINTERS exp) return EXCEPTION_WRONG_ACCESS; } -static DWORD pageMask; /* bit mask for the start of a memory page */ -static DWORD pageSize; /* size of a memory page */ +static sqIntptr_t pageMask; /* bit mask for the start of a memory page */ +static sqIntptr_t pageSize; /* size of a memory page */ static char *minAppAddr; /* SYSTEM_INFO lpMinimumApplicationAddress */ static char *maxAppAddr; /* SYSTEM_INFO lpMaximumApplicationAddress */ From 627bc5e39c3e718de09ddc0293997435b8c73413 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 20:01:19 +0100 Subject: [PATCH 07/36] Use the eventually true UNICODE imageNameT if -DUNICODE using `toUnicode` does not do the right thing: it promotes each UTF8 byte code to short... ... which can hardly work beyond ASCII. --- platforms/win32/vm/sqWin32Service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index 1372f215cd..ac119077e6 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -101,7 +101,7 @@ TCHAR *printCommandLine(int printFor95) } /* add image name */ lstrcat(buffer, TEXT("\"")); - lstrcat(buffer, toUnicode(imageName)); + lstrcat(buffer, imageNameT); lstrcat(buffer, TEXT("\"\0")); return buffer; From 98fc85ddd56487492a4988ad457dbc2b3fe88397 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 21:55:23 +0100 Subject: [PATCH 08/36] interpret pluginName as UTF8 rather than pure ASCII toUnicode is just expanding bytes to short, and re-interpret them UTF16, not regarding original encoding. This works well for pure ASCII, but is not really defined for other encodings. We should prefer using UTF8 by default for all the image-VM string transfer. --- platforms/win32/vm/sqWin32ExternalPrims.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32ExternalPrims.c b/platforms/win32/vm/sqWin32ExternalPrims.c index 3c05397ba5..86999c3711 100644 --- a/platforms/win32/vm/sqWin32ExternalPrims.c +++ b/platforms/win32/vm/sqWin32ExternalPrims.c @@ -41,7 +41,10 @@ void *ioLoadModule(char *pluginName) TCHAR *name; #ifdef UNICODE - name = toUnicode(pluginName); + int len = MultiByteToWideChar(CP_UTF8, 0, pluginName, -1, NULL, 0); + if (len <= 0) return 0; /* invalid UTF8 ? */ + name = alloca(len); + if (MultiByteToWideChar(CP_UTF8, 0, pluginName, -1, name, len) == 0) return 0; #else name = pluginName; #endif From 4f6f191613f6bf6cdb989b39e1002cf4938f475c Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 22:31:40 +0100 Subject: [PATCH 09/36] refactor sqMessageBox to void using toUnicode Why to get rid of toUnicode fromUnicode fromSqueak fromSqueak2? - we'd rather use UTF8 everywhere. - and it's the last usage remaining! Now the clients must use a TEXT() macro also for the format. In the future, we could eventually translate the VM messages... Get rid of wsprintf variant which has no character limit and might be prone to buffer overrun. An alternative for supporting both ASCII and WIDE is the _tcs* family in See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/vsprintf-vsprintf-l-vswprintf-vswprintf-l-vswprintf-l?view=vs-2017 In order to avoid buffer overrun, prefer a vsnprintf variant Note: I did also update the prototype in sqWin32SpurAlloc, but not the contents. It is dead code, and only there for testing purposes. We'd better remove it! --- .../win32/plugins/FilePlugin/sqWin32FilePrims.c | 2 +- platforms/win32/vm/sqWin32.h | 2 +- platforms/win32/vm/sqWin32Alloc.c | 4 ++-- platforms/win32/vm/sqWin32Main.c | 4 ++-- platforms/win32/vm/sqWin32SpurAlloc.c | 14 +++++++------- platforms/win32/vm/sqWin32Utils.c | 16 +++++++--------- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c b/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c index d32f8a9f24..63965b61d0 100644 --- a/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c +++ b/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c @@ -517,7 +517,7 @@ size_t sqImageFileRead(void *ptr, size_t sz, size_t count, sqImageFile h) ReadFile((HANDLE)(h-1), (LPVOID) ptr, count*sz, &dwReallyRead, NULL); while(dwReallyRead != (DWORD)(count*sz)) { DWORD err = GetLastError(); - if(sqMessageBox(MB_ABORTRETRYIGNORE, TEXT("Squeak Warning"),"Image file read problem (%d out of %d bytes read)", dwReallyRead, count*sz) + if(sqMessageBox(MB_ABORTRETRYIGNORE, TEXT("Squeak Warning"),TEXT("Image file read problem (%d out of %d bytes read)"), dwReallyRead, count*sz) == IDABORT) return (dwReallyRead / sz); sqImageFileSeek(h, position); ReadFile((HANDLE)(h-1), (LPVOID) ptr, count*sz, &dwReallyRead, NULL); diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index 3fbec49ebe..7c899e5ce3 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -382,7 +382,7 @@ TCHAR *lstrrchr(TCHAR *source, TCHAR c); /* Output stuff */ /******************************************************/ #ifndef sqMessageBox -int __cdecl sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...); +int __cdecl sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const TCHAR* fmt, ...); #endif #ifndef warnPrintf diff --git a/platforms/win32/vm/sqWin32Alloc.c b/platforms/win32/vm/sqWin32Alloc.c index a3b51dd364..e5f857fdf0 100644 --- a/platforms/win32/vm/sqWin32Alloc.c +++ b/platforms/win32/vm/sqWin32Alloc.c @@ -76,7 +76,7 @@ void *sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) } while(!pageBase); if(!pageBase) { sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "Unable to allocate memory (%d bytes requested)", + TEXT("Unable to allocate memory (%d bytes requested)"), maxReserved); return pageBase; } @@ -84,7 +84,7 @@ void *sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) commit = nowReserved; if(!VirtualAlloc(pageBase, commit, MEM_COMMIT, PAGE_READWRITE)) { sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "Unable to commit memory (%d bytes requested)", + TEXT("Unable to commit memory (%d bytes requested)"), commit); return NULL; } diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index ce3a2a7b54..850f145b65 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -1424,7 +1424,7 @@ sqImageFile findEmbeddedImage(void) { sqImageFileSeek(f, endMarker); sqImageFileRead(&magic, 1, 4, f); sqImageFileRead(&start, 1, 4, f); - sqMessageBox(MB_OK, "Magic number", "Expected:\t%x\nFound:\t\t%x", SQ_IMAGE_MAGIC, magic); + sqMessageBox(MB_OK, TEXT("Magic number", "Expected:\t%x\nFound:\t\t%x"), SQ_IMAGE_MAGIC, magic); /* Magic number must be okay and start must be within executable boundaries */ if(magic != SQ_IMAGE_MAGIC || start < 0 || start >= endMarker) { /* nope */ @@ -1434,7 +1434,7 @@ sqImageFile findEmbeddedImage(void) { /* Might have an embedded image; seek back and double check */ sqImageFileSeek(f,start); sqImageFileRead(&magic, 1, 4, f); - sqMessageBox(MB_OK, "Magic number", "Expected:\t%x\nFound:\t\t%x", SQ_IMAGE_MAGIC, magic); + sqMessageBox(MB_OK, TEXT("Magic number", "Expected:\t%x\nFound:\t\t%x"), SQ_IMAGE_MAGIC, magic); if(magic != SQ_IMAGE_MAGIC) { /* nope */ sqImageFileClose(f); diff --git a/platforms/win32/vm/sqWin32SpurAlloc.c b/platforms/win32/vm/sqWin32SpurAlloc.c index cc725d1c79..d20a57800a 100644 --- a/platforms/win32/vm/sqWin32SpurAlloc.c +++ b/platforms/win32/vm/sqWin32SpurAlloc.c @@ -50,7 +50,7 @@ sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) if (pageSize) { sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "sqAllocateMemory already called"); + TEXT("sqAllocateMemory already called")); exit(1); } @@ -77,7 +77,7 @@ sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) if (!alloc) { exit(errno); sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "sqAllocateMemory: initial alloc failed!\n"); + TEXT("sqAllocateMemory: initial alloc failed!\n")); exit(1); } return alloc; @@ -114,7 +114,7 @@ address_space_used(char *address, usqInt bytes) return 1; if (!VirtualQuery(address, &info, sizeof(info))) sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "Unable to VirtualQuery range [%p, %p), Error: %u", + TEXT("Unable to VirtualQuery range [%p, %p), Error: %u"), address, (char *)address + bytes, GetLastError()); addressSpaceUnused = info.BaseAddress == address @@ -160,7 +160,7 @@ sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress DWORD lastError = GetLastError(); #if 0 /* Can't report this without making the system unusable... */ sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), - "Unable to VirtualAlloc committed memory at desired address (%" PRIuSQINT " bytes requested at %p, above %p), Error: %lu", + TEXT("Unable to VirtualAlloc committed memory at desired address (%") TEXT(PRIuSQINT) TEXT(" bytes requested at %p, above %p), Error: %lu"), bytes, address, minAddress, lastError); #else if (fIsConsole) @@ -175,7 +175,7 @@ sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress */ if (alloc && !VirtualFree(alloc, SizeForRelease(bytes), MEM_RELEASE)) sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Warning:"), - "Unable to VirtualFree committed memory (%" PRIuSQINT " bytes requested), Error: %ul", + TEXT("Unable to VirtualFree committed memory (%") TEXT(PRIuSQINT) TEXT(" bytes requested), Error: %ul"), bytes, GetLastError()); address += delta; } @@ -190,7 +190,7 @@ sqDeallocateMemorySegmentAtOfSize(void *addr, sqInt sz) { if (!VirtualFree(addr, SizeForRelease(sz), MEM_RELEASE)) sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Warning:"), - "Unable to VirtualFree committed memory (%" PRIuSQINT " bytes requested), Error: %ul", + TEXT("Unable to VirtualFree committed memory (%") TEXT(PRIuSQINT) TEXT(" bytes requested), Error: %ul"), sz, GetLastError()); } @@ -257,7 +257,7 @@ main() return 0; } int __cdecl -sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...) +sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const TCHAR* fmt, ...) { va_list args; int result; diff --git a/platforms/win32/vm/sqWin32Utils.c b/platforms/win32/vm/sqWin32Utils.c index f8554007c1..34502881a9 100644 --- a/platforms/win32/vm/sqWin32Utils.c +++ b/platforms/win32/vm/sqWin32Utils.c @@ -117,19 +117,17 @@ TCHAR *lstrrchr(TCHAR *source, TCHAR c) /* printf() format string and arguments */ /****************************************************************************/ #ifndef sqMessageBox -int __cdecl sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...) -{ TCHAR *ptr, *buf; - va_list args; +int __cdecl sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const TCHAR* fmt, ...) +{ TCHAR *buf; + va_list args; DWORD result; - ptr = toUnicodeNew(fmt); buf = (TCHAR*) calloc(sizeof(TCHAR), 4096); - va_start(args, fmt); - wvsprintf(buf, ptr, args); - va_end(args); + va_start(args, fmt); + _vsntprintf(buf, 4096, fmt, args); + va_end(args); - result = MessageBox(stWindow,buf,titleString,dwFlags|MB_SETFOREGROUND); - free(ptr); + result = MessageBox(stWindow,buf,titleString,dwFlags|MB_SETFOREGROUND); free(buf); return result; } From 545ec0a931166a8374683940f3344e27a110f42c Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 22:56:17 +0100 Subject: [PATCH 10/36] Discard now unused toUnicode fromUnicode fromSqueak fromSqueak2 lstrrchr For UNICODE compatibility, - every String coming from image to the VM should better be interpreted UTF8, and converted to wide String via MultiByteToWideChar() - every String going to the image from the VM should better be converted from Wide string to UTF8 via WideCharToMultiByte() See: https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-multibytetowidechar https://docs.microsoft.com/en-us/windows/desktop/api/stringapiset/nf-stringapiset-widechartomultibyte Side note: there is also a _tcsrrchr in at least since visual studio 2015 See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strrchr-wcsrchr-mbsrchr-mbsrchr-l?view=vs-2017 is also supported by mingw so if ever we need lstrrchr again, we'll use that. --- platforms/win32/vm/sqWin32.h | 21 ------- platforms/win32/vm/sqWin32Utils.c | 98 ------------------------------- 2 files changed, 119 deletions(-) diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index 7c899e5ce3..d75afe3a27 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -357,27 +357,6 @@ extern BOOL f3ButtonMouse; /* Should we use a real 3 button mouse mapping? */ extern BOOL fBufferMouse; /* Should we buffer mouse input? */ -/******************************************************/ -/* String conversions between Unicode / Ansi / Squeak */ -/******************************************************/ -/* Note: fromSqueak() and fromSqueak2() are inline conversions - but operate on two different buffers. The maximum length - of strings that can be converted is MAX_PATH */ -TCHAR* fromSqueak(const char *sqPtr, int sqLen); /* Inline Squeak -> C */ -TCHAR* fromSqueak2(const char *sqPtr, int sqLen); /* 2nd inline conversion */ -/* Note: toUnicode() and fromUnicode() are inline conversions - with for at most MAX_PATH sized strings. If the VM - is not compiled with UNICODE defined they just return - the input strings. Also, toUnicode operates on the - same buffer as fromSqueak() */ -TCHAR* toUnicode(const char *ptr); /* Inline Ansi -> Unicode */ -char* fromUnicode(const TCHAR *ptr); /* Inline Unicode -> Ansi */ -/* Note: toUnicodeNew and fromUnicodeNew malloc() new strings. - It is up to the caller to free these! */ -TCHAR* toUnicodeNew(const char *ptr); /* Inline Ansi -> Unicode */ -char* fromUnicodeNew(const TCHAR *ptr); /* Inline Unicode -> Ansi */ -TCHAR *lstrrchr(TCHAR *source, TCHAR c); - /******************************************************/ /* Output stuff */ /******************************************************/ diff --git a/platforms/win32/vm/sqWin32Utils.c b/platforms/win32/vm/sqWin32Utils.c index 34502881a9..3a7ce61f12 100644 --- a/platforms/win32/vm/sqWin32Utils.c +++ b/platforms/win32/vm/sqWin32Utils.c @@ -13,104 +13,6 @@ #include #include "sq.h" -/***************************************************************************** - String conversions: Unicode / Ansi / Squeak - NOTES: - 1) The length of strings in inline functions MUST NOT exceed MAX_PATH. - 2) fromSqueak() and fromSqueak2() are inline conversions - but operate on two different buffers. - 3) toUnicode() and fromUnicode() are inline conversions - with for at most MAX_PATH sized strings. If the VM - is not compiled with UNICODE defined they just return - the input strings. Also, toUnicode operates on the - same buffer as fromSqueak() - 4) toUnicodeNew and fromUnicodeNew malloc() new strings. - It is up to the caller to free these! -*****************************************************************************/ - - -#define MAX_BUFFER MAX_PATH -static TCHAR w_buffer1[MAX_BUFFER]; /* wide buffer 1 */ -static TCHAR w_buffer2[MAX_BUFFER]; /* wide buffer 2 */ -static char a_buffer1[MAX_BUFFER]; /* ansi buffer 1 */ - -static TCHAR *fromSqueakInto(const char *sqPtr, int sqSize, TCHAR* buffer) -{ - int i; - if(sqSize >= MAX_BUFFER) sqSize = MAX_BUFFER-1; - for(i=0;i= MAX_BUFFER) size = MAX_BUFFER - 1; - for(i=0;i= source) - if(*tmp == c) return tmp; - else tmp--; - return NULL; -} - /****************************************************************************/ /* Helper to pop up a message box with a message formatted from the */ From 2024d4358c14482f4ad861180e412488e25aef7a Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 23:33:45 +0100 Subject: [PATCH 11/36] #if 0 ? YAGNI ! we now use builtin _WIN32 _WIN64 anyway Reminder: even in WIN64, _WIN32 is defined, so the comment was a bit misleading anyway. --- platforms/win32/vm/sqWin32.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index d75afe3a27..39e92f519b 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -143,16 +143,6 @@ #endif /* (_WIN32_WCE) */ -/* THE FOLLOWING IS WRONG (& HENCE I'VE IF 0'ed IT OUT. WE CAN'T MERELY DEFINE - * WIN32 BECAUSE WE MAY BE ON WIN64. eem 2017/05/16 - */ -#if 0 -/* due to weird include orders, make sure WIN32 is defined */ -# if !defined(WIN32) -# define WIN32 1 -# endif -#endif - /* Experimental */ #ifdef MINIMAL /* The hardcoded defs: From e1e83f770aaa7f903509eae7166c858260bd3b7c Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 23:34:50 +0100 Subject: [PATCH 12/36] which SetUpPreferences()? There is no SetUpPreferences()! --- platforms/win32/vm/sqWin32.h | 1 - 1 file changed, 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index 39e92f519b..42a17e6131 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -179,7 +179,6 @@ void SetupKeymap(); void SetupWindows(); void SetupPixmaps(); void SetupPrinter(); -void SetupPreferences(); void SetupMIDI(); /********************************************************/ From 8638522434f52dbacfdf634745ac50e387900ec6 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Sun, 30 Dec 2018 23:44:51 +0100 Subject: [PATCH 13/36] RegisterWindowMessage takes a TCHAR * --- platforms/win32/vm/sqWin32Main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 850f145b65..dba6fbd2b2 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -1790,7 +1790,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) #endif - SQ_LAUNCH_DROP = RegisterWindowMessage("SQUEAK_LAUNCH_DROP"); + SQ_LAUNCH_DROP = RegisterWindowMessage(TEXT("SQUEAK_LAUNCH_DROP")); /* Special startup stuff for windows 95 */ if(fWindows95) { From 1893512441e9838d3393896c1fe1abc507775c55 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 00:23:16 +0100 Subject: [PATCH 14/36] Platform specific knowledge: WIN32_FILE and STD_FILE are mutually exclusive options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Therefore, when we define `WIN32_FILE_SUPPORT` we must also define `NO_STD_FILE_SUPPORT` Until now, this knowledge was in build.win*/common/Makefile.* (and a legacy MSVC project in plaftorms/win32/misc/Squeak.dsp - on pourrait réduire la voilure !!!) Since it is easy to forget the second define, lets extend the knowledge in the sqPlatformSpecific.h --- platforms/win32/vm/sqPlatformSpecific.h | 1 + 1 file changed, 1 insertion(+) diff --git a/platforms/win32/vm/sqPlatformSpecific.h b/platforms/win32/vm/sqPlatformSpecific.h index ed4ab94f65..30f3f37c72 100644 --- a/platforms/win32/vm/sqPlatformSpecific.h +++ b/platforms/win32/vm/sqPlatformSpecific.h @@ -30,6 +30,7 @@ #ifdef WIN32_FILE_SUPPORT +#define NO_STD_FILE_SUPPORT /* STD_FILE or WIN32_FILE are mutually exclusive options */ #undef sqImageFile #undef sqImageFileClose #undef sqImageFileOpen From f25041584cb703d00ef9677b4403c50244daa024 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 00:53:45 +0100 Subject: [PATCH 15/36] We can't compare a TCHAR*windowClassName with a char*buf Even if buf were re-interpreted as WCHAR* when -DUNICODE, strcmp wouldn't do the right thing (it will stop at first ASCII because high 8 bits will be zero!) We thus use the TCHAR*dedicated `_tcscmp`. If ever we want to switch to UTF16 (WCHAR*) windowClassName, then it will be `wcscmp`. --- platforms/win32/vm/sqWin32Main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index dba6fbd2b2..f6fb749beb 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -1508,9 +1508,9 @@ sqMain(int argc, char *argv[]) if(fRunSingleApp) { HWND win = GetTopWindow(0); while (win != NULL) { - char buf[MAX_PATH]; - GetClassName(win, buf, 80); - if(strcmp(windowClassName, buf) == 0) break; + TCHAR buf[MAX_PATH]; + GetClassName(win, buf, MAX_PATH); + if(_tcscmp(windowClassName, buf) == 0) break; win = GetNextWindow(win, GW_HWNDNEXT); } From 84a8d172d1c524487cf9c2e06f2fc807ef27c6d9 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 01:01:54 +0100 Subject: [PATCH 16/36] Tu quoque NewspeakVM, frustra TEXT macro... That's the limit of using compiler warnings: we only focus on the sections we compile... BTW, ifdef NewspeakVM, OK, but what do Pharo people think about it? --- platforms/win32/vm/sqWin32Prefs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32Prefs.c b/platforms/win32/vm/sqWin32Prefs.c index dd2ceb917c..43629ad5e1 100644 --- a/platforms/win32/vm/sqWin32Prefs.c +++ b/platforms/win32/vm/sqWin32Prefs.c @@ -227,7 +227,7 @@ void LoadPreferences() /* get the window class name from the ini file */ GetPrivateProfileString(U_GLOBAL, TEXT("WindowClassName"), #if NewspeakVM - TEXT(VM_NAME"WindowClass"), + TEXT(VM_NAME) TEXT("WindowClass"), #else TEXT("SqueakWindowClass"), #endif From 7d3264e523709ea92aa1a70d1c9a97a863d3c0a8 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 01:23:40 +0100 Subject: [PATCH 17/36] gai_strerror returns a TCHAR*, we cannot simply fprintf it... Choose the UNICODE variant, because error messages are presumably localized an may use non ASCII characters The alternative would be to use `_ftprintf(stderr,TEXT("%s"),gai_strerror(gaiError))` and let -DUNICODE decide... --- platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c b/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c index 5fd1b3504d..4c32f0df1b 100644 --- a/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c +++ b/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c @@ -2179,7 +2179,7 @@ void sqResolverGetAddressInfoHostSizeServiceSizeFlagsFamilyTypeProtocol(char *ho if (gaiError) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gaiError)); + fwprintf(stderr, L"getaddrinfo: %s\n", gai_strerrorW(gaiError)); addrList= 0; /* succeed with zero results for impossible constraints */ } @@ -2349,7 +2349,7 @@ void sqResolverGetNameInfoSizeFlags(char *addr, sqInt addrSize, sqInt flags) if (gaiError) { - fprintf(stderr, "getnameinfo: %s\n", gai_strerror(gaiError)); + fwprintf(stderr, L"getnameinfo: %s\n", gai_strerrorW(gaiError)); lastError= gaiError; goto fail; } From 6ff9625b6cb47487bf5421921c9b7b9742571e7b Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 01:37:37 +0100 Subject: [PATCH 18/36] Let lookup account for NULL terminating a WCHAR* The low level `RegQueryValueEx` deals with a char*, but here char* just means some un-interpreted bytes, not a string! If we compile with `-DUNICODE` the un-interpreted bytes will contain a WCHAR* And if we want to properly NULL terminate this WCHAR*, then we need 2 null bytes, not 1! --- platforms/win32/vm/sqWin32DnsInfo.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platforms/win32/vm/sqWin32DnsInfo.c b/platforms/win32/vm/sqWin32DnsInfo.c index bd4e6d20c2..de25f44062 100644 --- a/platforms/win32/vm/sqWin32DnsInfo.c +++ b/platforms/win32/vm/sqWin32DnsInfo.c @@ -12,11 +12,11 @@ static char *buf = 0; #define LOOKUPBUFSIZ 1000 static void -lookup(HKEY hk, char *key, char lkupbuf[LOOKUPBUFSIZ]) { - DWORD dwLength = LOOKUPBUFSIZ - 1; +lookup(HKEY hk, TCHAR *key, char lkupbuf[LOOKUPBUFSIZ]) { + DWORD dwLength = LOOKUPBUFSIZ - 2; lkupbuf[0] = 0; (void)RegQueryValueEx(hk, key, NULL, NULL, (BYTE *)lkupbuf, &dwLength); - if (lkupbuf[dwLength]) lkupbuf[dwLength] = 0; + lkupbuf[dwLength+1] = lkupbuf[dwLength] = 0; /* account for WCHAR terminating NULL */ } /* Print base registry info. Return true if DNS info was provided. */ From 32840ac857dbef54e25c61c3e7bb0a9dde5aca5f Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 02:06:13 +0100 Subject: [PATCH 19/36] Revert to ASCII only version of DnsInfo Rationale: there's no urge in providing localized UNICODE info... That's IP addresses, etc... Eventually, server names could be UNICODE but this seems to be a real mess! The short term goal is to enable compilation with -DUNICODE For longer term, we'll see later. --- platforms/win32/vm/sqWin32DnsInfo.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/platforms/win32/vm/sqWin32DnsInfo.c b/platforms/win32/vm/sqWin32DnsInfo.c index de25f44062..d219ffe326 100644 --- a/platforms/win32/vm/sqWin32DnsInfo.c +++ b/platforms/win32/vm/sqWin32DnsInfo.c @@ -12,11 +12,11 @@ static char *buf = 0; #define LOOKUPBUFSIZ 1000 static void -lookup(HKEY hk, TCHAR *key, char lkupbuf[LOOKUPBUFSIZ]) { - DWORD dwLength = LOOKUPBUFSIZ - 2; +lookup(HKEY hk, char *key, char lkupbuf[LOOKUPBUFSIZ]) { + DWORD dwLength = LOOKUPBUFSIZ - 1; lkupbuf[0] = 0; - (void)RegQueryValueEx(hk, key, NULL, NULL, (BYTE *)lkupbuf, &dwLength); - lkupbuf[dwLength+1] = lkupbuf[dwLength] = 0; /* account for WCHAR terminating NULL */ + (void)RegQueryValueExA(hk, key, NULL, NULL, (BYTE *)lkupbuf, &dwLength); + lkupbuf[dwLength] = 0; } /* Print base registry info. Return true if DNS info was provided. */ @@ -27,9 +27,9 @@ printBaseInfo(char *hkeyName) { int result = 0; char value[LOOKUPBUFSIZ]; - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, hkeyName, 0, KEY_READ, &hkey); + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, hkeyName, 0, KEY_READ, &hkey); if(ret != ERROR_SUCCESS) { - printf("RegOpenKeyEx failed\n"); + printf("RegOpenKeyExA failed\n"); return vm->primitiveFail(); } @@ -89,9 +89,9 @@ printAdapterInfo(char *hkeyName) { int result = 0; char value[LOOKUPBUFSIZ]; - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, hkeyName, 0, KEY_READ, &hkey); + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, hkeyName, 0, KEY_READ, &hkey); if(ret != ERROR_SUCCESS) { - printf("RegOpenKeyEx failed\n"); + printf("RegOpenKeyExA failed\n"); return vm->primitiveFail(); } @@ -166,11 +166,11 @@ EXPORT(int) primitiveDnsInfo(void) { goto done; /* Enumerate the available interfaces */ - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, BASE_KEY, 0, KEY_READ, &hkey); + ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, BASE_KEY, 0, KEY_READ, &hkey); for(index=0;;index++) { char keyName[1024]; dwLength = sizeof(adapter); - ret = RegEnumKeyEx(hkey, index, adapter, &dwLength, NULL, NULL, NULL, NULL); + ret = RegEnumKeyExA(hkey, index, adapter, &dwLength, NULL, NULL, NULL, NULL); if(ret != ERROR_SUCCESS) break; strcpy(keyName, BASE_KEY); strcat(keyName, "\\"); From 475d84cf63fc05ca814fa67016f46e90263d0894 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 02:41:08 +0100 Subject: [PATCH 20/36] iconPath is char*, LoadImage expects a TCHAR* We now interpret iconPath as UTF-8 encoded We convert it to WideChar and call the W version. TODO: for now, do not deal with UNC long filenames... --- .../plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c b/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c index d65368fe94..2ef1c49195 100644 --- a/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c +++ b/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c @@ -415,12 +415,14 @@ sqInt ioSetTitleOfWindow(sqInt windowIndex, char * newTitle, sqInt sizeOfTitle) * int size of new logo path. If one of the function is failing, the logo is not set. */ sqInt ioSetIconOfWindow(sqInt windowIndex, char * iconPath, sqInt sizeOfPath) { + WCHAR iconPathW[MAX_PATH + 1]; HWND hwnd = (windowIndex == 1 ? stWindow : ((HWND)windowIndex)); if (!IsWindow(hwnd)) return 0; + if(MultiByteToWideChar(CP_UTF8, 0, iconPath, sizeOfPath, iconPathW, MAX_PATH) ==0) return -1; /* invalide UTF8 ? */ //Check if file exists and have read rights - if (access(iconPath, 4) == -1) { return -1; }; + if (_waccess(iconPath, 4) == -1) { return -1; }; //Load the image into an icon - HICON hIcon = (HICON)LoadImage(NULL, iconPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); + HICON hIcon = (HICON)LoadImageW(NULL, iconPathW, IMAGE_ICON, 0, 0, LR_LOADFROMFILE); if (hIcon == 0) return -2; SendMessage(hwnd, WM_SETICON, ICON_BIG, (LONG_PTR)hIcon); From ef245b6d485b0286f648fda560b0564d812593e5 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 02:47:56 +0100 Subject: [PATCH 21/36] And account for the fact that iconPath is not NULL-TERMINATED! --- .../win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c b/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c index 2ef1c49195..b81f378587 100644 --- a/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c +++ b/platforms/win32/plugins/HostWindowPlugin/sqWin32HostWindowPlugin.c @@ -417,8 +417,11 @@ sqInt ioSetTitleOfWindow(sqInt windowIndex, char * newTitle, sqInt sizeOfTitle) sqInt ioSetIconOfWindow(sqInt windowIndex, char * iconPath, sqInt sizeOfPath) { WCHAR iconPathW[MAX_PATH + 1]; HWND hwnd = (windowIndex == 1 ? stWindow : ((HWND)windowIndex)); + int len; if (!IsWindow(hwnd)) return 0; - if(MultiByteToWideChar(CP_UTF8, 0, iconPath, sizeOfPath, iconPathW, MAX_PATH) ==0) return -1; /* invalide UTF8 ? */ + len = MultiByteToWideChar(CP_UTF8, 0, iconPath, sizeOfPath, iconPathW, MAX_PATH); + if(len <= 0) return -1; /* invalid UTF8 ? */ + iconPathW[len] = 0; //Check if file exists and have read rights if (_waccess(iconPath, 4) == -1) { return -1; }; //Load the image into an icon From 07ff6a63ab5841ece882f93092d900d36ca371b0 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 03:05:02 +0100 Subject: [PATCH 22/36] DPRINTF must take a TCHAR*fmt because wvsprintf does! though, vfprintf does not, so reconcile by using _vftprintf from --- platforms/win32/vm/sqWin32Main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index f6fb749beb..c55d5ae37f 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* _O_BINARY */ #include #include @@ -301,7 +302,7 @@ OutputConsoleString(char *string) } // recommend using DPRINTF ifdef'ing to DPRINTF for debug output -int __cdecl DPRINTF(const char *fmt, ...) +int __cdecl DPRINTF(const TCHAR *fmt, ...) { va_list al; va_start(al, fmt); @@ -309,7 +310,7 @@ int __cdecl DPRINTF(const char *fmt, ...) wvsprintf(consoleBuffer, fmt, al); OutputDebugString(consoleBuffer); } - vfprintf(stdout, fmt, al); + _vftprintf(stdout, fmt, al); va_end(al); return 1; } From dfe4d09597d2c6ed94ff9d1f6305df677bf39067 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 03:14:34 +0100 Subject: [PATCH 23/36] NOTIFYICONDATA.szTip maybe a WCHAR* if -DUNICODE so let's use appropriate TCHAR* functions/macros --- platforms/win32/vm/sqWin32Main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index c55d5ae37f..254756f925 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -412,7 +412,7 @@ void SetSystemTrayIcon(BOOL on) nid.uFlags = NIF_MESSAGE | NIF_TIP | NIF_ICON; nid.uCallbackMessage = WM_USER+42; nid.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(1)); - strcpy(nid.szTip, VM_NAME "!"); + _tcscpy(nid.szTip, TEXT(VM_NAME) TEXT("!")); if (on) (*ShellNotifyIcon)(NIM_ADD, &nid); else From db33158941b1a8e74224d98ff6e81b2f20e6959c Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 04:32:47 +0100 Subject: [PATCH 24/36] _DISPLAY_DEVICE.DeviceString may be a WCHAR* if -DUNICODE, we cannot simply sprintf We have to test #ifdef UNICODE, and if so, convert to UTF8 --- platforms/win32/vm/sqWin32Main.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 254756f925..27fe00d6de 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -681,12 +681,24 @@ void gatherSystemInfo(void) { osInfoString = _strdup(tmpString); } - sprintf(tmpString, - "Display Information: \n" - "\tGraphics adapter name: %s\n" - "\tPrimary monitor resolution: %d x %d\n", +#ifdef UNICODE + { + char buf[128*3]; + if(WideCharToMultiByte(CP_UTF8,0,gDev.DeviceString,-1,buf,sizeof(buf),NULL,NULL)) snprintf(tmpString, sizeof(tmpString), + "Display Information: \n" + "\tGraphics adapter name: %s\n" + "\tPrimary monitor resolution: %d x %d\n", + buf, + screenX, screenY); + } +#else + snprintf(tmpString, sizeof(tmpString), + TEXT("Display Information: \n") + TEXT("\tGraphics adapter name: %s\n") + TEXT("\tPrimary monitor resolution: %d x %d\n"), gDev.DeviceString, screenX, screenY); +#endif /* Find the driver key in the registry */ keyName[0] = 0; From 46bd992b7633dfc4f96b142104651313b826809c Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 04:26:21 +0100 Subject: [PATCH 25/36] VM_VERSION_TEXT is a TCHAR*, we cannot simply fprintf But there is no need for UNICODE in VM_VERSION_TEXT Revert to plain ASCII and rename it VM_VERSION_VERBOSE --- platforms/win32/vm/sqWin32Main.c | 2 +- platforms/win32/vm/sqWin32Prefs.c | 4 ++-- platforms/win32/vm/sqWin32Prefs.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 27fe00d6de..9ee2200308 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -1102,7 +1102,7 @@ extern char *__cogitBuildInfo; fprintf(f,"\n%s", gdInfoString); /* print VM version information */ - fprintf(f,"\nVM Version: %s\n", VM_VERSION_TEXT); + fprintf(f,"\nVM Version: %s\n", VM_VERSION_VERBOSE); #if STACKVM fprintf(f,"Interpreter Build: %s\n", __interpBuildInfo); # if COGVM diff --git a/platforms/win32/vm/sqWin32Prefs.c b/platforms/win32/vm/sqWin32Prefs.c index 43629ad5e1..a2a5cc5645 100644 --- a/platforms/win32/vm/sqWin32Prefs.c +++ b/platforms/win32/vm/sqWin32Prefs.c @@ -433,8 +433,8 @@ void TrackPrefsMenu(void) { void HandlePrefsMenu(int cmd) { switch(cmd) { case ID_ABOUT: - MessageBox(stWindow,VM_VERSION_TEXT, - TEXT("About ") TEXT(VM_NAME) TEXT(" on Win32"), MB_OK); + MessageBoxA(stWindow,VM_VERSION_VERBOSE, + "About " VM_NAME " on Win32", MB_OK); break; case ID_DEFERUPDATES: fDeferredUpdate = !fDeferredUpdate; diff --git a/platforms/win32/vm/sqWin32Prefs.h b/platforms/win32/vm/sqWin32Prefs.h index 26864d4925..0133d1b3e3 100644 --- a/platforms/win32/vm/sqWin32Prefs.h +++ b/platforms/win32/vm/sqWin32Prefs.h @@ -50,7 +50,7 @@ int prefsEnableF2Menu(void); # define NICKNAME_EXTRA " VM " #endif -#define VM_VERSION_TEXT TEXT(NICKNAME) TEXT(NICKNAME_EXTRA) TEXT(VM_VERSION) \ - TEXT(" (release) from ") TEXT(__DATE__) TEXT("\n") \ - TEXT("Compiler: ") TEXT(COMPILER) TEXT(VERSION) +#define VM_VERSION_VERBOSE NICKNAME NICKNAME_EXTRA VM_VERSION \ + " (release) from " __DATE__ "\n" \ + "Compiler: " COMPILER VERSION #endif From c5f207c84ce1a9dca04ff4126e24a7489986c9c5 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 03:25:37 +0100 Subject: [PATCH 26/36] iniName, manufacturer and model may be WCHAR* if -DUNICODE Let iniName be WCHAR unconditionally. Get manufacturer and model into a UTF16 buffer, then convert them to UTF8 while at it, protect buffer overrun strcat - > wcsNcat --- platforms/win32/vm/sqWin32Main.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 9ee2200308..18831786fb 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -559,18 +559,21 @@ void gatherSystemInfo(void) { } { /* Figure out make and model from OEMINFO.ini */ - char iniName[256]; - char manufacturer[256]; - char model[256]; + WCHAR iniName[MAX_PATH]; + WCHAR bufferW[MAX_PATH]; + char manufacturer[MAX_PATH_UTF8]; + char model[MAX_PATH_UTF8]; - GetSystemDirectory(iniName, 256); - strcat(iniName,"\\OEMINFO.INI"); + GetSystemDirectoryW(iniName, MAX_PATH); + wcsncat(iniName,L"\\OEMINFO.INI",MAX_PATH); - GetPrivateProfileString("General", "Manufacturer", "Unknown", - manufacturer, 256, iniName); + GetPrivateProfileStringW(L"General", L"Manufacturer", L"Unknown", + bufferW, MAX_PATH, iniName); + if (WideCharToMultiByte(CP_UTF8, 0, bufferW, -1, manufacturer, MAX_PATH_UTF8, NULL, NULL) == 0) strcpy(manufacturer, "???"); - GetPrivateProfileString("General", "Model", "Unknown", - model, 256, iniName); + GetPrivateProfileStringW(L"General", L"Model", L"Unknown", + bufferW, MAX_PATH, iniName); + if (WideCharToMultiByte(CP_UTF8, 0, bufferW, -1, model, MAX_PATH_UTF8, NULL, NULL) == 0) strcpy(model, "???"); sprintf(tmpString, "Hardware information: \n" From d9b3927ed91ef60eeca7ec14dd7f48ccb6022cde Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 05:24:51 +0100 Subject: [PATCH 27/36] Information queried in Registry can be WideChar if -DUNICODE We want to generate an UTF8 report (do we really?). So provide a `RegLookupUTF8String`, so as to make all the queries with W variant, then convert all information to UTF8 While at it, use snprintf instead of sprintf and strncat instead of strcat --- platforms/win32/vm/sqWin32Main.c | 217 ++++++++++++++++--------------- 1 file changed, 113 insertions(+), 104 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 18831786fb..282829ba40 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -519,6 +519,18 @@ char *hwInfoString = ""; char *gdInfoString = ""; char *win32VersionName = ""; +void RegLookupUTF8String(HKEY hk, WCHAR *name, char *utf8, size_t utf8_len) +{ + WCHAR bufferW[MAX_PATH]; + DWORD dwSize = MAX_PATH * sizeof(WCHAR); + LSTATUS ok = RegQueryValueExW(hk, name, NULL, NULL,(LPBYTE)bufferW, &dwSize); + if (ok != ERROR_SUCCESS) strncpy(utf8, "???",utf8_len); + else { + int len = WideCharToMultiByte(CP_UTF8, 0, bufferW, -1, utf8, utf8_len, NULL, NULL); + if (len <= 0) strncpy(utf8, "???", utf8_len); + } +} + void gatherSystemInfo(void) { OSVERSIONINFOEX osInfo; MEMORYSTATUS memStat; @@ -527,7 +539,7 @@ void gatherSystemInfo(void) { int proc, screenX, screenY; char tmpString[2048]; - char keyName[256]; + WCHAR keyName[256]; DWORD ok, dwSize; HKEY hk; @@ -606,30 +618,23 @@ void gatherSystemInfo(void) { char *tmp = tmpString + strlen(tmpString); - strcpy(keyName, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"); - keyName[strlen(keyName)-1] = 48+proc; /* 0, 1, 2 etc. */ + wcscpy(keyName, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"); + keyName[wcslen(keyName)-1] = 48+proc; /* 0, 1, 2 etc. */ - ok = RegOpenKey(HKEY_LOCAL_MACHINE, keyName, &hk); + ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hk); if (!ok) { - char nameString[256]; - char identifier[256]; + char nameString[MAX_PATH_UTF8]; + char identifier[MAX_PATH_UTF8]; DWORD mhz; - dwSize = 256; - ok = RegQueryValueEx(hk, "ProcessorNameString", NULL, NULL, - (LPBYTE)nameString, &dwSize); - if (ok) strcpy(nameString, "???"); - - dwSize = 256; - ok = RegQueryValueEx(hk, "Identifier", NULL, NULL, - (LPBYTE)identifier, &dwSize); - if (ok) strcpy(identifier, "???"); + RegLookupUTF8String(hk, L"ProcessorNameString", nameString, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"Identifier", identifier, MAX_PATH_UTF8); dwSize = sizeof(DWORD); - ok = RegQueryValueEx(hk, "~MHz", NULL, NULL, + ok = RegQueryValueExW(hk, L"~MHz", NULL, NULL, (LPBYTE)&mhz, &dwSize); if (ok) mhz = -1; - sprintf(tmp, + snprintf(tmp, sizeof(tmpString)-strlen(tmpString), "\nProcessor %d: %s\n" "\tIdentifier: %s\n" "\t~MHZ: %lu\n", @@ -641,30 +646,50 @@ void gatherSystemInfo(void) { hwInfoString = _strdup(tmpString); { - char owner[256]; - char company[256]; - char product[256]; + char owner[MAX_PATH_UTF8]; + char company[MAX_PATH_UTF8]; + char product[MAX_PATH_UTF8]; if (osInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { - strcpy(keyName, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); + wcscpy(keyName, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); } else { - strcpy(keyName, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion"); + wcscpy(keyName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion"); } - ok = RegOpenKey(HKEY_LOCAL_MACHINE, keyName, &hk); + ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hk); if (!ok) { - dwSize = 256; - if (RegQueryValueEx(hk, "RegisteredOwner", NULL, NULL, - (LPBYTE)owner, &dwSize)) strcpy(owner, "???"); - dwSize = 256; - if (RegQueryValueEx(hk, "RegisteredOrganization", NULL, NULL, - (LPBYTE)company, &dwSize)) strcpy(company, "???"); - dwSize = 256; - if (RegQueryValueEx(hk, "ProductName", NULL, NULL, - (LPBYTE)product, &dwSize)) strcpy(product, "???"); - RegCloseKey(hk); - } + RegLookupUTF8String(hk, L"RegisteredOwner", owner, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"RegisteredOrganization", company, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"ProductName", product, MAX_PATH_UTF8); + } else { + strcpy(owner, "???"); + strcpy(company, "???"); + strcpy(product, "???"); + } - sprintf(tmpString, +#if defined(_MSC_VER) && _MSC_VER < 1300 +# define wSuiteMask wReserved[0] +# define wProductType wReserved[1] & 0xFF +#endif +#ifdef UNICODE + { + char buf[128 * 3]; + if(WideCharToMultiByte(CP_UTF8,0, osInfo.szCSDVersion,-1,buf,sizeof(buf),NULL,NULL)) + snprintf(tmpString, sizeof(tmpString), + "Operating System: %s (Build %lu %s)\n" + "\tRegistered Owner: %s\n" + "\tRegistered Company: %s\n" + "\tSP major version: %d\n" + "\tSP minor version: %d\n" + "\tSuite mask: %x\n" + "\tProduct type: %x\n", + product, + osInfo.dwBuildNumber, buf, + owner, company, + osInfo.wServicePackMajor, osInfo.wServicePackMinor, + osInfo.wSuiteMask, osInfo.wProductType); + } +#else + snprintf(tmpString,sizeof(tmpString), "Operating System: %s (Build %lu %s)\n" "\tRegistered Owner: %s\n" "\tRegistered Company: %s\n" @@ -676,11 +701,8 @@ void gatherSystemInfo(void) { osInfo.dwBuildNumber, osInfo.szCSDVersion, owner, company, osInfo.wServicePackMajor, osInfo.wServicePackMinor, -#if defined(_MSC_VER) && _MSC_VER < 1300 -# define wSuiteMask wReserved[0] -# define wProductType wReserved[1] & 0xFF -#endif osInfo.wSuiteMask, osInfo.wProductType); +#endif osInfoString = _strdup(tmpString); } @@ -705,24 +727,24 @@ void gatherSystemInfo(void) { /* Find the driver key in the registry */ keyName[0] = 0; - ok = RegOpenKey(HKEY_LOCAL_MACHINE, - "HARDWARE\\DEVICEMAP\\VIDEO", + ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, + L"HARDWARE\\DEVICEMAP\\VIDEO", &hk); if (!ok) { - dwSize = 256; - RegQueryValueEx(hk,"\\Device\\Video0", NULL, NULL, + dwSize = 256*sizeof(WCHAR); + RegQueryValueExW(hk,L"\\Device\\Video0", NULL, NULL, (LPBYTE)keyName, &dwSize); RegCloseKey(hk); } if (*keyName) { /* Got the key name; open it and get the info out of there */ char *tmp = tmpString + strlen(tmpString); - char deviceDesc[256]; - char adapterString[256]; - char biosString[256]; - char chipType[256]; - char dacType[256]; - char *drivers, *drv; + char deviceDesc[MAX_PATH_UTF8]; + char adapterString[MAX_PATH_UTF8]; + char biosString[MAX_PATH_UTF8]; + char chipType[MAX_PATH_UTF8]; + char dacType[MAX_PATH_UTF8]; + WCHAR *drivers, *drv; WCHAR buffer[256]; DWORD memSize; @@ -730,48 +752,20 @@ void gatherSystemInfo(void) { with \Registry\Machine\ which doesn't work with RegOpenKey below. I have no idea why but for now I'll just truncate that part if we recognize it... */ - if (_strnicmp(keyName, "\\registry\\machine\\", 18) == 0) { - memcpy(keyName, keyName+18, strlen(keyName)-17); + if (_wcsnicmp(keyName, L"\\registry\\machine\\", 18) == 0) { + memmove(keyName, keyName+18*sizeof(WCHAR), (wcslen(keyName)-17)*sizeof(WCHAR)); } - ok = RegOpenKey(HKEY_LOCAL_MACHINE, keyName, &hk); - if (ok) MessageBox(0, keyName, "Cannot open:", MB_OK); + ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hk); + if (ok) MessageBoxW(0, keyName, L"Cannot open:", MB_OK); if (!ok) { - dwSize = 256; - ok = RegQueryValueEx(hk,"Device Description", NULL, NULL, - (LPBYTE)deviceDesc, &dwSize); - if (ok) strcpy(deviceDesc, "???"); - - dwSize = 256*sizeof(WCHAR); - ok = RegQueryValueEx(hk,"HardwareInformation.AdapterString", NULL, NULL, - (LPBYTE)buffer, &dwSize); - if (!ok) { - WideCharToMultiByte(CP_UTF8,0,buffer,-1,adapterString,256,NULL,NULL); - } else strcpy(adapterString, "???"); - - dwSize = 256*sizeof(WCHAR); - ok = RegQueryValueEx(hk,"HardwareInformation.BiosString", NULL, NULL, - (LPBYTE)buffer, &dwSize); - if (!ok) { - WideCharToMultiByte(CP_UTF8,0,buffer,-1,biosString,256,NULL,NULL); - } else strcpy(biosString, "???"); - - dwSize = 256*sizeof(WCHAR); - ok = RegQueryValueEx(hk,"HardwareInformation.ChipType", NULL, NULL, - (LPBYTE)buffer, &dwSize); - if (!ok) { - WideCharToMultiByte(CP_UTF8,0,buffer,-1,chipType,256,NULL,NULL); - } else strcpy(chipType, "???"); - - dwSize = 256*sizeof(WCHAR); - ok = RegQueryValueEx(hk,"HardwareInformation.DacType", NULL, NULL, - (LPBYTE)buffer, &dwSize); - if (!ok) { - WideCharToMultiByte(CP_UTF8,0,buffer,-1,dacType,256,NULL,NULL); - } else strcpy(dacType, "???"); - + RegLookupUTF8String(hk, L"Device Description", deviceDesc, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"HardwareInformation.AdapterString", adapterString, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"HardwareInformation.BiosString", biosString, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"HardwareInformation.ChipType", chipType, MAX_PATH_UTF8); + RegLookupUTF8String(hk, L"HardwareInformation.DacType", dacType, MAX_PATH_UTF8); dwSize = sizeof(DWORD); - ok = RegQueryValueEx(hk,"HardwareInformation.MemorySize", NULL, NULL, + ok = RegQueryValueExW(hk,L"HardwareInformation.MemorySize", NULL, NULL, (LPBYTE)&memSize, &dwSize); if (ok) memSize = -1; @@ -790,47 +784,62 @@ void gatherSystemInfo(void) { memSize); /* Now process the installed drivers */ - ok = RegQueryValueEx(hk,"InstalledDisplayDrivers", + ok = RegQueryValueExW(hk,L"InstalledDisplayDrivers", NULL, NULL, NULL, &dwSize); if (!ok) { drivers = malloc(dwSize); - ok = RegQueryValueEx(hk,"InstalledDisplayDrivers", + ok = RegQueryValueExW(hk,L"InstalledDisplayDrivers", NULL, NULL, (LPBYTE)drivers, &dwSize); } if (!ok) { - strcat(tmpString,"\nDriver Versions:"); + strncat(tmpString,"\nDriver Versions:",sizeof(tmpString)); /* InstalledDrivers is REG_MULTI_SZ (extra terminating zero) */ - for(drv = drivers; drv[0]; drv +=strlen(drv)) { + for(drv = drivers; drv[0]; drv +=wcslen(drv)) { DWORD verSize, hh; UINT vLen; LPVOID verInfo = NULL, vInfo; + char drvA[MAX_PATH_UTF8]; /* Concat driver name */ - strcat(tmpString,"\n\t"); - strcat(tmpString, drv); - strcat(tmpString,": "); + if (WideCharToMultiByte(CP_UTF8,0,drv,-1,drvA,MAX_PATH_UTF8,NULL,NULL)>0) { + strncat(tmpString, "\n\t", sizeof(tmpString)); + strncat(tmpString, drvA , sizeof(tmpString)); + strncat(tmpString, ": " , sizeof(tmpString)); + } - verSize = GetFileVersionInfoSize(drv, &hh); + verSize = GetFileVersionInfoSizeW(drv, &hh); if (!verSize) goto done; verInfo = malloc(verSize); - if (!GetFileVersionInfo(drv, 0, verSize, verInfo)) goto done; + if (!GetFileVersionInfoW(drv, 0, verSize, verInfo)) goto done; /* Try Unicode first */ - if (VerQueryValue(verInfo,"\\StringFileInfo\\040904B0\\FileVersion", + if (VerQueryValueW(verInfo,L"\\StringFileInfo\\040904B0\\FileVersion", &vInfo, &vLen)) { - strcat(tmpString, vInfo); - goto done; + int utf8_len = WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, NULL, 0, NULL, NULL); + char *utf8 = (char *)malloc(utf8_len); + if(utf8) { + WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, utf8, utf8_len, NULL, NULL); + strncat(tmpString, utf8,sizeof(tmpString)); + free(utf8); + goto done; + } } /* Try US/English next */ - if (VerQueryValue(verInfo,"\\StringFileInfo\\040904E4\\FileVersion", + if (VerQueryValueW(verInfo,L"\\StringFileInfo\\040904E4\\FileVersion", &vInfo, &vLen)) { - strcat(tmpString, vInfo); - goto done; + int utf8_len = WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, NULL, 0, NULL, NULL); + char *utf8 = (char *)malloc(utf8_len); + if (utf8) { + WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, utf8, utf8_len, NULL, NULL); + strncat(tmpString, utf8, sizeof(tmpString)); + free(utf8); + goto done; + } } - strcat(tmpString, "???"); + strncat(tmpString, "???", sizeof(tmpString)); done: if (verInfo) { @@ -838,7 +847,7 @@ void gatherSystemInfo(void) { verInfo = NULL; } } - strcat(tmpString,"\n"); + strncat(tmpString,"\n",sizeof(tmpString)); } RegCloseKey(hk); } From 8b14fbfcb43951d7a7f87be1d927fadc7db85007 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 05:33:54 +0100 Subject: [PATCH 28/36] Make stderrName and stdoutName be WCHAR* There was a mixture of TCHAR* and char* which could not work with -DUNICODE Who knows, the TempPath might be localized, so go UNICODE... --- platforms/win32/vm/sqWin32Main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 282829ba40..dc2a4f0e9b 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -128,8 +128,8 @@ BOOL fIsConsole = 0; TCHAR consoleBuffer[4096]; /* stderr and stdout names */ -char stderrName[MAX_PATH+1]; -char stdoutName[MAX_PATH+1]; +WCHAR stderrName[MAX_PATH+1]; +WCHAR stdoutName[MAX_PATH+1]; static char vmLogDirA[MAX_PATH_UTF8]; static WCHAR vmLogDirW[MAX_PATH]; @@ -1026,22 +1026,22 @@ versionInfo(void) /* Error handling */ /****************************************************************************/ void SetupStderr() -{ TCHAR tmpName[MAX_PATH+1]; +{ WCHAR tmpName[MAX_PATH+1]; *stderrName = *stdoutName = 0; /* re-open stdout && stderr */ - GetTempPath(MAX_PATH,tmpName); + GetTempPathW(MAX_PATH,tmpName); if(GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) { - GetTempFileName(tmpName,TEXT("sq"),0,stderrName); - freopen(stderrName,"w+t",stderr); + GetTempFileNameW(tmpName,L"sq",0,stderrName); + _wfreopen(stderrName,L"w+t",stderr); } else *stderrName = 0; if(GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) { - GetTempFileName(tmpName,TEXT("sq"),0,stdoutName); - freopen(stdoutName,"w+t",stdout); + GetTempFileNameW(tmpName,L"sq",0,stdoutName); + _wfreopen(stdoutName,L"w+t",stdout); } else *stdoutName = 0; } @@ -1414,12 +1414,12 @@ void __cdecl Cleanup(void) if(*stderrName) { fclose(stderr); - remove(stderrName); + _wremove(stderrName); } if(*stdoutName) { fclose(stdout); - remove(stdoutName); + _wremove(stdoutName); } OleUninitialize(); } From 643a5e3bc03142a49115588a1b215cf963563e42 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 11:45:03 +0100 Subject: [PATCH 29/36] Retract support for Windows95 (no you don't dream it's nearly 2019!) I'm very sorry to dilapidate all this historical knowledge, but frankly YAGNI! Even if we are a museum, we can't expose all our art in permanent collections! Note that minimal version is already set to XP (via WINVER:=-D_WIN32_WINNT=0x0501 -DWINVER=0x0501 in Makefile.tools) So we're keeping this stuf for nothing, and now it gets in our way to UNICODE. --- platforms/win32/vm/sqWin32.h | 3 +- platforms/win32/vm/sqWin32Main.c | 68 ++-------------- platforms/win32/vm/sqWin32Service.c | 121 ---------------------------- platforms/win32/vm/sqWin32Window.c | 7 +- 4 files changed, 10 insertions(+), 189 deletions(-) diff --git a/platforms/win32/vm/sqWin32.h b/platforms/win32/vm/sqWin32.h index 42a17e6131..4123ca98fd 100644 --- a/platforms/win32/vm/sqWin32.h +++ b/platforms/win32/vm/sqWin32.h @@ -104,7 +104,7 @@ /* Definition for Intel Processors */ #if defined(_M_IX86) || defined(i386) # define WIN32_NAME "Win32" -# define WIN32_OS_NAME (fWindows95 ? "95" : "NT") +# define WIN32_OS_NAME "NT" # define WIN32_PROCESSOR_NAME "IX86" # if defined(X86) @@ -307,7 +307,6 @@ extern BITMAPINFO *bmi4; /* 4 bit depth bitmap info */ extern BITMAPINFO *bmi8; /* 8 bit depth bitmap info */ extern BITMAPINFO *bmi16; /* 16 bit depth bitmap info */ extern BITMAPINFO *bmi32; /* 32 bit depth bitmap info */ -extern BOOL fWindows95; /* Are we running on Win95 or NT? */ extern BOOL fIsConsole; /* Are we running as a console app? */ /* Startup options */ diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index dc2a4f0e9b..2c3c0fc754 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -368,12 +368,6 @@ static messageHook nextMessageHook = NULL; int ServiceMessageHook(void * hwnd, unsigned int message, unsigned int wParam, long lParam) { - if (fRunService && fWindows95 && message == WM_BROADCAST_SERVICE && hwnd == stWindow) - { - /* broadcast notification - install the running Win95 service in the system tray */ - SetSystemTrayIcon(1); - return 1; - } if (message == WM_USERCHANGED) { SetSystemTrayIcon(1); @@ -419,33 +413,6 @@ void SetSystemTrayIcon(BOOL on) (*ShellNotifyIcon)(NIM_DELETE, &nid); } -void SetupService95() -{ -#ifndef NO_SERVICE -#ifndef RSP_SIMPLE_SERVICE -#define RSP_SIMPLE_SERVICE 1 -#endif - BOOL (WINAPI *RegisterServiceProcess)(DWORD,DWORD); - static HMODULE hKernel32 = NULL; - - /* Inform Windows95 that we're running as a service process */ - if (!fRunService || !fWindows95) return; - hKernel32 = LoadLibrary(TEXT("kernel32.dll")); - if (!hKernel32) - { - printLastError(TEXT("Unable to load kernel32.dll")); - return; - } - (FARPROC) RegisterServiceProcess = GetProcAddress(hKernel32, "RegisterServiceProcess"); - if (!RegisterServiceProcess) - { - printLastError(TEXT("Unable to find RegisterServiceProcess")); - return; - } - if ( !(*RegisterServiceProcess)(GetCurrentProcessId(), RSP_SIMPLE_SERVICE ) ) - printLastError(TEXT("RegisterServiceProcess failed")); -#endif /* NO_SERVICE */ -} /****************************************************************************/ /* System Attributes */ @@ -1406,7 +1373,7 @@ void __cdecl Cleanup(void) ioReleaseTime(); /* tricky ... we have no systray icon when running headfull or when running as service on NT */ - if(fHeadlessImage && (!fRunService || fWindows95)) + if(fHeadlessImage && (!fRunService)) SetSystemTrayIcon(0); if(palette) DeleteObject(palette); PROFILE_SHOW(ticksForReversal); @@ -1582,11 +1549,6 @@ sqMain(int argc, char *argv[]) fRunService = 0; #endif - /* look for a few things easy to handle */ - if(fWindows95 && fBroadcastService95) { - PostMessage(HWND_BROADCAST, WM_BROADCAST_SERVICE, 0, 0); - return 0; - } /* set time zone accordingly */ _tzset(); @@ -1602,10 +1564,7 @@ sqMain(int argc, char *argv[]) svcStart = time(NULL); OutputLogMessage("\n\n"); OutputLogMessage(ctime(&svcStart)); - if(fWindows95) /* don't have a service name */ - OutputLogMessage("The service"); - else - OutputLogMessage(serviceName); + OutputLogMessage(serviceName); OutputLogMessage(" started with the following command line\n"); OutputLogMessage(initialCmdLine); OutputLogMessage("\n"); @@ -1632,7 +1591,6 @@ sqMain(int argc, char *argv[]) SetupKeymap(); SetupWindows(); SetupPixmaps(); - SetupService95(); { extern void ioInitTime(void); extern void ioInitThreads(void); ioInitTime(); @@ -1701,7 +1659,7 @@ sqMain(int argc, char *argv[]) /* if headless running is requested, try to to create an icon in the Win95/NT system tray */ - if(fHeadlessImage && (!fRunService || fWindows95)) + if(fHeadlessImage && (!fRunService)) SetSystemTrayIcon(1); /* read the image file */ @@ -1750,9 +1708,6 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) /* a few things which need to be done first */ gatherSystemInfo(); - /* check if we're running NT or 95 */ - fWindows95 = (GetVersion() & 0x80000000) != 0; - /* fetch us a copy of the command line */ initialCmdLine = _strdup(lpCmdLine); @@ -1808,21 +1763,14 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) if somebody out there knows how to find out when we're starting as a service - LET ME KNOW! */ - if(!fWindows95) /* 1) running NT */ - if(!*lpCmdLine) /* 2) No command line */ - if(sqServiceMain()) /* try starting the service */ - return 0; /* service was run - exit */ + if(!*lpCmdLine) /* No command line */ + if(sqServiceMain()) /* try starting the service */ + return 0; /* service was run - exit */ #endif SQ_LAUNCH_DROP = RegisterWindowMessage(TEXT("SQUEAK_LAUNCH_DROP")); - /* Special startup stuff for windows 95 */ - if(fWindows95) { - /* The message we use for notifying services of user logon */ - WM_BROADCAST_SERVICE = RegisterWindowMessage(msgBroadcastService); - } - /* start the non-service version */ sqMain(clargc, clargv); return 0; @@ -2047,10 +1995,6 @@ parseVMArgument(int argc, char *argv[]) #endif return 1; } - /* service support on 95 */ - else if (!strcmp(argv[0], VMOPTION("service95"))) { fRunService = true; return 1; } - else if (!strcmp(argv[0], VMOPTION("broadcast95"))) { fBroadcastService95 = true; return 1; } - return 0; /* option not recognised */ } diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index ac119077e6..e734bf51a8 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -265,109 +265,6 @@ sqInstallService(LPCTSTR serviceName, LPCTSTR serviceExe) return schService != NULL; } -/**************************************************************************** - Service Install/deinstall functions for Windows 95 - ****************************************************************************/ - -/* sqInstallService95: install the named service on a Windows 95 system */ -int -sqInstallService95(LPTSTR serviceName) -{ TCHAR tmpString[1024]; - DWORD dwSize, dwType; - HKEY hk; - int ok; - - /* Add a broadcast for user startup */ - ok = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"), &hk); - failOn(ok != ERROR_SUCCESS, TEXT("RegOpenKey failed")); - dwSize = 1024; - ok = RegQueryValueEx(hk, TEXT("SqueakBroadcastNotification"),NULL, &dwType, (LPBYTE) &tmpString, &dwSize); - /* ERROR_SUCCESS indicates that we have already an entry */ - if(ok != ERROR_SUCCESS) - /* ERROR_FILE_NOT_FOUND indicates no entry */ - if(ok != ERROR_FILE_NOT_FOUND) - failOn(1 ,TEXT("RegQueryValueEx failed")); - - /* set the new service entry */ - GetModuleFileName(hInstance, tmpString, 1024); - lstrcat(tmpString,TEXT(" -broadcast95")); - ok = RegSetValueEx(hk, /* subkey handle */ - TEXT("SqueakBroadcastNotification"), /* value name */ - 0, /* must be zero */ - REG_SZ, /* value type */ - (LPBYTE) &tmpString, /* address of value data */ - lstrlen(tmpString)); /* length of value data */ - failOn(ok != ERROR_SUCCESS, TEXT("RegSetValueEx failed")); - RegCloseKey(hk); - - ok = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunServices"), &hk); - if(ok != ERROR_SUCCESS) { - /* ERROR_FILE_NOT_FOUND indicates no entry */ - if(ok != ERROR_FILE_NOT_FOUND) - failOn(1, TEXT("RegOpenKey failed")); - ok = RegCreateKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunServices"), &hk); - failOn(ok != ERROR_SUCCESS, TEXT("RegCreateKey failed")); - } else { /* check for any existing service */ - dwSize = 1024; - ok = RegQueryValueEx(hk,serviceName,NULL, &dwType, (LPBYTE) &tmpString, &dwSize); - /* ERROR_SUCCESS indicates that we have already an entry */ - if(ok == ERROR_SUCCESS) { - /* The service already exists - ask the user to remove it */ - if(MessageBox(0,TEXT("A service of this name already exists. Try to remove it?"), - serviceName, MB_YESNOCANCEL) != IDYES) - return 0; /* Just return, in any other case we just overwrite the entry */ - } else { /* ERROR_FILE_NOT_FOUND indicates no entry */ - if(ok != ERROR_FILE_NOT_FOUND) - failOn(1 , TEXT("RegQueryValueEx failed")); - } - } - /* set the new service entry */ - lstrcpy(tmpString, printCommandLine(1)); - ok = RegSetValueEx(hk, /* subkey handle */ - serviceName, /* value name */ - 0, /* must be zero */ - REG_SZ, /* value type */ - (LPBYTE) &tmpString, /* address of value data */ - lstrlen(tmpString)); /* length of value data */ - failOn(ok != ERROR_SUCCESS, TEXT("RegSetValueEx failed")); - RegCloseKey(hk); - - return 1; -} - -/* sqStartService95: start the named service on a Windows 95 system */ -int -sqStartService95(LPTSTR serviceName) -{ TCHAR tmpString[1025]; - STARTUPINFO sInfo; - PROCESS_INFORMATION pInfo; - DWORD dwSize, dwType; - HKEY hk; - int ok; - - ok = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunServices"), &hk); - failOn(ok != ERROR_SUCCESS, TEXT("RegOpenKey failed")); - /* get the name of the program to run */ - dwSize = 1024; - ok = RegQueryValueEx(hk,serviceName,NULL, &dwType, (LPBYTE) &tmpString, &dwSize); - failOn(ok != ERROR_SUCCESS, TEXT("RegQueryValueEx failed")); - RegCloseKey(hk); - tmpString[dwSize] = 0; - /* and start up */ - ZeroMemory(&sInfo, sizeof(sInfo)); - ok = CreateProcess(NULL, /* no image name */ - tmpString, /* but full command line */ - NULL, /* no process security attributes */ - NULL, /* no thread security attributes */ - FALSE, /* don't inherit handles */ - 0, /* no special flags */ - NULL, /* no separate environment */ - NULL, /* no startup dir */ - &sInfo, /* startup info */ - &pInfo); /* process info */ - failOn(ok == 0, TEXT("CreateProcess failed")); - return 1; -} /**************************************************************************** Main service install function @@ -379,24 +276,6 @@ void sqServiceInstall(void) TCHAR tmp[1024]; DWORD ok; - if( fWindows95 ) - { /* Running on Win95 */ - if(!sqInstallService95(serviceName)) - { - warnPrintf(TEXT("The service was NOT installed.")); - return; - } - if(MessageBox(0,TEXT("Service installation successful.\n") - TEXT("Do you wish to start the service right now?"), - serviceName,MB_YESNOCANCEL) == IDYES) - { - if(!sqStartService95(serviceName)) - warnPrintf(TEXT("The service was NOT started.\n") - TEXT("You have to restart your system first.")); - } - /* success */ - exit(0); - } /* get the VM name */ GetModuleFileName(hInstance,tmp, 255); if(!sqInstallService(serviceName, tmp)) diff --git a/platforms/win32/vm/sqWin32Window.c b/platforms/win32/vm/sqWin32Window.c index 741fff4972..66502deb91 100644 --- a/platforms/win32/vm/sqWin32Window.c +++ b/platforms/win32/vm/sqWin32Window.c @@ -107,7 +107,6 @@ BITMAPINFO *bmi4; /* 4 bit depth bitmap info */ BITMAPINFO *bmi8; /* 8 bit depth bitmap info */ BITMAPINFO *bmi16; /* 16 bit depth bitmap info */ BITMAPINFO *bmi32; /* 32 bit depth bitmap info */ -BOOL fWindows95; /* Are we running on Win95 or NT? */ BOOL fHasFocus = 0; /* if Squeak has the input focus */ /* Preference values */ @@ -919,7 +918,7 @@ void SetupWindows() updateRgn = CreateRectRgn(0,0,1,1); /* No windows at all when running as NT service */ - if(fRunService && !fWindows95) return; + if(fRunService) return; wc.style = CS_OWNDC; /* don't waste resources ;-) */ wc.lpfnWndProc = (WNDPROC)MainWndProcA; @@ -1628,7 +1627,7 @@ sqInt ioProcessEvents(void) int result; extern sqInt inIOProcessEvents; - if (fRunService && !fWindows95) return 1; + if (fRunService) return 1; #if NewspeakVM /* inIOProcessEvents controls ioProcessEvents. If negative then @@ -1713,7 +1712,7 @@ ioDrainEventQueue(void) { static MSG msg; POINT mousePt; - if(fRunService && !fWindows95) return 1; + if(fRunService) return 1; /* WinCE doesn't retrieve WM_PAINTs from the queue with PeekMessage, so we won't get anything painted unless we use GetMessage() if there From 1f616371ff559224ad6200d1a1109e6c1a5007b3 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Mon, 31 Dec 2018 16:10:18 +0100 Subject: [PATCH 30/36] Handle UTF16 logName and serviceName MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are the last two obstacles that generate compiler warnings with -DUNICODE No compiler warning does not mean that UNICODE is OK and ready to go. It means that we have at least eliminated all the trivial TCHAR*/char*/WCHAR* mismatch (and there were a bunch of them!!!) Also the code is like a battlefield with lot of different ad hoc recipes, that would deserve more uniform approach (refactoring) But small steps! To reach the current stage, we have to overhaul printCommandLine() It can't answer a TCHAR* when sqMain expects a char*argv[]... So, like what is done in WinMain thru getCommandLineW, we first produce a Wide command line, then convert to UTF8. There is currently no provision for conversion failure: I don't know how to report and exit when the VM is ran as a service. Note that command line parsing is then working the other way around: some of the arguments are converted back to UTF16. I call this style tricotage coding: une maille à l'endroit, une maille à l'envers. Maybe a deeper change will be to WCHAR* all the way down, but let's differ this decision. Small steps! Note that another source of problems is RegQueryValueExW. When we query a WCHAR* string, it is not necessarily NULL terminated. But every answer is handled as raw un-interpretd-bytes (char *), whose byte length is answered in dwSize. That's not the WCHAR character length! So buffer[dwSize] = 0 is not the right thing: - if buffer is declared WVCHAR*, this would write the NULL well beyond the real string length (and eventually overrun the buffer); - if buffer is declared char*, this would not guaranty a terminating NULL WCHAR (we need 2 zero bytes to make a NULL WCHAR). Usage of this function will require a careful review, the price of low level API... Also note that we seem to compile with flag -DNO_SERVICE right now. Why? I have not enough knowledge in this area and lack tests/examples. In that conditions, it's not easy to test the modifications! Any help will be greatly appreciated. The good part is that It won't break current VM usage... You know what I think of dead-code, but half alive Frankenstein code scares me as well ;) --- platforms/win32/vm/sqWin32Main.c | 54 ++++++--- platforms/win32/vm/sqWin32Service.c | 169 +++++++++++++--------------- 2 files changed, 113 insertions(+), 110 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 2c3c0fc754..93e84fe66a 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -134,7 +134,7 @@ WCHAR stdoutName[MAX_PATH+1]; static char vmLogDirA[MAX_PATH_UTF8]; static WCHAR vmLogDirW[MAX_PATH]; -TCHAR *logName = TEXT(""); /* full path and name to log file */ +WCHAR *logName = L""; /* full path and name to log file */ #ifdef VISTA_SECURITY BOOL fLowRights = 0; /* started as low integiry process, @@ -142,9 +142,8 @@ BOOL fLowRights = 0; /* started as low integiry process, #endif /* VISTA_SECURITY */ /* Service stuff */ -TCHAR serviceName[MAX_PATH+1]; /* The name of the NT service */ -TCHAR *installServiceName = NULL; /* the name under which the service is to install */ -BOOL fBroadcastService95 = 0; /* Do we need a broadcast when a user has logged on? */ +WCHAR serviceName[MAX_PATH+1]; /* The name of the NT service */ +WCHAR *installServiceName = NULL; /* the name under which the service is to install */ UINT WM_BROADCAST_SERVICE = 0; /* The broadcast message we send */ TCHAR *msgBroadcastService = TEXT("SQUEAK_SERVICE_BROADCAST_MESSAGE"); /* The name of the broadcast message */ @@ -244,17 +243,17 @@ void UninstallExceptionHandler(void) #if __MINGW32__ # include static FILE * -fopen_for_append(char *filename) +fopen_for_append(WCHAR *filename) { - FILE *f = !access(filename, F_OK) /* access is bass ackwards */ - ? fopen(filename,"r+") - : fopen(filename,"w+"); + FILE *f = !_waccess(filename, F_OK) /* access is bass ackwards */ + ? _wfopen(filename,L"r+") + : _wfopen(filename,L"w+"); if (f) fseek(f,0,SEEK_END); return f; } #else -# define fopen_for_append(filename) fopen(filename,"a+t") +# define fopen_for_append(filename) _wfopen(filename,L"a+t") #endif /****************************************************************************/ @@ -273,6 +272,19 @@ OutputLogMessage(char *string) return 1; } +static int +OutputLogMessageW(WCHAR *string) +{ + int len = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); + char *utf8 = malloc(len); + if (!utf8) return 1; + WideCharToMultiByte(CP_UTF8, 0, string, -1, utf8, len, NULL, NULL); + OutputLogMessage(utf8); + free(utf8); + return 1; +} + + static int OutputConsoleString(char *string) { @@ -1156,7 +1168,7 @@ error(char *msg) { SetCurrentDirectoryW(vmLogDirW); #endif /* print the above information */ - f = fopen_for_append("crash.dmp"); + f = fopen_for_append(L"crash.dmp"); if(f){ time_t crashTime = time(NULL); fprintf(f,"---------------------------------------------------------------------\n"); @@ -1263,7 +1275,7 @@ printCrashDebugInformation(LPEXCEPTION_POINTERS exp) SetCurrentDirectoryW(vmLogDirW); #endif /* print the above information */ - f = fopen_for_append("crash.dmp"); + f = fopen_for_append(L"crash.dmp"); if(f){ time_t crashTime = time(NULL); fprintf(f,"---------------------------------------------------------------------\n"); @@ -1564,7 +1576,7 @@ sqMain(int argc, char *argv[]) svcStart = time(NULL); OutputLogMessage("\n\n"); OutputLogMessage(ctime(&svcStart)); - OutputLogMessage(serviceName); + OutputLogMessageW(serviceName); OutputLogMessage(" started with the following command line\n"); OutputLogMessage(initialCmdLine); OutputLogMessage("\n"); @@ -1579,7 +1591,7 @@ sqMain(int argc, char *argv[]) #ifndef NO_SERVICE /* if service installing is requested, do so */ if(installServiceName && *installServiceName) { - strcpy(serviceName, installServiceName); + wcscpy(serviceName, installServiceName); sqServiceInstall(); /* When installing was successful we won't come to this point. Otherwise ... */ @@ -1818,19 +1830,27 @@ parseVMArgument(int argc, char *argv[]) /* parameters */ else if (argc > 1 && !strcmp(argv[0], VMOPTION("service"))) { - installServiceName = argv[1]; + int len = MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, installServiceName, 0); + installServiceName = malloc(len * sizeof(WCHAR)); /* note: we will never free installServiceName - it's a global for lifetime of the VM */ + MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, installServiceName, len); return 2; } else if (!strncmp(argv[0], VMOPTION("service:"), strlen(VMOPTION("service:")))) { - installServiceName = argv[0] + strlen(VMOPTION("service:")); + int len = MultiByteToWideChar(CP_UTF8, 0, argv[0] + strlen(VMOPTION("log:")), -1, installServiceName, 0); + logName = malloc(len * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, argv[0] + strlen(VMOPTION("log:")), -1, installServiceName, len); return 1; } else if (argc > 1 && !strcmp(argv[0], VMOPTION("log"))) { - logName = argv[1]; + int len=MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, logName, 0); + logName = malloc(len * sizeof(WCHAR)); /* note: we will never free logName - it's a global for lifetime of the VM */ + MultiByteToWideChar(CP_UTF8, 0, argv[1], -1, logName, len); return 2; } else if (!strncmp(argv[0], VMOPTION("log:"), strlen(VMOPTION("log:")))) { - logName = argv[0] + strlen(VMOPTION("log:")); + int len = MultiByteToWideChar(CP_UTF8, 0, argv[0] + strlen(VMOPTION("log:")), -1, logName, 0); + logName = malloc(len * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, argv[0] + strlen(VMOPTION("log:")), -1, logName, len); return 1; } else if (argc > 1 && !strcmp(argv[0], VMOPTION("memory"))) { diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index e734bf51a8..3b8cc14eb2 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -22,8 +22,8 @@ /* Imports from sqWin32Main.c */ /****************************************************************/ -extern TCHAR *logName; /* full path and name to log file */ -extern TCHAR serviceName[]; /* The name of the NT service */ +extern WCHAR *logName; /* full path and name to log file */ +extern WCHAR serviceName[]; /* The name of the NT service */ HANDLE hServDoneEvent = NULL; @@ -32,12 +32,12 @@ SERVICE_STATUS ssStatus; /* current status of the service */ SERVICE_STATUS_HANDLE sshStatusHandle; DWORD dwGlobalErr; -void sqStopService(LPTSTR lpszMsg); +void sqStopService(LPWSTR lpszMsg); void sqEventLogMessage(LPTSTR lpszMsg); BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwCheckPoint,DWORD dwWaitHint); void sqServiceControl(DWORD dwCtrlCode); -void sqServiceMainFunction(DWORD dwArgc, LPTSTR *lpszArgv); +void sqServiceMainFunction(DWORD dwArgc, LPWSTR *lpszArgv); #define failOn(condition, msg)\ if(condition)\ @@ -46,65 +46,49 @@ void sqServiceMainFunction(DWORD dwArgc, LPTSTR *lpszArgv); return 0;\ } -/* printCommandLine(): Return a command line string from the current settings */ +/* printCommandLine(): Return a command line string from the current settings, UTF8 encoded */ -TCHAR *printCommandLine(int printFor95) -{ static TCHAR buffer[1024]; - TCHAR lbuf[50]; +char *printCommandLine() +{ static WCHAR buffer[1024]; + static char utf8[1024*3]; /* remember: at most 3 byte char for 1 WCHAR - See comment of MAX_PATH_UTF8 */ + WCHAR lbuf[50]; *buffer = 0; - if(printFor95) - { - GetModuleFileName(hInstance, buffer, 1024); - lstrcat(buffer,TEXT(" -service95 -headless ")); - } - if(dwMemorySize) /* need -memory: mb */ { - lstrcat(buffer,TEXT("-memory: ")); -#ifdef UNICODE - lstrcat(buffer, _ltow(dwMemorySize, lbuf, 10)); -#else - lstrcat(buffer, _ltoa(dwMemorySize, lbuf, 10)); -#endif - lstrcat(buffer,TEXT(" ")); + wcsncat(buffer,L"-memory: ",1024); + wcsncat(buffer, _ltow(dwMemorySize, lbuf, 10),1024); + wcsncat(buffer,L" ",1024); } #if STACKVM { extern sqInt desiredNumStackPages; extern sqInt desiredEdenBytes; if (desiredEdenBytes) { - lstrcat(buffer,TEXT("-eden: ")); -# ifdef UNICODE - lstrcat(buffer, _ltow(desiredEdenBytes, lbuf, 10)); -# else - lstrcat(buffer, _ltoa(desiredEdenBytes, lbuf, 10)); -# endif - lstrcat(buffer,TEXT(" ")); + wcsncat(buffer,L"-eden: ",1024); + wcsncat(buffer, _ltow(desiredEdenBytes, lbuf, 10),1024); + wcsncat(buffer,L" ",1024); } if (desiredNumStackPages) { - lstrcat(buffer,TEXT("-stackpages: ")); -# ifdef UNICODE - lstrcat(buffer, _ltow(desiredNumStackPages, lbuf, 10)); -# else - lstrcat(buffer, _ltoa(desiredNumStackPages, lbuf, 10)); -# endif - lstrcat(buffer,TEXT(" ")); + wcsncat(buffer,L"-stackpages: ",1024); + wcsncat(buffer, _ltow(desiredNumStackPages, lbuf, 10),1024); + wcsncat(buffer,L" ",1024); } } #endif /* STACKVM */ if(*logName) /* need -log: "logName" */ { - lstrcat(buffer, TEXT("-log: \"")); - lstrcat(buffer, logName); - lstrcat(buffer, TEXT("\" ")); + wcsncat(buffer, L"-log: \"",1024); + wcsncat(buffer, logName,1024); + wcsncat(buffer, L"\" ",1024); } /* add image name */ - lstrcat(buffer, TEXT("\"")); - lstrcat(buffer, imageNameT); - lstrcat(buffer, TEXT("\"\0")); + wcsncat(buffer, L"\"",1024); + wcsncat(buffer, imageNameW,1024); + wcsncat(buffer, L"\"\0",1024); - return buffer; + WideCharToMultiByte(CP_UTF8, 0, buffer, -1, utf8, 1024 * 3, NULL, NULL); + return utf8; } /**************************************************************************** @@ -113,7 +97,7 @@ TCHAR *printCommandLine(int printFor95) /* sqStartService: start the named service */ int -sqStartService(LPCTSTR serviceName) +sqStartService(LPCWSTR serviceName) { SC_HANDLE schService; SC_HANDLE schSCManager; @@ -123,7 +107,7 @@ sqStartService(LPCTSTR serviceName) schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); failOn(!schSCManager, TEXT("OpenSCManager failed")); - schService = OpenService(schSCManager, serviceName, SERVICE_ALL_ACCESS); + schService = OpenServiceW(schSCManager, serviceName, SERVICE_ALL_ACCESS); if(schService) { /* This may take some time */ @@ -145,7 +129,7 @@ sqStartService(LPCTSTR serviceName) /* sqChangeServiceConfig: change the startup type of the named service */ int -sqChangeServiceConfig(LPCTSTR serviceName, DWORD startType) +sqChangeServiceConfig(LPCWSTR serviceName, DWORD startType) { SC_HANDLE schService; SC_HANDLE schSCManager; @@ -155,7 +139,7 @@ sqChangeServiceConfig(LPCTSTR serviceName, DWORD startType) schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); failOn(!schSCManager, TEXT("OpenSCManager failed")); - schService = OpenService(schSCManager, serviceName, SERVICE_ALL_ACCESS); + schService = OpenServiceW(schSCManager, serviceName, SERVICE_ALL_ACCESS); if (schService != NULL) { if(ChangeServiceConfig(schService, @@ -184,7 +168,7 @@ sqChangeServiceConfig(LPCTSTR serviceName, DWORD startType) /* sqRemoveService: remove the named service */ int -sqRemoveService(LPCTSTR serviceName) +sqRemoveService(LPCWSTR serviceName) { SC_HANDLE schService; SC_HANDLE schSCManager; @@ -194,7 +178,7 @@ sqRemoveService(LPCTSTR serviceName) schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); failOn(!schSCManager, TEXT("OpenSCManager failed")); - schService = OpenService(schSCManager, serviceName, SERVICE_ALL_ACCESS); + schService = OpenServiceW(schSCManager, serviceName, SERVICE_ALL_ACCESS); if (schService == NULL) { printLastError(TEXT("OpenService failed")); @@ -213,17 +197,17 @@ sqRemoveService(LPCTSTR serviceName) /* sqInstallService: install the named service */ int -sqInstallService(LPCTSTR serviceName, LPCTSTR serviceExe) +sqInstallService(LPCWSTR serviceName, LPCWSTR serviceExe) { - LPCTSTR lpszBinaryPathName = serviceExe; + LPCWSTR lpszBinaryPathName = serviceExe; SC_HANDLE schService; SC_HANDLE schSCManager; /* open service control manager on the local machine with default database */ - schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + schSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS); failOn(!schSCManager, TEXT("OpenSCManager failed")); - schService = CreateService( + schService = CreateServiceW( schSCManager, /* SCManager database */ serviceName, /* name of service */ serviceName, /* name to display */ @@ -245,7 +229,7 @@ sqInstallService(LPCTSTR serviceName, LPCTSTR serviceExe) if(GetLastError() == ERROR_SERVICE_EXISTS) { /* The service already exists - ask the user to remove it */ - if(MessageBox(0,TEXT("A service of this name already exists. Try to remove it?"), + if(MessageBoxW(0,L"A service of this name already exists. Try to remove it?", serviceName, MB_YESNOCANCEL) == IDYES) { /* Now close the service manager ... */ @@ -273,19 +257,19 @@ sqInstallService(LPCTSTR serviceName, LPCTSTR serviceExe) NT Service. Run it afterwards */ void sqServiceInstall(void) { HKEY hk; - TCHAR tmp[1024]; + WCHAR tmp[1024]; DWORD ok; /* get the VM name */ - GetModuleFileName(hInstance,tmp, 255); + GetModuleFileNameW(hInstance,tmp, 255); if(!sqInstallService(serviceName, tmp)) { warnPrintf(TEXT("The service has NOT been installed.")); return; } /* Create a new key for our service */ - wsprintf(tmp,TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Startup"),serviceName); - ok = RegCreateKey(HKEY_LOCAL_MACHINE, tmp, &hk); + _snwprintf(tmp,1024,L"SYSTEM\\CurrentControlSet\\Services\\%s\\Startup",serviceName); + ok = RegCreateKeyW(HKEY_LOCAL_MACHINE, tmp, &hk); if(ok != ERROR_SUCCESS) { printLastError(TEXT("RegCreateKey failed")); @@ -304,12 +288,12 @@ void sqServiceInstall(void) return; } /* Add the log file to the subkey. */ - ok = RegSetValueEx(hk, /* subkey handle */ - TEXT("Log"), /* value name */ - 0, /* must be zero */ - REG_EXPAND_SZ, /* value type */ - (LPBYTE) logName, /* address of value data */ - lstrlen(logName) + 1); /* length of value data */ + ok = RegSetValueEx(hk, /* subkey handle */ + TEXT("Log"), /* value name */ + 0, /* must be zero */ + REG_EXPAND_SZ, /* value type */ + (LPBYTE) logName, /* address of value data */ + wcslen(logName) + 1)*sizeof(WCHAR); /* length of value data */ if(ok != ERROR_SUCCESS) { printLastError(TEXT("RegSetValueEX failed")); @@ -329,15 +313,15 @@ void sqServiceInstall(void) } RegCloseKey(hk); /* Successfully finished install */ - if(MessageBox(0,TEXT("Service installation successful. ") - TEXT("Do you wish to start the service automatically on system startup?"), + if(MessageBoxW(0,L"Service installation successful. " + L"Do you wish to start the service automatically on system startup?", serviceName,MB_YESNOCANCEL) == IDYES) { if(!sqChangeServiceConfig(serviceName, SERVICE_AUTO_START)) warnPrintf(TEXT("The service was NOT configured.\n") TEXT("Please go to control panel and configure the service manually.\n")); } - if(MessageBox(0,TEXT("Do you wish to start the service right now?"), + if(MessageBoxW(0,L"Do you wish to start the service right now?", serviceName,MB_YESNOCANCEL) == IDYES) { if(!sqStartService(serviceName)) @@ -378,8 +362,7 @@ int sqServiceMain(void) DWORD WINAPI sqThreadMain(DWORD ignored) { DWORD dwSize, dwType, ok; HKEY hk; - static TCHAR tmpString[MAX_PATH+1]; - static TCHAR lbuf[50]; + static WCHAR tmpString[MAX_PATH+1]; char *cmd; /* first of all set a few flags */ @@ -388,11 +371,11 @@ DWORD WINAPI sqThreadMain(DWORD ignored) /* get values from the registry settings */ /* Create a new key for our service */ - wsprintf(tmpString,TEXT("SYSTEM\\CurrentControlSet\\Services\\%s\\Startup"),serviceName); - ok = RegOpenKey(HKEY_LOCAL_MACHINE, tmpString, &hk); + _snwprintf(tmpString,MAX_PATH+1,L"SYSTEM\\CurrentControlSet\\Services\\%s\\Startup",serviceName); + ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, tmpString, &hk); if(ok != ERROR_SUCCESS) { - sqStopService(TEXT("Failed to open registry for startup parameters")); + sqStopService(L"Failed to open registry for startup parameters"); TerminateThread(GetCurrentThread(), 0); } /* Read the image name from the subkey. */ @@ -400,34 +383,34 @@ DWORD WINAPI sqThreadMain(DWORD ignored) ok = RegQueryValueExW(hk,L"Image",NULL, &dwType, (LPBYTE) imageNameW, &dwSize); if(ok != ERROR_SUCCESS || dwType != REG_EXPAND_SZ) { - sqStopService(TEXT("Failed to read the image name from registry")); + sqStopService(L"Failed to read the image name from registry"); TerminateThread(GetCurrentThread(), 0); } - imageNameW[(dwSize+1)/2] = 0; + imageNameW[dwSize/2] = 0; WideCharToMultiByte(CP_UTF8, 0, imageNameW, -1, imageName, MAX_PATH_UTF8, NULL, NULL); /* Read the log file name from the subkey. */ - dwSize = MAX_PATH; - ok = RegQueryValueEx(hk,TEXT("Log"),NULL, &dwType, (LPBYTE) &tmpString, &dwSize); + dwSize = MAX_PATH*sizeof(WCHAR); + ok = RegQueryValueExW(hk,L"Log",NULL, &dwType, (LPBYTE) &tmpString, &dwSize); if(ok != ERROR_SUCCESS || dwType != REG_EXPAND_SZ) { - sqStopService(TEXT("Failed to read the log file name from registry")); + sqStopService(L"Failed to read the log file name from registry"); TerminateThread(GetCurrentThread(), 0); } - tmpString[dwSize] = 0; - logName = malloc(lstrlen(tmpString)+1); - lstrcpy(logName, tmpString); + tmpString[dwSize/2] = 0; + logName = malloc((wcslen(tmpString)+1)*sizeof(WCHAR)); + wcscpy(logName, tmpString); /* Read the memory size from the subkey. */ dwSize = sizeof(DWORD); - ok = RegQueryValueEx(hk,TEXT("Memory"),NULL, &dwType, (LPBYTE) &dwMemorySize, &dwSize); + ok = RegQueryValueExW(hk,L"Memory",NULL, &dwType, (LPBYTE) &dwMemorySize, &dwSize); if(ok != ERROR_SUCCESS || dwType != REG_DWORD) { - sqStopService(TEXT("Failed to read the memory amount from registry")); + sqStopService(L"Failed to read the memory amount from registry"); TerminateThread(GetCurrentThread(), 0); } RegCloseKey(hk); /* and away we go ... */ - cmd = printCommandLine(0); + cmd = printCommandLine(); sqMain(0,&cmd); return 1; } @@ -435,15 +418,15 @@ DWORD WINAPI sqThreadMain(DWORD ignored) /**************************************************************************** sqServiceMain(): This function will be called when the service is about to run ****************************************************************************/ -void sqServiceMainFunction(DWORD dwArgc, LPTSTR *lpszArgv) +void sqServiceMainFunction(DWORD dwArgc, LPWSTR *lpszArgv) { DWORD id; HANDLE eventList[2]; /* store the name of our service */ - lstrcpy(serviceName, lpszArgv[0]); + wcscpy(serviceName, lpszArgv[0]); /* register our service control handler: */ - sshStatusHandle = RegisterServiceCtrlHandler( serviceName, + sshStatusHandle = RegisterServiceCtrlHandlerW( serviceName, (LPHANDLER_FUNCTION) sqServiceControl); if (!sshStatusHandle) goto cleanup; @@ -592,31 +575,31 @@ ReportStatusToSCMgr(DWORD dwCurrentState, if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) { /* If an error occurs, stop the service. */ - sqStopService(TEXT("SetServiceStatus")); + sqStopService(L"SetServiceStatus"); } return fResult; } /* sqStopService: report an error, and stop the service. */ -void sqStopService(LPTSTR lpszMsg) +void sqStopService(LPWSTR lpszMsg) { - TCHAR chMsg[256]; + WCHAR chMsg[256]; HANDLE hEventSource; - LPTSTR lpszStrings[2]; + LPWSTR lpszStrings[2]; dwGlobalErr = GetLastError(); /* Use event logging to log the error. */ - hEventSource = RegisterEventSource(NULL, serviceName); + hEventSource = RegisterEventSourceW(NULL, serviceName); - wsprintf(chMsg, TEXT("%s error: %d"), serviceName, dwGlobalErr); + _snwprintf(chMsg, 256, L"%s error: %d", serviceName, dwGlobalErr); lpszStrings[0] = chMsg; lpszStrings[1] = lpszMsg; if (hEventSource != NULL) { - ReportEvent(hEventSource, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 2, 0, (LPCTSTR*)lpszStrings, NULL); + ReportEventW(hEventSource, EVENTLOG_ERROR_TYPE, 0, 0, NULL, 2, 0, (LPCWSTR*)lpszStrings, NULL); DeregisterEventSource(hEventSource); } @@ -631,7 +614,7 @@ void sqEventLogMessage(LPTSTR lpszMsg) HANDLE hEventSource; LPTSTR lpszStrings[1]; - hEventSource = RegisterEventSource(NULL, serviceName); + hEventSource = RegisterEventSourceW(NULL, serviceName); lpszStrings[0] = lpszMsg; if (hEventSource != NULL) { From e5cd4fb0538cdd090e7ceb4078edc261cb5099a0 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Tue, 1 Jan 2019 15:38:28 +0100 Subject: [PATCH 31/36] Fix 3 potential buffer overrun --- platforms/win32/vm/sqWin32ExternalPrims.c | 2 +- platforms/win32/vm/sqWin32Utils.c | 2 +- platforms/win32/vm/sqWin32Window.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platforms/win32/vm/sqWin32ExternalPrims.c b/platforms/win32/vm/sqWin32ExternalPrims.c index 86999c3711..8653472393 100644 --- a/platforms/win32/vm/sqWin32ExternalPrims.c +++ b/platforms/win32/vm/sqWin32ExternalPrims.c @@ -43,7 +43,7 @@ void *ioLoadModule(char *pluginName) #ifdef UNICODE int len = MultiByteToWideChar(CP_UTF8, 0, pluginName, -1, NULL, 0); if (len <= 0) return 0; /* invalid UTF8 ? */ - name = alloca(len); + name = alloca(len*sizeof(WCHAR)); if (MultiByteToWideChar(CP_UTF8, 0, pluginName, -1, name, len) == 0) return 0; #else name = pluginName; diff --git a/platforms/win32/vm/sqWin32Utils.c b/platforms/win32/vm/sqWin32Utils.c index 3a7ce61f12..0ca78bc604 100644 --- a/platforms/win32/vm/sqWin32Utils.c +++ b/platforms/win32/vm/sqWin32Utils.c @@ -26,7 +26,7 @@ int __cdecl sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const TCHAR* f buf = (TCHAR*) calloc(sizeof(TCHAR), 4096); va_start(args, fmt); - _vsntprintf(buf, 4096, fmt, args); + _vsntprintf(buf, 4096-1, fmt, args); va_end(args); result = MessageBox(stWindow,buf,titleString,dwFlags|MB_SETFOREGROUND); diff --git a/platforms/win32/vm/sqWin32Window.c b/platforms/win32/vm/sqWin32Window.c index 66502deb91..941d4d8048 100644 --- a/platforms/win32/vm/sqWin32Window.c +++ b/platforms/win32/vm/sqWin32Window.c @@ -72,7 +72,7 @@ char vmPathA[MAX_PATH_UTF8 + 1]; /* full path to interpreter's directory */ WCHAR vmPathW[MAX_PATH + 1]; /* full path to interpreter's directory */ char vmNameA[MAX_PATH_UTF8 + 1]; /* name of the interpreter's executable UTF8 */ WCHAR vmNameW[MAX_PATH + 1]; /* name of the interpreter's executable UTF16 */ -char windowTitle[MAX_PATH]; /* what should we display in the title? */ +char windowTitle[MAX_PATH+1]; /* what should we display in the title? */ TCHAR squeakIniName[MAX_PATH+1]; /* full path and name to ini file */ TCHAR windowClassName[MAX_PATH+1]; /* Window class name */ From 4ded3182b34a2cc2ac3ec1299590a71515a07972 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Tue, 1 Jan 2019 16:21:32 +0100 Subject: [PATCH 32/36] Fix my own confusion about wcsncat wcsncat does not care about the write limit! (buffer overrun). It only specifies the maximum number of characters to read from source... This way, we pay one more Shlemiel the painter run, and write hyper convoluted code. Nice! --- platforms/win32/vm/sqWin32Main.c | 2 +- platforms/win32/vm/sqWin32Service.c | 30 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 93e84fe66a..3ab56ec315 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -556,7 +556,7 @@ void gatherSystemInfo(void) { char model[MAX_PATH_UTF8]; GetSystemDirectoryW(iniName, MAX_PATH); - wcsncat(iniName,L"\\OEMINFO.INI",MAX_PATH); + wcsncat(iniName,L"\\OEMINFO.INI",MAX_PATH-1-wcslen(iniName)); GetPrivateProfileStringW(L"General", L"Manufacturer", L"Unknown", bufferW, MAX_PATH, iniName); diff --git a/platforms/win32/vm/sqWin32Service.c b/platforms/win32/vm/sqWin32Service.c index 3b8cc14eb2..80e22f9e9e 100644 --- a/platforms/win32/vm/sqWin32Service.c +++ b/platforms/win32/vm/sqWin32Service.c @@ -57,35 +57,35 @@ char *printCommandLine() if(dwMemorySize) /* need -memory: mb */ { - wcsncat(buffer,L"-memory: ",1024); - wcsncat(buffer, _ltow(dwMemorySize, lbuf, 10),1024); - wcsncat(buffer,L" ",1024); + wcsncat(buffer,L"-memory: ",1024 - 1 - wcslen(buffer)); + wcsncat(buffer, _ltow(dwMemorySize, lbuf, 10),1024 - 1 - wcslen(buffer)); + wcsncat(buffer,L" ",1024 - 1 - wcslen(buffer)); } #if STACKVM { extern sqInt desiredNumStackPages; extern sqInt desiredEdenBytes; if (desiredEdenBytes) { - wcsncat(buffer,L"-eden: ",1024); - wcsncat(buffer, _ltow(desiredEdenBytes, lbuf, 10),1024); - wcsncat(buffer,L" ",1024); + wcsncat(buffer,L"-eden: ",1024 - 1 - wcslen(buffer)); + wcsncat(buffer, _ltow(desiredEdenBytes, lbuf, 10),1024 - 1 - wcslen(buffer)); + wcsncat(buffer,L" ",1024 - 1 - wcslen(buffer)); } if (desiredNumStackPages) { - wcsncat(buffer,L"-stackpages: ",1024); - wcsncat(buffer, _ltow(desiredNumStackPages, lbuf, 10),1024); - wcsncat(buffer,L" ",1024); + wcsncat(buffer,L"-stackpages: ",1024 - 1 - wcslen(buffer)); + wcsncat(buffer, _ltow(desiredNumStackPages, lbuf, 10),1024 - 1 - wcslen(buffer)); + wcsncat(buffer,L" ",1024 - 1 - wcslen(buffer)); } } #endif /* STACKVM */ if(*logName) /* need -log: "logName" */ { - wcsncat(buffer, L"-log: \"",1024); - wcsncat(buffer, logName,1024); - wcsncat(buffer, L"\" ",1024); + wcsncat(buffer, L"-log: \"",1024 - 1 - wcslen(buffer)); + wcsncat(buffer, logName,1024 - 1 - wcslen(buffer)); + wcsncat(buffer, L"\" ",1024 - 1 - wcslen(buffer)); } /* add image name */ - wcsncat(buffer, L"\"",1024); - wcsncat(buffer, imageNameW,1024); - wcsncat(buffer, L"\"\0",1024); + wcsncat(buffer, L"\"",1024 - 1 - wcslen(buffer)); + wcsncat(buffer, imageNameW,1024 - 1 - wcslen(buffer)); + wcsncat(buffer, L"\"\0",1024 - 1 - wcslen(buffer)); WideCharToMultiByte(CP_UTF8, 0, buffer, -1, utf8, 1024 * 3, NULL, NULL); return utf8; From af19ed7e15bed953ddbc3d3ab4295683e34d0ab6 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Tue, 1 Jan 2019 16:55:31 +0100 Subject: [PATCH 33/36] Fix: Shlemiel the painter needs to paint strncat too Like wcsncat, the ugly and correct usage is strncat( dest, src, sizeof(dest) - 1 - strlen(dest) ); --- platforms/win32/vm/sqWin32Main.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 3ab56ec315..600bd056e1 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -771,7 +771,7 @@ void gatherSystemInfo(void) { NULL, NULL, (LPBYTE)drivers, &dwSize); } if (!ok) { - strncat(tmpString,"\nDriver Versions:",sizeof(tmpString)); + strncat(tmpString,"\nDriver Versions:",sizeof(tmpString) - 1 - strlen(tmpString)); /* InstalledDrivers is REG_MULTI_SZ (extra terminating zero) */ for(drv = drivers; drv[0]; drv +=wcslen(drv)) { DWORD verSize, hh; @@ -781,9 +781,9 @@ void gatherSystemInfo(void) { /* Concat driver name */ if (WideCharToMultiByte(CP_UTF8,0,drv,-1,drvA,MAX_PATH_UTF8,NULL,NULL)>0) { - strncat(tmpString, "\n\t", sizeof(tmpString)); - strncat(tmpString, drvA , sizeof(tmpString)); - strncat(tmpString, ": " , sizeof(tmpString)); + strncat(tmpString, "\n\t", sizeof(tmpString) - 1 - strlen(tmpString)); + strncat(tmpString, drvA , sizeof(tmpString) - 1 - strlen(tmpString)); + strncat(tmpString, ": " , sizeof(tmpString) - 1 - strlen(tmpString)); } verSize = GetFileVersionInfoSizeW(drv, &hh); @@ -799,7 +799,7 @@ void gatherSystemInfo(void) { char *utf8 = (char *)malloc(utf8_len); if(utf8) { WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, utf8, utf8_len, NULL, NULL); - strncat(tmpString, utf8,sizeof(tmpString)); + strncat(tmpString, utf8,sizeof(tmpString) - 1 - strlen(tmpString)); free(utf8); goto done; } @@ -812,13 +812,13 @@ void gatherSystemInfo(void) { char *utf8 = (char *)malloc(utf8_len); if (utf8) { WideCharToMultiByte(CP_UTF8, 0, vInfo, -1, utf8, utf8_len, NULL, NULL); - strncat(tmpString, utf8, sizeof(tmpString)); + strncat(tmpString, utf8, sizeof(tmpString) - 1 - strlen(tmpString)); free(utf8); goto done; } } - strncat(tmpString, "???", sizeof(tmpString)); + strncat(tmpString, "???", sizeof(tmpString) - 1 - strlen(tmpString)); done: if (verInfo) { @@ -826,7 +826,7 @@ void gatherSystemInfo(void) { verInfo = NULL; } } - strncat(tmpString,"\n",sizeof(tmpString)); + strncat(tmpString,"\n",sizeof(tmpString) - 1 - strlen(tmpString)); } RegCloseKey(hk); } From bf3840c320cf6cd488d83b1ebf7d0ea124b6ad87 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Tue, 1 Jan 2019 21:00:23 +0100 Subject: [PATCH 34/36] Fix a TCHAR*crashInfo trying to mix a char*msg Here, i don't want to redefine error() to take a TCHAR* So the simplest alternative is to switch to MessageBoxA But vmLogDirA may contain UNICODE, and UTF8 encoded character may be mangled in the MessageBox. The right way is to switch to W variant unconditionnally, and interpret msg as UTF8... --- platforms/win32/vm/sqWin32Main.c | 85 +++++++++++++++++++------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 600bd056e1..4b836b8a5c 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -1134,34 +1134,47 @@ static int inError = 0; void error(char *msg) { FILE *f; - TCHAR crashInfo[1024]; + WCHAR crashInfo[1024]; void *callstack[MAXFRAMES]; symbolic_pc symbolic_pcs[MAXFRAMES]; int nframes; int inVMThread = ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread()); + int len; + WCHAR *msgW; if (inError) exit(-2); inError = 1; - nframes = backtrace(callstack, MAXFRAMES); - symbolic_backtrace(++nframes, callstack, symbolic_pcs); - - wsprintf(crashInfo, - TEXT("Sorry but the VM has crashed.\n\n") - TEXT("Reason: %s\n\n") - TEXT("Current byte code: %d\n") - TEXT("Primitive index: %d\n\n") - TEXT("This information will be stored in the file\n") - TEXT("%s\\%s\n") - TEXT("with a complete stack dump"), - msg, - getCurrentBytecode(), - methodPrimitiveIndex(), - vmLogDirA, - TEXT("crash.dmp")); + nframes = backtrace(callstack, MAXFRAMES); + symbolic_backtrace(++nframes, callstack, symbolic_pcs); + + len = MultiByteToWideChar(CP_UTF8, 0, msg, -1, NULL, 0); + if (len > 0) { + msgW = alloca(len * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, msg, -1, msgW, len); + } + else { + msgW = alloca(4 * sizeof(WCHAR)); + wcscpy(msgW, L"???"); + } +#define __UNICODE_TEXT(x) L##x +#define _UNICODE_TEXT(x) __UNICODE_TEXT(x) + _snwprintf(crashInfo,1024, + L"Sorry but the VM has crashed.\n\n" + L"Reason: %s\n\n" + L"Current byte code: %d\n" + L"Primitive index: %" _UNICODE_TEXT(PRIdSQINT) L"\n\n" + L"This information will be stored in the file\n" + L"%s\\%s\n" + L"with a complete stack dump", + msgW, + getCurrentBytecode(), + methodPrimitiveIndex(), + vmLogDirW, + L"crash.dmp"); if(!fHeadlessImage) - MessageBox(stWindow,crashInfo,TEXT("Fatal VM error"), + MessageBoxW(stWindow,crashInfo,L"Fatal VM error", MB_OK | MB_APPLMODAL | MB_ICONSTOP); #if !NewspeakVM @@ -1223,7 +1236,7 @@ printCrashDebugInformation(LPEXCEPTION_POINTERS exp) void *callstack[MAXFRAMES]; symbolic_pc symbolic_pcs[MAXFRAMES]; int nframes, inVMThread; - TCHAR crashInfo[1024]; + WCHAR crashInfo[1024]; FILE *f; int byteCode = -2; @@ -1250,25 +1263,29 @@ printCrashDebugInformation(LPEXCEPTION_POINTERS exp) callstack+1, MAXFRAMES-1); symbolic_backtrace(++nframes, callstack, symbolic_pcs); - wsprintf(crashInfo, - TEXT("Sorry but the VM has crashed.\n\n") - TEXT("Exception code: %08X\n") - TEXT("Exception address: %08X\n") - TEXT("Current byte code: %d\n") - TEXT("Primitive index: %d\n\n") - TEXT("Crashed in %s thread\n\n") - TEXT("This information will be stored in the file\n") - TEXT("%s\\%s\n") - TEXT("with a complete stack dump"), + _snwprintf(crashInfo,1024, + L"Sorry but the VM has crashed.\n\n" + L"Exception code: %08x\n" +#ifdef _WIN64 + L"Exception address: %016" _UNICODE_TEXT(PRIxSQPTR) L"\n" +#else + L"Exception address: %08" _UNICODE_TEXT(PRIxSQPTR) L"\n" +#endif + L"Current byte code: %d\n" + L"Primitive index: %" _UNICODE_TEXT(PRIdSQINT) L"\n\n" + L"Crashed in %s thread\n\n" + L"This information will be stored in the file\n" + L"%s\\%s\n" + L"with a complete stack dump", exp->ExceptionRecord->ExceptionCode, - exp->ExceptionRecord->ExceptionAddress, + (sqIntptr_t) (exp->ExceptionRecord->ExceptionAddress), byteCode, methodPrimitiveIndex(), - inVMThread ? "the VM" : "some other", - vmLogDirA, - TEXT("crash.dmp")); + inVMThread ? L"the VM" : L"some other", + vmLogDirW, + L"crash.dmp"); if(!fHeadlessImage) - MessageBox(stWindow,crashInfo,TEXT("Fatal VM error"), + MessageBoxW(stWindow,crashInfo,L"Fatal VM error", MB_OK | MB_APPLMODAL | MB_ICONSTOP); #if !NewspeakVM From 252e2a84a250faa99dbacae6c439ac132b8ca9e7 Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Wed, 2 Jan 2019 11:24:32 +0100 Subject: [PATCH 35/36] Fix another potential Buffer overrun in sqWin32MIDI.c The joy of 0-based indices... [skip travis] --- platforms/win32/plugins/MIDIPlugin/sqWin32MIDI.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/plugins/MIDIPlugin/sqWin32MIDI.c b/platforms/win32/plugins/MIDIPlugin/sqWin32MIDI.c index 0366d070e9..3bceb4e7d2 100644 --- a/platforms/win32/plugins/MIDIPlugin/sqWin32MIDI.c +++ b/platforms/win32/plugins/MIDIPlugin/sqWin32MIDI.c @@ -605,7 +605,7 @@ int finishSysExCommand(sqMidiPort *port, char *bufferPtr, int count) /* Return the port with the given number or NULL */ static sqMidiPort* GetPort(int portNum) { - if(portNum > MAX_DEVICES || portNum < 0) + if(portNum >= MAX_DEVICES || portNum < 0) { success(false); return NULL; From 3e51616a56a0ef49b7c67a580db0f0fbaaea588f Mon Sep 17 00:00:00 2001 From: Nicolas Cellier Date: Wed, 2 Jan 2019 14:58:35 +0100 Subject: [PATCH 36/36] fixup: skip only 18 WCHAR if keyName begins with \\registry\\machine Note that keyName was declared char * in the original code. But now it is WCHAR*, so keyName+18 already does the right thing (skip 18 char) while modified code would now skip 36! Note that original code was using memcpy for this case of overlapping memory which is BAD and eventually UB. [skip travis] --- platforms/win32/vm/sqWin32Main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platforms/win32/vm/sqWin32Main.c b/platforms/win32/vm/sqWin32Main.c index 4b836b8a5c..51b91b2e9c 100644 --- a/platforms/win32/vm/sqWin32Main.c +++ b/platforms/win32/vm/sqWin32Main.c @@ -732,7 +732,7 @@ void gatherSystemInfo(void) { I have no idea why but for now I'll just truncate that part if we recognize it... */ if (_wcsnicmp(keyName, L"\\registry\\machine\\", 18) == 0) { - memmove(keyName, keyName+18*sizeof(WCHAR), (wcslen(keyName)-17)*sizeof(WCHAR)); + memmove(keyName, keyName+18, (wcslen(keyName)+1-18)*sizeof(WCHAR)); /* +1 for terminating NULL */ } ok = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hk);