[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.2' - Repository.mk shell/AllLangMoTarget_shell.mk shell/Executable_spsupp_helper.mk shell/inc shell/Module_shell.mk shell/source shell/WinResTarget_spsupp_dlg.mk

Mike Kaganski (via logerrit) logerrit at kemper.freedesktop.org
Thu Jun 20 05:10:35 UTC 2019


 Repository.mk                                  |    2 
 shell/AllLangMoTarget_shell.mk                 |   11 +
 shell/Executable_spsupp_helper.mk              |   37 ++++
 shell/Module_shell.mk                          |    6 
 shell/WinResTarget_spsupp_dlg.mk               |   14 +
 shell/inc/spsupp/spsuppServ.hpp                |    2 
 shell/inc/spsupp/spsuppStrings.hrc             |   23 ++
 shell/source/win32/spsupp/COMOpenDocuments.cxx |  113 ++++++------
 shell/source/win32/spsupp/res/spsuppDlg.h      |   27 ++
 shell/source/win32/spsupp/res/spsuppDlg.rc     |   29 +++
 shell/source/win32/spsupp/spsuppHelper.cxx     |  231 +++++++++++++++++++++++++
 shell/source/win32/spsupp/spsuppServ.cxx       |   17 -
 12 files changed, 453 insertions(+), 59 deletions(-)

New commits:
commit 9d241cb1723a165960ba14183fd8e3c580c71241
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Tue May 7 21:22:08 2019 +0300
Commit:     Mike Kaganski <mike.kaganski at collabora.com>
CommitDate: Thu Jun 20 07:09:54 2019 +0200

    SharePoint: Add dialog to choose opening in read-only or edit mode.
    
    ViewDocument3 method takes OpenType parameter, which allows to decide
    if user can choose opening the document read-only or to edit.
    
    Since we need to show localizable strings, we need to bootstrap part
    of LO (l10n), which is impossible when e.g. 64-bit ActiveX DLL tries
    to load installed 32-bit sal DLLs. Thus, we need a separate helper
    .exe, which architecture matches the rest of installed LO, and which
    handles user interaction.
    
    Change-Id: I0ad53ba64272fb84728d2221e3dc85d3eefdda68
    Reviewed-on: https://gerrit.libreoffice.org/72355
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    Tested-by: Mike Kaganski <mike.kaganski at collabora.com>
    (cherry picked from commit f60cc89ec35f8b1bf56e9f69ef15143fd002c409)
    Reviewed-on: https://gerrit.libreoffice.org/74407

diff --git a/Repository.mk b/Repository.mk
index ca1c359b1f64..396bdf585b3a 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -205,6 +205,7 @@ $(eval $(call gb_Helper_register_executables_for_install,OOO,ooo, \
 	) \
 	$(if $(filter WNT,$(OS)), \
 		senddoc \
+		spsupp_helper \
 	) \
 ))
 
@@ -1127,6 +1128,7 @@ $(eval $(call gb_Helper_register_mos,\
 	scc \
 	sd \
 	sfx \
+	shell \
 	sm \
 	svl \
 	svt \
diff --git a/shell/AllLangMoTarget_shell.mk b/shell/AllLangMoTarget_shell.mk
new file mode 100644
index 000000000000..41285974e3b7
--- /dev/null
+++ b/shell/AllLangMoTarget_shell.mk
@@ -0,0 +1,11 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+
+$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,shell))
+
+# vim: set noet sw=4 ts=4:
diff --git a/shell/Executable_spsupp_helper.mk b/shell/Executable_spsupp_helper.mk
new file mode 100644
index 000000000000..5daeff28da56
--- /dev/null
+++ b/shell/Executable_spsupp_helper.mk
@@ -0,0 +1,37 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,spsupp_helper))
+
+$(eval $(call gb_Executable_set_targettype_gui,spsupp_helper,YES))
+
+$(eval $(call gb_Executable_set_include,spsupp_helper,\
+    -I$(SRCDIR)/shell/inc/spsupp \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_Executable_use_sdk_api,spsupp_helper))
+
+$(eval $(call gb_Executable_use_libraries,spsupp_helper,\
+	i18nlangtag \
+	sal \
+	utl \
+))
+
+$(eval $(call gb_Executable_add_exception_objects,spsupp_helper,\
+    shell/source/win32/spsupp/spsuppHelper \
+))
+
+$(eval $(call gb_Executable_add_nativeres,spsupp_helper,spsupp_dlg))
+
+$(eval $(call gb_Executable_use_system_win32_libs,spsupp_helper,\
+    shell32 \
+))
+
+# vim:set noet sw=4 ts=4:
diff --git a/shell/Module_shell.mk b/shell/Module_shell.mk
index 6d54617f0268..b1bebc69beae 100644
--- a/shell/Module_shell.mk
+++ b/shell/Module_shell.mk
@@ -49,6 +49,7 @@ $(eval $(call gb_Module_add_targets,shell,\
 	Executable_senddoc \
 	Library_smplmail \
 	Library_wininetbe \
+	Executable_spsupp_helper \
 ))
 
 ifeq ($(COM),MSC)
@@ -63,6 +64,7 @@ $(eval $(call gb_Module_add_targets,shell,\
 	CustomTarget_spsupp_idl \
 	Library_spsupp \
 	WinResTarget_spsupp \
+	WinResTarget_spsupp_dlg \
 ))
 
 $(eval $(call gb_Module_add_check_targets,shell,\
@@ -116,4 +118,8 @@ endif
 
 endif
 
+$(eval $(call gb_Module_add_l10n_targets,shell,\
+    AllLangMoTarget_shell \
+))
+
 # vim: set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/shell/WinResTarget_spsupp_dlg.mk b/shell/WinResTarget_spsupp_dlg.mk
new file mode 100644
index 000000000000..9880433c7826
--- /dev/null
+++ b/shell/WinResTarget_spsupp_dlg.mk
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_WinResTarget_WinResTarget,spsupp_dlg))
+
+$(eval $(call gb_WinResTarget_set_rcfile,spsupp_dlg,shell/source/win32/spsupp/res/spsuppDlg))
+
+# vim: set shiftwidth=4 tabstop=4 noexpandtab:
diff --git a/shell/inc/spsupp/spsuppServ.hpp b/shell/inc/spsupp/spsuppServ.hpp
index e3c78dc3fa6b..7e4d2fc54cd9 100644
--- a/shell/inc/spsupp/spsuppServ.hpp
+++ b/shell/inc/spsupp/spsuppServ.hpp
@@ -13,7 +13,7 @@
 #include <objbase.h>
 
 ITypeLib* GetTypeLib();
-const wchar_t* GetLOPath();
+const wchar_t* GetHelperExe();
 
 #endif
 
diff --git a/shell/inc/spsupp/spsuppStrings.hrc b/shell/inc/spsupp/spsuppStrings.hrc
new file mode 100644
index 000000000000..dc96fbb89459
--- /dev/null
+++ b/shell/inc/spsupp/spsuppStrings.hrc
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+
+#ifndef INCLUDED_SHELL_INC_SPSUPP_STRINGS_HRC
+#define INCLUDED_SHELL_INC_SPSUPP_STRINGS_HRC
+
+#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
+
+#define RID_STR_SP_VIEW_OR_EDIT_TITLE   NC_("RID_STR_SP_VIEW_OR_EDIT_TITLE", "Open Document")
+#define RID_STR_SP_VIEW_OR_EDIT_MESSAGE NC_("RID_STR_SP_VIEW_OR_EDIT_MESSAGE", "You are opening document\n\n  %DOCNAME\n\nDo you want to open it to view or to edit?")
+#define RID_STR_SP_VIEW_OR_EDIT_VIEW    NC_("RID_STR_SP_VIEW_OR_EDIT_VIEW", "View")
+#define RID_STR_SP_VIEW_OR_EDIT_EDIT    NC_("RID_STR_SP_VIEW_OR_EDIT_EDIT", "Edit")
+#define RID_STR_SP_VIEW_OR_EDIT_CANCEL  NC_("RID_STR_SP_VIEW_OR_EDIT_CANCEL", "Cancel")
+
+#endif          // INCLUDED_SHELL_INC_SPSUPP_STRINGS_HRC
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/COMOpenDocuments.cxx b/shell/source/win32/spsupp/COMOpenDocuments.cxx
index 2e6b8cfad8ec..92ad732bfda0 100644
--- a/shell/source/win32/spsupp/COMOpenDocuments.cxx
+++ b/shell/source/win32/spsupp/COMOpenDocuments.cxx
@@ -10,6 +10,7 @@
 #include <sal/config.h>
 
 #include <cstring>
+#include <string>
 #include <vector>
 
 #include <COMOpenDocuments.hpp>
@@ -18,11 +19,12 @@
 
 namespace
 {
-
-// Returns S_OK if successful
-HRESULT LOStart(const wchar_t* sModeArg, const wchar_t* sFilePath)
+template<class... Args>
+HRESULT LOStart(Args... args)
 {
-    const wchar_t* sProgram = GetLOPath();
+    auto quote = [](const std::wstring& s) { return L"\"" + s + L"\""; };
+    std::wstring sCmdLine((quote(GetHelperExe()) + ... + (L" " + quote(args))));
+    LPWSTR pCmdLine = const_cast<LPWSTR>(sCmdLine.c_str());
 
     STARTUPINFOW si;
     std::memset(&si, 0, sizeof si);
@@ -30,39 +32,57 @@ HRESULT LOStart(const wchar_t* sModeArg, const wchar_t* sFilePath)
     si.dwFlags = STARTF_USESHOWWINDOW;
     si.wShowWindow = SW_SHOW;
     PROCESS_INFORMATION pi = {};
-    const size_t cchCommandLine = 32768;
-    wchar_t sCommandLine[cchCommandLine];
-    swprintf(sCommandLine, cchCommandLine, L"\"%s\" %s \"%s\"", sProgram, sModeArg, sFilePath);
-    if (CreateProcessW(nullptr, sCommandLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi) == FALSE)
-    {
-        DWORD dwError = GetLastError();
-        wchar_t* sMsgBuf = nullptr;
-        FormatMessageW(
-            FORMAT_MESSAGE_ALLOCATE_BUFFER |
-            FORMAT_MESSAGE_FROM_SYSTEM |
-            FORMAT_MESSAGE_IGNORE_INSERTS,
-            nullptr,
-            dwError,
-            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-            reinterpret_cast<LPWSTR>(&sMsgBuf),
-            0, nullptr);
-
-        size_t nBufSize = wcslen(sMsgBuf) + 100;
-        std::vector<wchar_t> sDisplayBuf(nBufSize);
-        swprintf(sDisplayBuf.data(), nBufSize, L"Could not start LibreOffice. Error is 0x%08X:\n\n%s", dwError, sMsgBuf);
-        HeapFree(GetProcessHeap(), 0, sMsgBuf);
-
-        // Report the error to user and return error
-        MessageBoxW(nullptr, sDisplayBuf.data(), nullptr, MB_ICONERROR);
-        return HRESULT_FROM_WIN32(dwError);
-    }
+    if (!CreateProcessW(nullptr, pCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
+        return HRESULT_FROM_WIN32(GetLastError());
+
+    WaitForSingleObject(pi.hProcess, INFINITE);
+    DWORD nExitCode;
+    const bool bGotExitCode = GetExitCodeProcess(pi.hProcess, &nExitCode);
+    const DWORD nGetExitCodeError = GetLastError();
+
     CloseHandle(pi.hProcess);
     CloseHandle(pi.hThread);
-    return S_OK;
+
+    if (!bGotExitCode)
+        return HRESULT_FROM_WIN32(nGetExitCodeError);
+    if (nExitCode == 0)
+        return S_OK;
+    if (nExitCode == 1)
+        return S_FALSE;
+    return E_FAIL;
 }
 
 VARIANT_BOOL toVBool(bool b) { return b ? VARIANT_TRUE : VARIANT_FALSE; }
 
+HRESULT ImplCreateNewDocument(IDispatch* /*pdisp*/, BSTR bstrTemplateLocation,
+                              BSTR bstrDefaultSaveLocation, VARIANT_BOOL* pbResult)
+{
+    HRESULT hr = LOStart(L"CreateNewDocument", bstrTemplateLocation, bstrDefaultSaveLocation);
+    *pbResult = toVBool(hr == S_OK);
+    return hr;
+}
+
+HRESULT ImplEditDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation,
+                         VARIANT_BOOL fUseLocalCopy, const VARIANT& varProgID,
+                         VARIANT_BOOL* pbResult)
+{
+    const wchar_t* sUseLocalCopy = (fUseLocalCopy == VARIANT_FALSE) ? L"0" : L"1";
+    const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L"";
+    HRESULT hr = LOStart(L"EditDocument", bstrDocumentLocation, sUseLocalCopy, sProgId);
+    *pbResult = toVBool(hr == S_OK);
+    return hr;
+}
+
+HRESULT ImplViewDocument(IDispatch* /*pdisp*/, BSTR bstrDocumentLocation, int OpenType,
+                         const VARIANT& varProgID, VARIANT_BOOL* pbResult)
+{
+    wchar_t sOpenType[16]{};
+    swprintf(sOpenType, L"%d", OpenType);
+    const wchar_t* sProgId = (varProgID.vt == VT_BSTR) ? varProgID.bstrVal : L"";
+    HRESULT hr = LOStart(L"ViewDocument", bstrDocumentLocation, sOpenType, sProgId);
+    *pbResult = toVBool(hr == S_OK);
+    return hr;
+}
 } // namespace
 
 long COMOpenDocuments::m_nObjCount = 0;
@@ -224,17 +244,15 @@ STDMETHODIMP COMOpenDocuments::EditDocument2(
 
 // Creates a document based on the specified document template and window object
 STDMETHODIMP COMOpenDocuments::CreateNewDocument2(
-    IDispatch* /*pdisp*/,             // An Object that represents the window from which the CreateNewDocument2 method is being activated
+    IDispatch* pdisp,                 // An Object that represents the window from which the CreateNewDocument2 method is being activated
     BSTR bstrTemplateLocation,        // A string that contains the URL of the document template from which the document is created, or the programmatic identifier (progID) of the application to invoke when creating the document
-    BSTR /*bstrDefaultSaveLocation*/, // A string that contains the path that specifies a suggested default location for saving the new document
+    BSTR bstrDefaultSaveLocation,     // A string that contains the path that specifies a suggested default location for saving the new document
     VARIANT_BOOL* pbResult)           // true if the document creation succeeds; otherwise false
 {
     if (!pbResult)
         return E_POINTER;
     // TODO: resolve the program from varProgID (nullptr -> default?)
-    HRESULT hr = LOStart(L"-n", bstrTemplateLocation);
-    *pbResult = toVBool(SUCCEEDED(hr));
-    return hr;
+    return ImplCreateNewDocument(pdisp, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult);
 }
 
 // Used with the OpenDocuments.CreateNewDocument2 method to determine
@@ -276,18 +294,15 @@ STDMETHODIMP COMOpenDocuments::PromptedOnLastOpen(
 // 3 When the document is not checked out and the document library requires that documents be checked out to be edited, the user can only read the document, or check it out and edit it
 // 4 When the current user has checked it out, the user can only edit the local copy of the document
 STDMETHODIMP COMOpenDocuments::ViewDocument3(
-    IDispatch* /*pdisp*/,      // An Object that represents the window from which the ViewDocument3 method is being activated
+    IDispatch* pdisp,          // An Object that represents the window from which the ViewDocument3 method is being activated
     BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
-    int /*OpenType*/,          // A Long integer that specifies the rights for opening the document
-    VARIANT /*varProgID*/,     // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used
+    int OpenType,              // A Long integer that specifies the rights for opening the document
+    VARIANT varProgID,         // An optional string that contains the ProgID of the application with which to open the document. If this argument is omitted, the default viewer for the document is used
     VARIANT_BOOL *pbResult)    // true if the document was successfully opened; otherwise false
 {
     if (!pbResult)
         return E_POINTER;
-    // TODO: resolve the program from varProgID (nullptr -> default?)
-    HRESULT hr = LOStart(L"--view", bstrDocumentLocation);
-    *pbResult = toVBool(SUCCEEDED(hr));
-    return hr;
+    return ImplViewDocument(pdisp, bstrDocumentLocation, OpenType, varProgID, pbResult);
 }
 
 // Checks in the specified document to a library
@@ -340,18 +355,16 @@ STDMETHODIMP COMOpenDocuments::CheckoutDocumentPrompt(
 // or with the specified editor based on the specified window object,
 // and specifies whether to use a local copy
 STDMETHODIMP COMOpenDocuments::EditDocument3(
-    IDispatch* /*pdisp*/,           // An Object that represents the window from which the EditDocument3 method is being activated
+    IDispatch* pdisp,               // An Object that represents the window from which the EditDocument3 method is being activated
     BSTR bstrDocumentLocation,      // A string that contains the URL of the document to open for editing
-    VARIANT_BOOL /*fUseLocalCopy*/, // true to use a local copy; otherwise false
-    VARIANT /*varProgID*/,          // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used
-    VARIANT_BOOL *pbResult)     // true if the document was successfully opened; otherwise false
+    VARIANT_BOOL fUseLocalCopy,     // true to use a local copy; otherwise false
+    VARIANT varProgID,              // An optional string that contains the ProgID of the application with which to edit the document. If this argument is omitted, the default editor for the document is used
+    VARIANT_BOOL *pbResult)         // true if the document was successfully opened; otherwise false
 {
     if (!pbResult)
         return E_POINTER;
     // TODO: resolve the program from varProgID (nullptr -> default?)
-    HRESULT hr = LOStart(L"-o", bstrDocumentLocation);
-    *pbResult = toVBool(SUCCEEDED(hr));
-    return hr;
+    return ImplEditDocument(pdisp, bstrDocumentLocation, fUseLocalCopy, varProgID, pbResult);
 }
 
 // Creates a new blog post in the editing application
diff --git a/shell/source/win32/spsupp/res/spsuppDlg.h b/shell/source/win32/spsupp/res/spsuppDlg.h
new file mode 100644
index 000000000000..f4b6ccb34410
--- /dev/null
+++ b/shell/source/win32/spsupp/res/spsuppDlg.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* 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/.
+*/
+
+#define IDD_EDIT_OR_RO 101
+#define IDC_STATIC -1
+#define ID_RO 1000
+#define ID_EDIT 1001
+#define IDC_EDIT_OR_RO 1002
+
+// Next default values for new objects
+
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1002
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/res/spsuppDlg.rc b/shell/source/win32/spsupp/res/spsuppDlg.rc
new file mode 100644
index 000000000000..a27974661f30
--- /dev/null
+++ b/shell/source/win32/spsupp/res/spsuppDlg.rc
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* 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/.
+*/
+
+#include "spsuppDlg.h"
+// We need to include windows.h to use IDI_QUESTION
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT
+
+// Dialog
+
+IDD_EDIT_OR_RO DIALOGEX 0, 0, 309, 87
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Open Document"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    ICON            IDI_QUESTION,IDC_STATIC,7,7,21,20
+    LTEXT           "Do you want to open the document to view or to edit?",IDC_EDIT_OR_RO,36,7,266,44
+    DEFPUSHBUTTON   "View",ID_RO,91,66,77,14
+    PUSHBUTTON      "Edit",ID_EDIT,171,66,77,14
+    PUSHBUTTON      "Cancel",IDCANCEL,252,66,50,14
+END
diff --git a/shell/source/win32/spsupp/spsuppHelper.cxx b/shell/source/win32/spsupp/spsuppHelper.cxx
new file mode 100644
index 000000000000..0c4834f960e0
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppHelper.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+* 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/.
+*/
+
+#include <prewin.h>
+#include <postwin.h>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+#include <spsuppStrings.hrc>
+#include <unotools/resmgr.hxx>
+#include "res/spsuppDlg.h"
+
+// Since we need to show localized messages to user before starting LibreOffice, we need to
+// bootstrap part of LO (l10n machinery). This implies loading some LO libraries, and since
+// there are ActiveX controls for both x86 and x64 for use in corresponding clients, they
+// can't both load the libraries that exist only for one architecture, like sal. Thus we need
+// a dedicated helper process, which is launched by ActiveX, and handle user interactions.
+
+namespace
+{
+const OUString& GetSofficeExe()
+{
+    static const OUString s_sPath = []() {
+        OUString result;
+        wchar_t sPath[MAX_PATH];
+        if (GetModuleFileNameW(nullptr, sPath, MAX_PATH) == 0)
+            return result;
+        wchar_t* pSlashPos = wcsrchr(sPath, L'\\');
+        if (pSlashPos == nullptr)
+            return result;
+        wcscpy(pSlashPos + 1, L"soffice.exe");
+        result = o3tl::toU(sPath);
+        return result;
+    }();
+    return s_sPath;
+}
+
+OUString GetString(const char* pResId)
+{
+    static const std::locale s_pLocale = [] {
+        // Initialize soffice bootstrap: see getIniFileName_Impl for reference
+        OUString sPath = GetSofficeExe();
+        if (const sal_Int32 nDotPos = sPath.lastIndexOf('.'); nDotPos >= 0)
+        {
+            sPath = sPath.replaceAt(nDotPos, sPath.getLength() - nDotPos, SAL_CONFIGFILE(""));
+            if (osl::FileBase::getFileURLFromSystemPath(sPath, sPath) == osl::FileBase::E_None)
+                rtl::Bootstrap::setIniFilename(sPath);
+        }
+        return Translate::Create("shell", LanguageTag("")); // Use system language
+    }();
+    return Translate::get(pResId, s_pLocale);
+}
+
+INT_PTR CALLBACK EditOrRODlgproc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (Msg)
+    {
+        case WM_INITDIALOG:
+        {
+            if (const wchar_t* sFilePath = reinterpret_cast<const wchar_t*>(lParam))
+            {
+                OUString sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_TITLE);
+                SetWindowTextW(hDlg, o3tl::toW(sMsg.getStr()));
+                sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_MESSAGE)
+                           .replaceFirst("%DOCNAME", o3tl::toU(sFilePath));
+                SetWindowTextW(GetDlgItem(hDlg, IDC_EDIT_OR_RO), o3tl::toW(sMsg.getStr()));
+                sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_VIEW);
+                SetWindowTextW(GetDlgItem(hDlg, ID_RO), o3tl::toW(sMsg.getStr()));
+                sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_EDIT);
+                SetWindowTextW(GetDlgItem(hDlg, ID_EDIT), o3tl::toW(sMsg.getStr()));
+                sMsg = GetString(RID_STR_SP_VIEW_OR_EDIT_CANCEL);
+                SetWindowTextW(GetDlgItem(hDlg, IDCANCEL), o3tl::toW(sMsg.getStr()));
+            }
+            return TRUE; // set default focus
+        }
+        case WM_COMMAND:
+        {
+            WORD nId = LOWORD(wParam);
+            switch (nId)
+            {
+                case IDCANCEL:
+                case ID_RO:
+                case ID_EDIT:
+                    EndDialog(hDlg, nId);
+                    return TRUE;
+            }
+            break;
+        }
+    }
+    return FALSE;
+}
+
+enum class Answer
+{
+    Cancel,
+    ReadOnly,
+    Edit
+};
+
+Answer AskIfUserWantsToEdit(const wchar_t* sFilePath)
+{
+    Answer res = Answer::Cancel;
+    INT_PTR nResult = DialogBoxParamW(nullptr, MAKEINTRESOURCEW(IDD_EDIT_OR_RO), nullptr,
+                                      EditOrRODlgproc, reinterpret_cast<LPARAM>(sFilePath));
+    if (nResult == ID_RO)
+        res = Answer::ReadOnly;
+    else if (nResult == ID_EDIT)
+        res = Answer::Edit;
+    return res;
+}
+
+// Returns ERROR_SUCCESS or Win32 error code
+DWORD LOStart(const wchar_t* sModeArg, const wchar_t* sFilePath)
+{
+    OUString sCmdLine = "\"" + GetSofficeExe() + "\" " + OUString(o3tl::toU(sModeArg)) + " \""
+                        + OUString(o3tl::toU(sFilePath)) + "\"";
+    LPWSTR pCmdLine = const_cast<LPWSTR>(o3tl::toW(sCmdLine.getStr()));
+
+    STARTUPINFOW si;
+    std::memset(&si, 0, sizeof si);
+    si.cb = sizeof si;
+    si.dwFlags = STARTF_USESHOWWINDOW;
+    si.wShowWindow = SW_SHOW;
+    PROCESS_INFORMATION pi{};
+    if (!CreateProcessW(nullptr, pCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi))
+    {
+        DWORD dwError = GetLastError();
+        wchar_t* sMsgBuf = nullptr;
+        FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
+                           | FORMAT_MESSAGE_IGNORE_INSERTS,
+                       nullptr, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       reinterpret_cast<LPWSTR>(&sMsgBuf), 0, nullptr);
+
+        size_t nBufSize = wcslen(sMsgBuf) + 100;
+        std::vector<wchar_t> sDisplayBuf(nBufSize);
+        swprintf(sDisplayBuf.data(), nBufSize,
+                 L"Could not start LibreOffice. Error is 0x%08X:\n\n%s", dwError, sMsgBuf);
+        HeapFree(GetProcessHeap(), 0, sMsgBuf);
+
+        // Report the error to user and return error
+        MessageBoxW(nullptr, sDisplayBuf.data(), nullptr, MB_ICONERROR);
+        return dwError;
+    }
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    return ERROR_SUCCESS;
+}
+
+int CreateNewDocument(LPCWSTR TemplateLocation, LPCWSTR /*DefaultSaveLocation*/)
+{
+    // TODO: resolve the program from varProgID (nullptr -> default?)
+    DWORD nResult = LOStart(L"-n", TemplateLocation);
+    return nResult == ERROR_SUCCESS ? 0 : 2;
+}
+
+// UseLocalCopy would be either "0" or "1", denoting boolean value
+int EditDocument(LPCWSTR DocumentLocation, LPCWSTR /*UseLocalCopy*/, LPCWSTR /*varProgID*/)
+{
+    // TODO: resolve the program from varProgID (nullptr -> default?)
+    DWORD nResult = LOStart(L"-o", DocumentLocation);
+    return nResult == ERROR_SUCCESS ? 0 : 2;
+}
+
+// Possible values for OpenType
+//
+// "0" When checked out, or when the document library does not require check out, the user can read or edit the document
+// "1" When another user has checked it out, the user can only read the document
+// "2" When the current user has checked it out, the user can only edit the document
+// "3" When the document is not checked out and the document library requires that documents be checked out to be edited, the user can only read the document, or check it out and edit it
+// "4" When the current user has checked it out, the user can only edit the local copy of the document
+int ViewDocument(LPCWSTR DocumentLocation, LPCWSTR OpenType, LPCWSTR varProgID)
+{
+    if (wcscmp(OpenType, L"0") == 0)
+    {
+        switch (AskIfUserWantsToEdit(DocumentLocation))
+        {
+            case Answer::Cancel:
+                return 1;
+            case Answer::Edit:
+                return EditDocument(DocumentLocation, L"0", varProgID);
+        }
+    }
+    // TODO: resolve the program from varProgID (nullptr -> default?)
+    DWORD nResult = LOStart(L"--view", DocumentLocation);
+    return nResult == ERROR_SUCCESS ? 0 : 2;
+}
+} // namespace
+
+// Returns 0 on success, 1 when operation wasn't performed because user cancelled, 2 on an error
+int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
+{
+    int argc = 0;
+    const LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+    if (argc < 2)
+        return 2; // Wrong argument count
+
+    if (wcscmp(argv[1], L"CreateNewDocument") == 0)
+    {
+        if (argc != 4)
+            return 2; // Wrong argument count
+        return CreateNewDocument(argv[2], argv[3]);
+    }
+
+    if (wcscmp(argv[1], L"ViewDocument") == 0)
+    {
+        if (argc != 4 && argc != 5)
+            return 2; // Wrong argument count
+        LPCWSTR pProgId = argc == 5 ? argv[4] : nullptr;
+        return ViewDocument(argv[2], argv[3], pProgId);
+    }
+
+    if (wcscmp(argv[1], L"EditDocument") == 0)
+    {
+        if (argc != 4 && argc != 5)
+            return 2; // Wrong argument count
+        LPCWSTR pProgId = argc == 5 ? argv[4] : nullptr;
+        return EditDocument(argv[2], argv[3], pProgId);
+    }
+
+    return 2; // Wrong command
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/spsuppServ.cxx b/shell/source/win32/spsupp/spsuppServ.cxx
index f15f505fd730..64b2090c4cf9 100644
--- a/shell/source/win32/spsupp/spsuppServ.cxx
+++ b/shell/source/win32/spsupp/spsuppServ.cxx
@@ -28,11 +28,12 @@
 
 #include <shlwapi.h> // declaration of DllInstall
 
-namespace {
-
+namespace
+{
 HANDLE g_hModule;
 
-}
+HMODULE GetHModule() { return static_cast<HMODULE>(g_hModule); }
+} // namespace
 
 ITypeLib* GetTypeLib()
 {
@@ -40,7 +41,7 @@ ITypeLib* GetTypeLib()
     static ITypeLibGuard s_aITypeLibGuard = [] {
         ITypeLibGuard aITypeLibGuard(nullptr, [](IUnknown* p) { if (p) p->Release(); });
         wchar_t szFile[MAX_PATH];
-        if (GetModuleFileNameW(static_cast<HMODULE>(g_hModule), szFile, MAX_PATH) == 0)
+        if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0)
             return aITypeLibGuard;
         ITypeLib* pTypeLib;
         if (FAILED(LoadTypeLib(szFile, &pTypeLib)))
@@ -51,16 +52,16 @@ ITypeLib* GetTypeLib()
     return s_aITypeLibGuard.get();
 }
 
-const wchar_t* GetLOPath()
+const wchar_t* GetHelperExe()
 {
     static wchar_t* s_sPath = []() -> wchar_t* {
         static wchar_t sPath[MAX_PATH];
-        if (GetModuleFileNameW(static_cast<HMODULE>(g_hModule), sPath, MAX_PATH) == 0)
+        if (GetModuleFileNameW(GetHModule(), sPath, MAX_PATH) == 0)
             return nullptr;
         wchar_t* pSlashPos = wcsrchr(sPath, L'\\');
         if (pSlashPos == nullptr)
             return nullptr;
-        wcscpy(pSlashPos + 1, L"soffice.exe");
+        wcscpy(pSlashPos + 1, L"spsupp_helper.exe");
         return sPath;
     }();
     return s_sPath;
@@ -120,7 +121,7 @@ STDAPI DllRegisterServer(void)
         return ResultFromScode(SELFREG_E_TYPELIB);
 
     wchar_t szFile[MAX_PATH];
-    if (GetModuleFileNameW(static_cast<HMODULE>(g_hModule), szFile, MAX_PATH) == 0)
+    if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0)
         return HRESULT_FROM_WIN32(GetLastError());
 
     HRESULT hr = RegisterTypeLib(pTypeLib, szFile, nullptr);


More information about the Libreoffice-commits mailing list