comphelper/source/misc/lok.cxx | 24 ++ desktop/qa/desktop_lib/test_desktop_lib.cxx | 3 desktop/source/lib/init.cxx | 11 include/LibreOfficeKit/LibreOfficeKit.h | 4 include/LibreOfficeKit/LibreOfficeKit.hxx | 8 include/LibreOfficeKit/LibreOfficeKitTypes.h | 6 include/comphelper/lok.hxx | 4 include/tools/UnixWrappers.h | 310 +++++++++++++++++++++++++++ sfx2/source/doc/guisaveas.cxx | 21 - 9 files changed, 378 insertions(+), 13 deletions(-)
New commits: commit 6112dac3ab1d60097255e227f475aaff3d49d2a0 Author: Miklos Vajna <[email protected]> AuthorDate: Tue Dec 9 14:17:42 2025 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Dec 11 15:49:35 2025 +0100 cool#13770 lok: add a file save dialog callback Commit f4b9d6f024b27c4d8ddaabfe0eaa0b404137e3cf (Add horrible hack to ask the filename when exporting PDF in CODA-W, 2025-12-02) added a callback from core to the LOK client to trigger a file picker during PDF export. The problem is that in its current form, this callback is Windows-specific so other platforms (Linux, macOS) can't provide their implementation in a straightforward way. Fix the problem by reworkig this, so it works similar to the anyInput callback, which is cross-platform already. The behavior for the case when the new callback is not registered is meant to be unchanged. Change-Id: I27934774889fc827772b2665799eca358dd62e55 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195381 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins diff --git a/comphelper/source/misc/lok.cxx b/comphelper/source/misc/lok.cxx index c8330f992c3a..2ddf430c521c 100644 --- a/comphelper/source/misc/lok.cxx +++ b/comphelper/source/misc/lok.cxx @@ -14,6 +14,9 @@ #include <osl/process.h> #include <i18nlangtag/languagetag.hxx> #include <sal/log.hxx> +#ifdef _WIN32 +#include <tools/UnixWrappers.h> +#endif #include <iostream> @@ -51,6 +54,8 @@ static std::function<bool(void*, int)> g_pAnyInputCallback; static void* g_pAnyInputCallbackData; static std::function<int()> g_pMostUrgentPriorityGetter; +static std::function<void(const char*, char*, size_t)> g_pFileSaveDialogCallback; + static std::function<void(int)> g_pViewSetter; static std::function<int()> g_pViewGetter; @@ -367,6 +372,25 @@ bool anyInput() return bRet; } +void setFileSaveDialogCallback(const std::function<void(const char*, char*, size_t)>& pFileSaveDialogCallback) +{ + g_pFileSaveDialogCallback = pFileSaveDialogCallback; +} + +bool fileSaveDialog(const OUString& rSuggested, OUString& rResult) +{ + if (!g_pFileSaveDialogCallback) + { + return false; + } + + OString aSuggested = rSuggested.toUtf8(); + char aResult[PATH_MAX]; + g_pFileSaveDialogCallback(aSuggested.getStr(), aResult, PATH_MAX); + rResult = OUString::fromUtf8(aResult); + return true; +} + void setViewSetter(const std::function<void(int)>& pViewSetter) { g_pViewSetter = pViewSetter; diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index d1cd353b712a..dc52bcc39edd 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -4239,10 +4239,11 @@ void DesktopLOKTest::testABI() CPPUNIT_ASSERT_EQUAL(classOffset(23), offsetof(struct _LibreOfficeKitClass, extractDocumentStructureRequest)); CPPUNIT_ASSERT_EQUAL(classOffset(24), offsetof(struct _LibreOfficeKitClass, registerAnyInputCallback)); CPPUNIT_ASSERT_EQUAL(classOffset(25), offsetof(struct _LibreOfficeKitClass, getDocsCount)); + CPPUNIT_ASSERT_EQUAL(classOffset(26), offsetof(struct _LibreOfficeKitClass, registerFileSaveDialogCallback)); // When extending LibreOfficeKit with a new function pointer, add new assert for the offsetof the // new function pointer and bump this assert for the size of the class. - CPPUNIT_ASSERT_EQUAL(classOffset(26), sizeof(struct _LibreOfficeKitClass)); + CPPUNIT_ASSERT_EQUAL(classOffset(27), sizeof(struct _LibreOfficeKitClass)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct _LibreOfficeKitDocumentClass, destroy)); CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct _LibreOfficeKitDocumentClass, saveAs)); diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index ca25346f9b2a..24624b26ac9d 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -2742,6 +2742,9 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* pThis, LibreOfficeKitAnyInputCallback pAnyInputCallback, void* pData); +static void lo_registerFileSaveDialogCallback(LibreOfficeKit* pThis, + LibreOfficeKitFileSaveDialogCallback pFileSaveDialogCallback); + static void lo_sendDialogEvent(LibreOfficeKit* pThis, unsigned long long int nLOKWindowId, const char* pArguments); @@ -2791,6 +2794,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl() m_pOfficeClass->setForkedChild = lo_setForkedChild; m_pOfficeClass->extractDocumentStructureRequest = lo_extractDocumentStructureRequest; m_pOfficeClass->registerAnyInputCallback = lo_registerAnyInputCallback; + m_pOfficeClass->registerFileSaveDialogCallback = lo_registerFileSaveDialogCallback; m_pOfficeClass->getDocsCount = lo_getDocsCount; gOfficeClass = m_pOfficeClass; @@ -7932,6 +7936,13 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* /*pThis*/, }); } +static void lo_registerFileSaveDialogCallback(LibreOfficeKit* /*pThis*/, + LibreOfficeKitFileSaveDialogCallback pFileSaveDialogCallback) +{ + SolarMutexGuard aGuard; + comphelper::LibreOfficeKit::setFileSaveDialogCallback(pFileSaveDialogCallback); +} + static int lo_getDocsCount(LibreOfficeKit* /*pThis*/) { SolarMutexGuard aGuard; diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h index 7860adf3c66b..e49cdf174892 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.h +++ b/include/LibreOfficeKit/LibreOfficeKit.h @@ -165,6 +165,10 @@ struct _LibreOfficeKitClass /// @see lok::Office::getDocsCount(). int (*getDocsCount) (LibreOfficeKit* pThis); + + /// @see lok::Office::registerFileSaveDialogCallback() + void (*registerFileSaveDialogCallback)(LibreOfficeKit* pThis, + LibreOfficeKitFileSaveDialogCallback pCallback); }; #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize) diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx index b6cdd929cb95..fae2b0de7c84 100644 --- a/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -1316,6 +1316,14 @@ public: { return mpThis->pClass->getDocsCount(mpThis); } + + /** + * Registers a callback that can display an interactive file save dialog. + */ + void registerFileSaveDialogCallback(LibreOfficeKitFileSaveDialogCallback pCallback) + { + return mpThis->pClass->registerFileSaveDialogCallback(mpThis, pCallback); + } }; /// Factory method to create a lok::Office instance. diff --git a/include/LibreOfficeKit/LibreOfficeKitTypes.h b/include/LibreOfficeKit/LibreOfficeKitTypes.h index 939766567607..167b4f65ca8d 100644 --- a/include/LibreOfficeKit/LibreOfficeKitTypes.h +++ b/include/LibreOfficeKit/LibreOfficeKitTypes.h @@ -10,6 +10,8 @@ #ifndef INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H #define INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H +#include <stddef.h> + #ifdef __cplusplus extern "C" { #endif @@ -28,6 +30,10 @@ typedef void (*LibreOfficeKitWakeCallback)(void* pData); /// @see lok::Office::registerAnyInputCallback() typedef bool (*LibreOfficeKitAnyInputCallback)(void* pData, int nMostUrgentPriority); +/// @see lok::Office::registerFileSaveDialogCallback() +typedef void (*LibreOfficeKitFileSaveDialogCallback)(const char* pSuggestedUri, char* pResultUri, + size_t nResultUri); + #ifdef __cplusplus } #endif diff --git a/include/comphelper/lok.hxx b/include/comphelper/lok.hxx index 9d28f839c9c0..ebdb8439ba0e 100644 --- a/include/comphelper/lok.hxx +++ b/include/comphelper/lok.hxx @@ -150,6 +150,10 @@ setAnyInputCallback(const std::function<bool(void*, int)>& pAnyInputCallback, vo const std::function<int()>& pMostUrgentPriorityGetter); COMPHELPER_DLLPUBLIC bool anyInput(); +COMPHELPER_DLLPUBLIC void setFileSaveDialogCallback( + const std::function<void(const char*, char*, size_t)>& pFileSaveDialogCallback); +COMPHELPER_DLLPUBLIC bool fileSaveDialog(const OUString& rSuggested, OUString& rResult); + // These allow setting callbacks, so that set/get of a LOK view is possible even in code that is // below sfx2. COMPHELPER_DLLPUBLIC void setViewSetter(const std::function<void(int)>& pViewSetter); diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx index a12ecb01acc7..4ef76c1b278e 100644 --- a/sfx2/source/doc/guisaveas.cxx +++ b/sfx2/source/doc/guisaveas.cxx @@ -1718,25 +1718,18 @@ bool SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons { OUString aFileName; aFileNameIter->second >>= aFileName; -#ifdef _WIN32 if (comphelper::LibreOfficeKit::isActive()) { - // FIXME: Horrible hack. In CODA-W, we didn't actually - // display any dialog yet, so call into a function in - // CODA.cpp. - typedef void (*ofd_t)(const std::wstring& suggestedURI, std::string& result); - // Use mangled name to catch changes in parameters... - ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), "?output_file_dialog_from_core@@YAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z"); - if (ofd != NULL) + // In the LOK case, we didn't actually display any dialog yet, so invoke a callback if + // that's set. + OUString aNewURI; + if (comphelper::LibreOfficeKit::fileSaveDialog(aFileName, aNewURI)) { - std::string newURI; - (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI); - if (newURI == "") + if (aNewURI.isEmpty()) return false; - aFileName = OUString::fromUtf8(newURI.c_str()); + aFileName = aNewURI; } } -#endif aURL.SetURL( aFileName ); DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal URL!" ); commit ddcfcd67afe12d080019654efc7ad8db938d2c9a Author: Tor Lillqvist <[email protected]> AuthorDate: Thu Feb 27 23:47:55 2025 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Dec 11 15:49:28 2025 +0100 Add wrappers to use Unix-style file APIs on Windows Of course, LibreOffice has tons of APIs from before whose whole point is to hide system dependencies behind a cross-platform API. All the "sal" and "osl" stuff, for starters. The wrappers in this file is not intended to replace those. The point with these wrappers is to quickly port code that was written for Unix *only* to Windows, with minimal changes to the source code. Basically you just need to prefix calls to functions like open() and stat() with wrap_. In Windows, "file descriptors" are a thing in the C library, not the operating system. But the C library does provide the same core set of functionality as Unix-like systems do. The functions use almost the same names, except that for some reason, to avoid compilation warnings, you need to call variants with names preceded by an underscore, like _close(). Another Windows complication is that the file system is based on UTF-16 names. You can't just pass file names as eight-bit characters to _open() and expect it to work for all file names. Instead, you need to call a function with a "w" prefix, like _wopen(), that takes a wide character string (UTF-16). This header is in C, not C++, because we want it to be usable from some external libraries written in C that we compile as part of LibreOffice in some circumstances, particularly for Windows. All functions behave like the wrapped ones, set errno on errors. Change-Id: Ia093ccf976659ceabbef6e0071da73d4df0b63b7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195422 Tested-by: Jenkins Reviewed-by: Miklos Vajna <[email protected]> diff --git a/include/tools/UnixWrappers.h b/include/tools/UnixWrappers.h new file mode 100644 index 000000000000..70d560ff0907 --- /dev/null +++ b/include/tools/UnixWrappers.h @@ -0,0 +1,310 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* Wrappers to use Unix-style file APIs on Windows. + * + * Of course, LibreOffice has tons of APIs from before whose whole point is to hide system + * dependencies behind a cross-platform API. All the "sal" and "osl" stuff, for starters. The + * wrappers in this file is not intended to replace those. The point with these wrappers is to + * quickly port code that was written for Unix *only* to Windows, with minimal changes to the source + * code. Basically you just need to prefix calls to functions like open() and stat() with wrap_. + * + * In Windows, "file descriptors" are a thing in the C library, not the operating system. But the C + * library does provide the same core set of functionality as Unix-like systems do. The functions + * use almost the same names, except that for some reason, to avoid compilation warnings, you need + * to call variants with names preceded by an underscore, like _close(). + + * Another Windows complication is that the file system is based on UTF-16 names. You can't just + * pass file names as eight-bit characters to _open() and expect it to work for all file + * names. Instead, you need to call a function with a "w" prefix, like _wopen(), that takes a wide + * character string (UTF-16). + + * This header is in C, not C++, because we want it to be usable from some external libraries + * written in C that we compile as part of LibreOffice in some circumstances, particularly for + * Windows. + * + * All functions behave like the wrapped ones, set errno on errors. + */ + +#include <wchar.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/stat.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#include <io.h> +#include <string.h> +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +/* Undo the mapping in <Windows.h> of CreateFont => CreateFontW etc, as vcl also uses these + * identifiers. + */ +#undef CreateFont +#undef GetGlyphOutline +#else +#include <unistd.h> +#include <sys/mman.h> +#endif + +#ifdef _WIN32 + +#define MAP_FAILED ((void*)-1) + +#define S_ISDIR(m) (((m)&_S_IFMT) == _S_IFDIR) + +#define F_OK 00 +#define W_OK 02 +#define R_OK 04 +#define X_OK R_OK + +#define PATH_MAX MAX_PATH + +#endif + +#ifdef _WIN32 +[[maybe_unused]] static wchar_t* string_to_wide_string(const char* string) +{ + const int len = strlen(string); + if (len == 0) + { + return _wcsdup(L""); + } + + const int wlen = MultiByteToWideChar(CP_UTF8, 0, string, len, NULL, 0); + if (wlen <= 0) + { + return NULL; + } + + wchar_t* result = (wchar_t*)malloc(wlen * 2 + 2); + MultiByteToWideChar(CP_UTF8, 0, string, len, result, wlen); + result[wlen] = L' + + return result; +} + +#endif + +/* Pass in UTF-8 filename */ +[[maybe_unused]] static int wrap_open(const char* path, int flags, int mode) +{ +#ifdef _WIN32 + wchar_t* wpath = string_to_wide_string(path); + int result = _wopen(wpath, flags, mode); + free(wpath); + return result; +#else + return open(path, flags, mode); +#endif +} + +#if 0 + +/* Pass in UTF-16 filename */ +[[maybe_unused]] static int wrap_wopen(const wchar_t* path, int flags, int mode) +{ +#ifdef _WIN32 + return _wopen(path, flags, mode); +#else + rtl_String* str; + int len = wcslen(path); + bool success = rtl_convertUStringToString(&str, path, len, RTL_TEXTENCODING_UTF8, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR); + int result; + if (success) + { + char* spath = malloc(len + 1); + strcat(spath, str, len); + spath[len] = 0; + result = open(str->buffer, flags, path); + free(spath); + } + else + { + result = -1; + errno = EIO; + } + rtl_string_release(str); + return result; +#endif +} + +#endif + +[[maybe_unused]] static int wrap_read(int fd, void* buf, int nbytes) +{ +#ifdef _WIN32 + return _read(fd, buf, nbytes); +#else + return read(fd, buf, nbytes); +#endif +} + +[[maybe_unused]] static int wrap_write(int fd, const void* buf, int nbytes) +{ +#ifdef _WIN32 + return _write(fd, buf, nbytes); +#else + return write(fd, buf, nbytes); +#endif +} + +[[maybe_unused]] static int wrap_fstat(int fd, struct stat* st) +{ +#ifdef _WIN32 + /* Sadly just "struct stat" in the Microsoft C library means a legacy one with 32-bit size, + * 32-bit time one. But it is a *different* type than struct _stat32, even if identical. + */ + struct _stat32 st32; + int result = _fstat32(fd, &st32); + if (result != -1) + { + st->st_dev = st32.st_dev; + st->st_ino = st32.st_ino; + st->st_mode = st32.st_mode; + st->st_nlink = st32.st_nlink; + st->st_uid = st32.st_uid; + st->st_gid = st32.st_gid; + st->st_rdev = st32.st_rdev; + st->st_size = st32.st_size; + st->st_atime = st32.st_atime; + st->st_mtime = st32.st_mtime; + st->st_ctime = st32.st_ctime; + } + return result; +#else + return fstat(fd, st); +#endif +} + +[[maybe_unused]] static void* wrap_mmap(int64_t size, int fd, intptr_t* handle) +{ +#ifdef _WIN32 + *handle = (intptr_t)CreateFileMappingW((HANDLE)_get_osfhandle(fd), NULL, + SEC_COMMIT | PAGE_READONLY, 0, 0, NULL); + return MapViewOfFile((HANDLE)*handle, FILE_MAP_READ, 0, 0, size); +#else + /* No handle needs to be stored */ + (void)handle; + return mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); +#endif +} + +[[maybe_unused]] static int wrap_munmap(void* pointer, int64_t size, intptr_t handle) +{ +#ifdef _WIN32 + (void)size; + CloseHandle((HANDLE)handle); + if (!UnmapViewOfFile(pointer)) + { + errno = EIO; + return -1; + } + return 0; +#else + (void)handle; + return munmap(pointer, size); +#endif +} + +[[maybe_unused]] static int wrap_dup(int fd) +{ +#ifdef _WIN32 + return _dup(fd); +#else + return dup(fd); +#endif +} + +[[maybe_unused]] static int wrap_close(int fd) +{ +#ifdef _WIN32 + return _close(fd); +#else + return close(fd); +#endif +} + +/* This doesn't usea file descriptor but is related to the above functionality so keep here + * anyway. + */ + +[[maybe_unused]] static FILE* wrap_fopen(const char* path, const char* mode) +{ +#ifdef _WIN32 + wchar_t* wpath = string_to_wide_string(path); + wchar_t* wmode = string_to_wide_string(mode); + FILE* result = _wfopen(wpath, wmode); + free(wpath); + free(wmode); + return result; +#else + return fopen(path, mode); +#endif +} + +[[maybe_unused]] static int wrap_stat(const char* path, struct stat* st) +{ +#ifdef _WIN32 + struct _stat32 st32; + wchar_t* wpath = string_to_wide_string(path); + int result = _wstat32(wpath, &st32); + free(wpath); + if (result != -1) + { + st->st_dev = st32.st_dev; + st->st_ino = st32.st_ino; + st->st_mode = st32.st_mode; + st->st_nlink = st32.st_nlink; + st->st_uid = st32.st_uid; + st->st_gid = st32.st_gid; + st->st_rdev = st32.st_rdev; + st->st_size = st32.st_size; + st->st_atime = st32.st_atime; + st->st_mtime = st32.st_mtime; + st->st_ctime = st32.st_ctime; + } + return result; +#else + return stat(path, st); +#endif +} + +[[maybe_unused]] static int wrap_access(const char* path, int mode) +{ +#ifdef _WIN32 + wchar_t* wpath = string_to_wide_string(path); + int result = _waccess(wpath, mode); + free(wpath); + return result; +#else + return access(path, mode); +#endif +} + +[[maybe_unused]] static const char* wrap_realpath(const char* path, char* resolved_path) +{ +#ifdef _WIN32 + strcpy(resolved_path, path); + return resolved_path; +#else + return realpath(path, resolved_path); +#endif +} + +#ifdef __cplusplus +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ commit b0ecad5a3d83729aaf7c8a44983d20eedcbbdbff Author: Tor Lillqvist <[email protected]> AuthorDate: Tue Nov 4 20:40:34 2025 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Thu Dec 11 15:49:22 2025 +0100 Make the CODA-W PDF export file save dialog hack a bit more secure Look for the mangled ++ name, to catch a potential mismatch in function signature. Also, check that the returned function pointer is non-null before calling it. Change-Id: Ib2e0d1cd4b3e26081800b736b74e5972d0819736 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195380 Tested-by: Jenkins Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx index 1c9dd37343f7..a12ecb01acc7 100644 --- a/sfx2/source/doc/guisaveas.cxx +++ b/sfx2/source/doc/guisaveas.cxx @@ -1725,12 +1725,16 @@ bool SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons // display any dialog yet, so call into a function in // CODA.cpp. typedef void (*ofd_t)(const std::wstring& suggestedURI, std::string& result); - ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), "output_file_dialog_from_core"); - std::string newURI; - (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI); - if (newURI == "") - return false; - aFileName = OUString::fromUtf8(newURI.c_str()); + // Use mangled name to catch changes in parameters... + ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), "?output_file_dialog_from_core@@YAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z"); + if (ofd != NULL) + { + std::string newURI; + (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI); + if (newURI == "") + return false; + aFileName = OUString::fromUtf8(newURI.c_str()); + } } #endif
