From fdfb295cc4882b1641b5f50254d45e91c47bc628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz=20M=C3=A1s?= Date: Fri, 11 Feb 2022 10:16:59 +0100 Subject: [PATCH] windows: Trick to get UTF-8 encoded argv in main entry point - Adapt exifprint to the new wmain strategy - Delete have_unicode_path - wmain does not work with MSYS & MinGW - cmake: entry point via cmake instead of pragma - cmake: better doc for MSVC flags - Fix entry point in sample apps - Adapt CMake code to work with MSVC & MinGW --- CMakeLists.txt | 2 +- cmake/compilerFlags.cmake | 12 +++--------- samples/CMakeLists.txt | 15 ++++++++++++--- samples/exifprint.cpp | 34 ++++++++------------------------- src/CMakeLists.txt | 17 +++++++++++++++++ src/exiv2.cpp | 2 ++ src/version.cpp | 2 -- src/wmain.c | 40 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 83 insertions(+), 41 deletions(-) create mode 100644 src/wmain.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c4afc99..573268ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ project(exiv2 # use TWEAK to categorize the build # 1.00.0.20 = RC2 Preview # 1.00.0.29 = RC2 Development DESCRIPTION "Exif/IPTC/Xmp C++ metadata library and tools plus ICC Profiles, Previews and more." - LANGUAGES CXX + LANGUAGES C CXX ) if(NOT CMAKE_BUILD_TYPE) diff --git a/cmake/compilerFlags.cmake b/cmake/compilerFlags.cmake index 4d145116..dc82f2bc 100644 --- a/cmake/compilerFlags.cmake +++ b/cmake/compilerFlags.cmake @@ -167,13 +167,7 @@ if(MSVC) string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endif () - # Object Level Parallelism - add_compile_options(/MP) - add_definitions(-DNOMINMAX) # This definition is not only needed for Exiv2 but also for xmpsdk - - # https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ - if (MSVC_VERSION GREATER_EQUAL "1910") # VS2017 and up - add_compile_options("/Zc:__cplusplus") - endif() - + add_compile_options(/MP) # Object Level Parallelism + add_compile_options(/utf-8) # Set source and execution character sets to UTF-8 + add_definitions(-DNOMINMAX) # This definition is not only needed for Exiv2 but also for xmpsdk endif() diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 91635849..a2c335f6 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -114,11 +114,20 @@ list(APPEND APPLICATIONS remotetest) # ****************************************************************************** foreach(application ${APPLICATIONS}) target_link_libraries(${application} PRIVATE exiv2lib) + if(MSVC) + target_link_libraries(${application} PRIVATE wmain) + target_link_options(${application} PRIVATE "/ENTRY:wWinMainCRTStartup") + endif() + if (MINGW) + target_link_libraries(${application} PRIVATE wmain) + target_compile_options(${application} PRIVATE -municode) + target_link_options(${application} PRIVATE -municode) + endif() if( EXIV2_ENABLE_PNG ) target_link_libraries(${application} PRIVATE ${ZLIB_LIBRARIES} ) - if (MSVC) - set_target_properties(${application} PROPERTIES LINK_FLAGS "/ignore:4099") # Ignore missing PDBs - endif() + if (MSVC) + set_target_properties(${application} PROPERTIES LINK_FLAGS "/ignore:4099") # Ignore missing PDBs + endif() endif() endforeach() diff --git a/samples/exifprint.cpp b/samples/exifprint.cpp index 0b8daf89..1a7e8f67 100644 --- a/samples/exifprint.cpp +++ b/samples/exifprint.cpp @@ -25,25 +25,6 @@ #include #include -// https://github.com/Exiv2/exiv2/issues/468 -#if defined(EXV_UNICODE_PATH) && defined(__MINGW__) -#undef EXV_UNICODE_PATH -#endif - -#ifdef EXV_UNICODE_PATH -#define _tchar wchar_t -#define _tstrcmp wcscmp -#define _t(s) L##s -#define _tcout wcout -#define _tmain wmain -#else -#define _tchar char -#define _tstrcmp strcmp -#define _t(s) s -#define _tcout cout -#define _tmain main -#endif - // copied from src/tiffvisitor_int.cpp static const Exiv2::TagInfo* findTag(const Exiv2::TagInfo* pList,uint16_t tag) { @@ -52,31 +33,32 @@ static const Exiv2::TagInfo* findTag(const Exiv2::TagInfo* pList,uint16_t tag) } -int _tmain(int argc, _tchar* const argv[]) +int main(int argc, char* const argv[]) try { + setlocale(LC_CTYPE, ".utf8"); Exiv2::XmpParser::initialize(); ::atexit(Exiv2::XmpParser::terminate); #ifdef EXV_ENABLE_BMFF Exiv2::enableBMFF(); #endif - const _tchar* prog = argv[0]; + const char* prog = argv[0]; if (argc == 1) { - std::_tcout << _t("Usage: ") << prog << _t(" [ [--lint] path | --version | --version-test ]") << std::endl; + std::cout << "Usage: " << prog << " [ [--lint] path | --version | --version-test ]" << std::endl; return 1; } int rc = 0 ; - const _tchar* file = argv[1]; - bool bLint = _tstrcmp(file,_t("--lint")) == 0 && argc == 3; + const char* file = argv[1]; + bool bLint = strcmp(file, "--lint") == 0 && argc == 3; if ( bLint ) file= argv[2]; - if ( _tstrcmp(file,_t("--version")) == 0 ) { + if ( strcmp(file,"--version") == 0 ) { std::vector keys; Exiv2::dumpLibraryInfo(std::cout,keys); return rc; - } else if ( _tstrcmp(file,_t("--version-test")) == 0 ) { + } else if ( strcmp(file,"--version-test") == 0 ) { // verifies/test macro EXIV2_TEST_VERSION // described in include/exiv2/version.hpp std::cout << "EXV_PACKAGE_VERSION " << EXV_PACKAGE_VERSION << std::endl diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2216dcf6..b871acc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -287,6 +287,23 @@ if(EXIV2_BUILD_EXIV2_COMMAND) target_include_directories(exiv2 PRIVATE ${Intl_INCLUDE_DIRS}) endif() + if(MSVC OR MINGW) + # Trick to get properly UTF-8 encoded argv + # More info at: https://github.com/huangqinjin/wmain + add_library(wmain STATIC wmain.c) + target_link_libraries(exiv2 PRIVATE wmain) + endif() + + if (MSVC) + target_link_options(wmain INTERFACE /WHOLEARCHIVE:$) + target_link_options(exiv2 PRIVATE "/ENTRY:wWinMainCRTStartup") + endif() + + if (MINGW) + target_compile_options(exiv2 PRIVATE -municode) + target_link_options(exiv2 PRIVATE -municode) + endif() + if (USING_CONAN AND WIN32 AND EXISTS ${PROJECT_BINARY_DIR}/conanDlls) # In case of using conan recipes with their 'shared' option turned on, we will have dlls of # the 3rd party dependencies in the conanDlls folder. diff --git a/src/exiv2.cpp b/src/exiv2.cpp index c96e8bc5..fe97449d 100644 --- a/src/exiv2.cpp +++ b/src/exiv2.cpp @@ -120,6 +120,8 @@ namespace { // Main int main(int argc, char* const argv[]) { + setlocale(LC_CTYPE, ".utf8"); + Exiv2::XmpParser::initialize(); ::atexit(Exiv2::XmpParser::terminate); #ifdef EXV_ENABLE_BMFF diff --git a/src/version.cpp b/src/version.cpp index ad7280f5..d1982caf 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -332,7 +332,6 @@ void Exiv2::dumpLibraryInfo(std::ostream& os,const std::vector& keys int have_strings =0; int have_sys_types =0; int have_unistd =0; - int have_unicode_path=0; int enable_bmff =0; int enable_webready =0; @@ -512,7 +511,6 @@ void Exiv2::dumpLibraryInfo(std::ostream& os,const std::vector& keys output(os,keys,"have_strings" ,have_strings ); output(os,keys,"have_sys_types" ,have_sys_types ); output(os,keys,"have_unistd" ,have_unistd ); - output(os,keys,"have_unicode_path" ,have_unicode_path); output(os,keys,"enable_bmff" ,enable_bmff ); output(os,keys,"enable_webready" ,enable_webready ); output(os,keys,"enable_nls" ,enable_nls ); diff --git a/src/wmain.c b/src/wmain.c new file mode 100644 index 00000000..d2d90b25 --- /dev/null +++ b/src/wmain.c @@ -0,0 +1,40 @@ +#include + +extern int __cdecl main(); + +int wmain(int argc, wchar_t* argv[]) +{ + char** args; + int nbytes = sizeof(char*) * (argc + 1); + HANDLE heap = GetProcessHeap(); + + for (int i = 0; i < argc; ++i) + nbytes += WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL); + + args = HeapAlloc(heap, 0, nbytes); + args[0] = (char*)(args + argc + 1); + + for (int i = 0; i < argc; ++i) + args[i+1] = args[i] + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, args[i], nbytes, NULL, NULL); + + args[argc] = NULL; + + argc = main(argc, args); + HeapFree(heap, 0, args); + return argc; +} + +int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow) +{ + (void) hInstance; + (void) hPrevInstance; + (void) lpCmdLine; + (void) nCmdShow; + + int argc; + wchar_t** argv; + argv = CommandLineToArgvW(GetCommandLineW(), &argc); + argc = wmain(argc, argv); + LocalFree(argv); + return argc; +}