Windows/MSVC: Fix wide char commandline handling (#840)

Windows programs (subsystem:windows, not Windows console programs)
compiled with Visual Studio need a WinMain entry point. The commandline
arguments handled by this function are now properly converted to UTF-8
before the standard main() entry point is called.

This applies only to Visual Studio! Note that some build systems like
MinGW and/or MSYS2 may still have issues with some Unicode (non-ASCII)
commandline arguments.
This commit is contained in:
Albrecht Schlosser 2023-11-19 19:08:53 +01:00
parent a72eff7588
commit 7e8994c4a2
3 changed files with 60 additions and 65 deletions

View File

@ -578,9 +578,9 @@ list (APPEND SHARED_FILES ${HEADER_FILES} ${DRIVER_HEADER_FILES})
set (STATIC_FILES ${SHARED_FILES}) set (STATIC_FILES ${SHARED_FILES})
if (WIN32) if (MSVC)
list (APPEND STATIC_FILES fl_call_main.c) list (APPEND STATIC_FILES fl_call_main.c)
endif (WIN32) endif (MSVC)
####################################################################### #######################################################################

View File

@ -244,7 +244,8 @@ IMGCPPFILES = \
Fl_SVG_Image.cxx \ Fl_SVG_Image.cxx \
drivers/SVG/Fl_SVG_File_Surface.cxx drivers/SVG/Fl_SVG_File_Surface.cxx
CFILES = fl_call_main.c flstring.c numericsort.c vsnprintf.c CFILES = flstring.c numericsort.c vsnprintf.c
CFILES_WIN = fl_call_main.c
UTF8CFILES = \ UTF8CFILES = \
xutf8/case.c \ xutf8/case.c \
@ -431,7 +432,7 @@ EXTRA_OBJECTS_WAYLANDX11 = $(EXTRA_OBJECTS_WAYLAND)
EXTRA_CXXFLAGS_WAYLAND = -I. EXTRA_CXXFLAGS_WAYLAND = -I.
EXTRA_CXXFLAGS_WAYLANDX11 = $(EXTRA_CXXFLAGS_WAYLAND) EXTRA_CXXFLAGS_WAYLANDX11 = $(EXTRA_CXXFLAGS_WAYLAND)
CFILES_WIN = $(GDICFILES) CFILES_WIN += $(GDICFILES)
CFILES += $(CFILES_$(BUILD)) CFILES += $(CFILES_$(BUILD))
CXXFLAGS += $(EXTRA_CXXFLAGS_$(BUILD)) CXXFLAGS += $(EXTRA_CXXFLAGS_$(BUILD))

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 1998-2018 by Bill Spitzak and others. * Copyright 1998-2023 by Bill Spitzak and others.
* *
* fl_call_main() calls main() for you Windows people. Needs to be done in C * fl_call_main() calls main() for you Windows people. Needs to be done in C
* because Borland C++ won't let you call main() from C++. * because Borland C++ won't let you call main() from C++.
@ -35,44 +35,34 @@
* Microsoft(r) Windows(r) that allows for it. * Microsoft(r) Windows(r) that allows for it.
*/ */
#if defined(_WIN32) && !defined(FL_DLL) && !defined (__GNUC__) /*
* This file is compiled only on Windows platforms (since FLTK 1.4.0).
* Therefore we don't need to test the _WIN32 macro anymore.
* The _MSC_VER macro is tested to compile it only for Visual Studio
* platforms because GNU platforms (MinGW, MSYS) don't need it.
*/
#if defined(_MSC_VER) && !defined(FL_DLL)
# include <windows.h> #include <FL/fl_utf8.h>
# include <stdio.h> #include <FL/fl_string_functions.h>
# include <stdlib.h>
# include <FL/fl_utf8.h> #include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <shellapi.h>
extern int main(int, char *[]); extern int main(int, char *[]);
# ifdef BORLAND5
# define __argc _argc
# define __argv _argv
# endif /* BORLAND5 */
/* static int mbcs2utf(const char *s, int l, char *dst, unsigned dstlen) */
static int mbcs2utf(const char *s, int l, char *dst)
{
static wchar_t *mbwbuf;
unsigned dstlen = 0;
if (!s) return 0;
dstlen = (l * 6) + 6;
mbwbuf = (wchar_t*)malloc(dstlen * sizeof(wchar_t));
l = (int) mbstowcs(mbwbuf, s, l);
/* l = fl_unicode2utf(mbwbuf, l, dst); */
l = fl_utf8fromwc(dst, dstlen, mbwbuf, l);
dst[l] = 0;
free(mbwbuf);
return l;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) { LPSTR lpCmdLine, int nCmdShow) {
int rc, i; int rc;
char **ar; int i;
int argc;
char** argv;
char strbuf[2048];
# ifdef _DEBUG
/* /*
* If we are using compiling in debug mode, open a console window so * If we are compiling in debug mode, open a console window so
* we can see any printf's, etc... * we can see any printf's, etc...
* *
* While we can detect if the program was run from the command-line - * While we can detect if the program was run from the command-line -
@ -81,48 +71,52 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
* applications in the background anyways... * applications in the background anyways...
*/ */
#ifdef _DEBUG
AllocConsole(); AllocConsole();
freopen("conin$", "r", stdin); freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout); freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr); freopen("conout$", "w", stderr);
# endif /* _DEBUG */ #endif /* _DEBUG */
ar = (char**) malloc(sizeof(char*) * (__argc + 1)); /* Convert the command line arguments to UTF-8 */
i = 0; LPWSTR *wideArgv = CommandLineToArgvW(GetCommandLineW(), &argc);
while (i < __argc) { argv = malloc(argc * sizeof(void *));
int l; for (i = 0; i < argc; i++) {
unsigned dstlen; int ret = WideCharToMultiByte(CP_UTF8, /* CodePage */
if (__wargv ) { 0, /* dwFlags */
for (l = 0; __wargv[i] && __wargv[i][l]; l++) {}; /* is this just wstrlen??? */ wideArgv[i], /* lpWideCharStr */
dstlen = (l * 5) + 1; -1, /* cchWideChar */
ar[i] = (char*) malloc(dstlen); strbuf, /* lpMultiByteStr */
/* ar[i][fl_unicode2utf(__wargv[i], l, ar[i])] = 0; */ sizeof(strbuf), /* cbMultiByte */
dstlen = fl_utf8fromwc(ar[i], dstlen, __wargv[i], l); NULL, /* lpDefaultChar */
ar[i][dstlen] = 0; NULL); /* lpUsedDefaultChar */
} else { argv[i] = fl_strdup(strbuf);
for (l = 0; __argv[i] && __argv[i][l]; l++) {};
dstlen = (l * 5) + 1;
ar[i] = (char*) malloc(dstlen);
/* ar[i][mbcs2utf(__argv[i], l, ar[i], dstlen)] = 0; */
ar[i][mbcs2utf(__argv[i], l, ar[i])] = 0;
}
i++;
} }
ar[__argc] = 0;
/* Run the standard main entry point function... */
rc = main(__argc, ar);
# ifdef _DEBUG /* Free the wide character string array */
LocalFree(wideArgv);
/* Call the program's entry point main() */
rc = main(argc, argv);
/* Cleanup allocated memory for argv */
for (int i = 0; i < argc; ++i) {
free((void *)argv[i]);
}
free((void *)argv);
/* Close the console in debug mode */
#ifdef _DEBUG
fclose(stdin); fclose(stdin);
fclose(stdout); fclose(stdout);
fclose(stderr); fclose(stderr);
# endif /* _DEBUG */ #endif /* _DEBUG */
return rc; return rc;
} }
#else #else
/* STR# 2973: solves "empty translation unit" error (Sun, HP-UX..) */ /* STR# 2973: solves "empty translation unit" error */
typedef int dummy; typedef int dummy;
#endif /* _WIN32 && !FL_DLL && !__GNUC__ */ #endif /* defined(_MSC_VER) && !defined(FL_DLL) */