summaryrefslogtreecommitdiffstats
path: root/shell/source/win32/spsupp
diff options
context:
space:
mode:
Diffstat (limited to 'shell/source/win32/spsupp')
-rw-r--r--shell/source/win32/spsupp/COMOpenDocuments.cxx424
-rw-r--r--shell/source/win32/spsupp/COMOpenDocuments_x64.cxx13
-rw-r--r--shell/source/win32/spsupp/registrar.cxx290
-rw-r--r--shell/source/win32/spsupp/registrar_x64.cxx13
-rw-r--r--shell/source/win32/spsupp/res/spsupp.rc12
-rw-r--r--shell/source/win32/spsupp/res/spsuppDlg.h27
-rw-r--r--shell/source/win32/spsupp/res/spsuppDlg.rc29
-rw-r--r--shell/source/win32/spsupp/spsupp.def8
-rw-r--r--shell/source/win32/spsupp/spsupp.idl147
-rw-r--r--shell/source/win32/spsupp/spsuppClassFactory.cxx81
-rw-r--r--shell/source/win32/spsupp/spsuppClassFactory_x64.cxx13
-rw-r--r--shell/source/win32/spsupp/spsuppHelper.cxx232
-rw-r--r--shell/source/win32/spsupp/spsuppServ.cxx180
-rw-r--r--shell/source/win32/spsupp/spsuppServ_x64.cxx13
14 files changed, 1482 insertions, 0 deletions
diff --git a/shell/source/win32/spsupp/COMOpenDocuments.cxx b/shell/source/win32/spsupp/COMOpenDocuments.cxx
new file mode 100644
index 000000000..b851f0e5b
--- /dev/null
+++ b/shell/source/win32/spsupp/COMOpenDocuments.cxx
@@ -0,0 +1,424 @@
+/* -*- 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 <sal/config.h>
+
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include <COMOpenDocuments.hpp>
+#include <spsuppServ.hpp>
+#include <stdio.h>
+
+namespace
+{
+template<class... Args>
+HRESULT LOStart(Args... args)
+{
+ 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 = {};
+ 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))
+ 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);
+
+ 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;
+ITypeInfo* COMOpenDocuments::m_pTypeInfo = nullptr;
+
+COMOpenDocuments::COMOpenDocuments()
+{
+ ::InterlockedIncrement(&m_nObjCount);
+ if (m_pTypeInfo == nullptr)
+ {
+ ITypeLib* pITypeLib = GetTypeLib();
+ HRESULT hr = pITypeLib->GetTypeInfoOfGuid(__uuidof(IOWSNewDocument3), &m_pTypeInfo);
+ if (FAILED(hr))
+ throw Error(hr);
+ }
+}
+
+COMOpenDocuments::~COMOpenDocuments()
+{
+ if (::InterlockedDecrement(&m_nObjCount) == 0 && m_pTypeInfo)
+ {
+ m_pTypeInfo->Release();
+ m_pTypeInfo = nullptr;
+ }
+}
+
+// IUnknown methods
+
+STDMETHODIMP COMOpenDocuments::QueryInterface(REFIID riid, void **ppvObject)
+{
+ *ppvObject = nullptr;
+ if (IsEqualIID(riid, __uuidof(IUnknown)) ||
+ IsEqualIID(riid, __uuidof(IDispatch)) ||
+ IsEqualIID(riid, __uuidof(IOWSNewDocument)) ||
+ IsEqualIID(riid, __uuidof(IOWSNewDocument2)) ||
+ IsEqualIID(riid, __uuidof(IOWSNewDocument3)))
+ {
+ *ppvObject = static_cast<IOWSNewDocument3*>(this);
+ }
+ else if (IsEqualIID(riid, __uuidof(IObjectSafety)))
+ {
+ *ppvObject = static_cast<IObjectSafety*>(this);
+ }
+ else
+ {
+ return E_NOINTERFACE;
+ }
+
+ static_cast<IUnknown*>(*ppvObject)->AddRef();
+ return S_OK;
+}
+
+// IDispatch methods
+
+STDMETHODIMP COMOpenDocuments::GetTypeInfoCount(UINT *pctinfo)
+{
+ if (pctinfo == nullptr)
+ return E_INVALIDARG;
+
+ *pctinfo = 1;
+ return S_OK;
+}
+
+STDMETHODIMP COMOpenDocuments::GetTypeInfo(UINT iTInfo, LCID /*lcid*/, ITypeInfo **ppTInfo)
+{
+ if (ppTInfo == nullptr)
+ return E_INVALIDARG;
+ *ppTInfo = nullptr;
+
+ if (iTInfo != 0)
+ return DISP_E_BADINDEX;
+
+ (*ppTInfo = m_pTypeInfo)->AddRef();
+ return S_OK;
+}
+
+STDMETHODIMP COMOpenDocuments::GetIDsOfNames(
+ REFIID /*riid*/,
+ LPOLESTR *rgszNames,
+ UINT cNames,
+ LCID /*lcid*/,
+ DISPID *rgDispId)
+{
+ return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
+}
+
+STDMETHODIMP COMOpenDocuments::Invoke(
+ DISPID dispIdMember,
+ REFIID /*riid*/, // IID_NULL (see https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479)
+ LCID /*lcid*/,
+ WORD wFlags,
+ DISPPARAMS *pDispParams,
+ VARIANT *pVarResult,
+ EXCEPINFO *pExcepInfo,
+ UINT *puArgErr)
+{
+ return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+}
+
+// IOWSNewDocument methods
+
+// Creates a document based on the specified document template
+STDMETHODIMP COMOpenDocuments::CreateNewDocument(
+ 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
+ VARIANT_BOOL *pbResult) // true if the document creation succeeds; otherwise false
+{
+ return CreateNewDocument2(nullptr, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult);
+}
+
+// Opens the specified document for editing with its associated application
+// or with the specified editor
+STDMETHODIMP COMOpenDocuments::EditDocument(
+ BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing
+ 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
+{
+ return EditDocument3(nullptr, bstrDocumentLocation, FALSE, varProgID, pbResult);
+}
+
+// IOWSNewDocument2 methods
+
+// Opens the document for reading instead of editing, so that the document is not locked on the server
+//
+// Use the ViewDocument method to open a document in its appropriate application,
+// instead of inside of an application instance embedded within the browser
+STDMETHODIMP COMOpenDocuments::ViewDocument(
+ BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
+ 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
+{
+ return ViewDocument3(nullptr, bstrDocumentLocation, 0, varProgID, pbResult);
+}
+
+// Opens the document for reading instead of editing, so that the document
+// is not locked on the server and in a specified window
+//
+// Use the ViewDocument method to open a document in its appropriate application,
+// instead of inside of an application instance embedded within the browser
+STDMETHODIMP COMOpenDocuments::ViewDocument2(
+ IDispatch *pdisp, // An Object that represents the window from which the ViewDocument2 method is being activated
+ BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for reading
+ 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
+{
+ return ViewDocument3(pdisp, bstrDocumentLocation, 0, varProgID, pbResult);
+}
+
+// Opens the specified document for editing with its associated application
+// or with the specified editor based on the specified window object
+STDMETHODIMP COMOpenDocuments::EditDocument2(
+ IDispatch *pdisp, // An Object that represents the window from which the EditDocument2 method is being activated
+ BSTR bstrDocumentLocation, // A string that contains the URL of the document to open for editing
+ 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
+{
+ return EditDocument3(pdisp, bstrDocumentLocation, FALSE, varProgID, pbResult);
+}
+
+// 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
+ 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
+ VARIANT_BOOL* pbResult) // true if the document creation succeeds; otherwise false
+{
+ if (!pbResult)
+ return E_POINTER;
+ // TODO: resolve the program from varProgID (nullptr -> default?)
+ return ImplCreateNewDocument(pdisp, bstrTemplateLocation, bstrDefaultSaveLocation, pbResult);
+}
+
+// Used with the OpenDocuments.CreateNewDocument2 method to determine
+// whether the security dialog box that appears when a document is opened has already appeared
+//
+// If the PromptedOnLastOpen method returns true, the window containing the document library view
+// refreshes itself the next time it receives focus. One refresh can occur after the new document
+// is saved to the server
+STDMETHODIMP COMOpenDocuments::PromptedOnLastOpen(
+ VARIANT_BOOL* pbResult) // true if the security dialog box that appears when a document is opened has already appeared; otherwise false
+{
+ // This method is used by SharePoint e.g. after calling ViewDocument3. Needs to be implemented,
+ // otherwise IE would show download bar ("Do you want to open Foo.xls?"), as if opening with
+ // LibreOffice failed, even if actually it succeeded.
+ if (!pbResult)
+ return E_POINTER;
+ // Returning true makes SharePoint library to refresh only when focused next time; false makes
+ // it refresh instantly. The JavaScript code involved is this:
+ // var fRefreshOnNextFocus=stsOpen.PromptedOnLastOpen();
+ // if (fRefreshOnNextFocus)
+ // window.onfocus=RefreshOnNextFocus;
+ // else
+ // SetWindowRefreshOnFocus();
+ // It seems to be no reason to require immediate refresh, so just return true.
+ *pbResult = VARIANT_TRUE;
+ return S_OK;
+}
+
+// IOWSNewDocument3 methods
+
+// Opens the document for reading instead of editing, so that the document
+// is not locked on the server in a specified window, and with a specified type
+//
+// The following table shows 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
+STDMETHODIMP COMOpenDocuments::ViewDocument3(
+ 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
+ VARIANT_BOOL *pbResult) // true if the document was successfully opened; otherwise false
+{
+ if (!pbResult)
+ return E_POINTER;
+ return ImplViewDocument(pdisp, bstrDocumentLocation, OpenType, varProgID, pbResult);
+}
+
+// Checks in the specified document to a library
+STDMETHODIMP COMOpenDocuments::CheckinDocument(
+ BSTR /*bstrDocumentLocation*/, // A string that contains the URL of the document to check in
+ int /*CheckinType*/, // A Long that specifies the type of check-in, where 0 = minor check-in, 1 = major check-in, and 2 = overwrite check-in
+ BSTR /*CheckinComment*/, // A string that contains a comment for checking in the document
+ VARIANT_BOOL /*bKeepCheckout*/, // Optional. true to check in changes that have been made to the document yet keep the document checked out; otherwise, false. The default value is false
+ VARIANT_BOOL* /*pbResult*/) // true if the document is successfully checked in; otherwise, false
+{
+ // TODO
+ return E_NOTIMPL;
+}
+
+// Discards the check out of a document to the client computer and deletes the local draft
+STDMETHODIMP COMOpenDocuments::DiscardLocalCheckout(
+ BSTR /*bstrDocumentLocationRaw*/, // A string that contains the URL of the document
+ VARIANT_BOOL* /*pbResult*/) // true if the operation to discard the local checkout of the document is successful; otherwise, false
+{
+ // TODO
+ return E_NOTIMPL;
+}
+
+// Deprecated. Returns E_NOTIMPL
+STDMETHODIMP COMOpenDocuments::ViewInExcel(
+ BSTR /*SiteUrl*/,
+ BSTR /*FileName*/,
+ BSTR /*SessionId*/,
+ BSTR /*Cmd*/,
+ BSTR /*Sheet*/,
+ int /*Row*/,
+ int /*Column*/,
+ VARIANT /*varProgID*/)
+{
+ return E_NOTIMPL;
+}
+
+// Checks out a document from a library
+STDMETHODIMP COMOpenDocuments::CheckoutDocumentPrompt(
+ BSTR /*bstrDocumentLocationRaw*/, // A string that contains the URL of the document to check out
+ VARIANT_BOOL /*fEditAfterCheckout*/, // true to open the document in an editing application; otherwise, false
+ VARIANT /*varProgID*/, // An optional string that contains the ProgID of the application that is used to work with the document. If this argument is omitted, the default application for the document is used
+ VARIANT_BOOL* /*pbResult*/) // true if the document is successfully checked out; otherwise, false
+{
+ // TODO
+ return E_NOTIMPL;
+}
+
+// Opens the specified document for editing with its associated application
+// 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
+ 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
+{
+ if (!pbResult)
+ return E_POINTER;
+ // TODO: resolve the program from varProgID (nullptr -> default?)
+ return ImplEditDocument(pdisp, bstrDocumentLocation, fUseLocalCopy, varProgID, pbResult);
+}
+
+// Creates a new blog post in the editing application
+STDMETHODIMP COMOpenDocuments::NewBlogPost(
+ BSTR /*bstrProviderId*/, // A string that contains the GUID of the blog provider
+ BSTR /*bstrBlogUrl*/, // A string that contains the absolute URL of the blog site
+ BSTR /*bstrBlogName*/) // A string that contains the GUID of the blog site and the GUID of the post list separated by the pound sign (#)
+{
+ return E_NOTIMPL;
+}
+
+// IObjectSafety methods
+
+HRESULT STDMETHODCALLTYPE COMOpenDocuments::GetInterfaceSafetyOptions(
+ REFIID riid,
+ DWORD *pdwSupportedOptions,
+ DWORD *pdwEnabledOptions)
+{
+ IUnknown* pUnk;
+ HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk));
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ // We know about it; release reference and return required information
+ pUnk->Release();
+ *pdwSupportedOptions = iSupportedOptionsMask;
+ *pdwEnabledOptions = m_iEnabledOptions;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE COMOpenDocuments::SetInterfaceSafetyOptions(
+ REFIID riid,
+ DWORD dwOptionSetMask,
+ DWORD dwEnabledOptions)
+{
+ IUnknown* pUnk;
+ HRESULT hr = QueryInterface(riid, reinterpret_cast<void**>(&pUnk));
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+ pUnk->Release();
+
+ // Are there unsupported options in mask?
+ if (dwOptionSetMask & ~iSupportedOptionsMask)
+ return E_FAIL;
+
+ m_iEnabledOptions = (m_iEnabledOptions & ~dwOptionSetMask) | (dwOptionSetMask & dwEnabledOptions);
+ return S_OK;
+}
+
+// Non-COM methods
+
+long COMOpenDocuments::GetObjectCount() { return m_nObjCount; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx b/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx
new file mode 100644
index 000000000..2a155fefa
--- /dev/null
+++ b/shell/source/win32/spsupp/COMOpenDocuments_x64.cxx
@@ -0,0 +1,13 @@
+/* -*- 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/.
+*/
+
+// A stub for generating x64 DLL without the need to copy source files to a temporary directory
+#include "COMOpenDocuments.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/registrar.cxx b/shell/source/win32/spsupp/registrar.cxx
new file mode 100644
index 000000000..008c2ac59
--- /dev/null
+++ b/shell/source/win32/spsupp/registrar.cxx
@@ -0,0 +1,290 @@
+/* -*- 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 <registrar.hpp>
+#include <wchar.h>
+#include <objbase.h>
+
+namespace {
+
+ HRESULT RegRead(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, wchar_t* valData, size_t cchData)
+ {
+ HKEY hKey;
+ long iRetVal = RegCreateKeyExW(
+ hRootKey,
+ subKey,
+ 0,
+ nullptr,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ nullptr,
+ &hKey,
+ nullptr);
+ if (iRetVal != ERROR_SUCCESS)
+ return HRESULT_FROM_WIN32(iRetVal);
+
+ DWORD cbData = cchData * sizeof(valData[0]);
+ DWORD dwType;
+ iRetVal = RegQueryValueExW(hKey, valName, nullptr, &dwType, reinterpret_cast<LPBYTE>(valData), &cbData);
+ RegCloseKey(hKey);
+ if ((iRetVal == ERROR_SUCCESS) && (dwType != REG_SZ))
+ {
+ return E_FAIL;
+ }
+ return HRESULT_FROM_WIN32(iRetVal);
+ }
+
+ HRESULT RegWrite(HKEY hRootKey, const wchar_t* subKey, const wchar_t* valName, const wchar_t* valData, HKEY *hKeyResult = nullptr)
+ {
+ HKEY hKey;
+ long iRetVal = RegCreateKeyExW(
+ hRootKey,
+ subKey,
+ 0,
+ nullptr,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ nullptr,
+ &hKey,
+ nullptr);
+ if (iRetVal != ERROR_SUCCESS)
+ return HRESULT_FROM_WIN32(iRetVal);
+
+ if (valData)
+ {
+ DWORD cbData = static_cast<DWORD>(wcslen(valData)*sizeof(valData[0]));
+ iRetVal = RegSetValueExW(hKey, valName, 0, REG_SZ, reinterpret_cast<const BYTE *>(valData), cbData);
+ }
+
+ if (hKeyResult && (iRetVal == ERROR_SUCCESS))
+ *hKeyResult = hKey;
+ else
+ RegCloseKey(hKey);
+
+ return HRESULT_FROM_WIN32(iRetVal);
+ }
+
+ HRESULT RegDel(HKEY hRootKey, const wchar_t* subKey)
+ {
+ long iRetVal = RegDeleteKeyW(hRootKey, subKey);
+ return HRESULT_FROM_WIN32(iRetVal);
+ }
+
+} // namespace
+
+// see http://stackoverflow.com/questions/284619
+// see https://msdn.microsoft.com/en-us/library/ms691424
+// see https://msdn.microsoft.com/en-us/library/ms694514
+
+Registrar::Registrar(REFIID riidCLSID)
+{
+ m_ConstructionResult = (StringFromGUID2(riidCLSID, m_sCLSID, nGUIDlen) == 0) ?
+ E_UNEXPECTED: S_OK;
+}
+
+HRESULT Registrar::RegisterObject(REFIID riidTypeLib, const wchar_t* sProgram,
+ const wchar_t* sComponent, std::initializer_list<int> aVersions,
+ const wchar_t* Path)
+{
+ if (!wcslen(sComponent) || !wcslen(sProgram))
+ return E_INVALIDARG;
+
+ if (FAILED(m_ConstructionResult))
+ return m_ConstructionResult;
+
+ // HKEY_CLASSES_ROOT
+ // \CLSID
+ // \{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
+ // (default) = "MyLibrary MyControl Class"
+ // \InprocServer32
+ // (default) = "c:\foo\control.dll"
+ // ThreadingModel = "Apartment"
+ // \ProgID
+ // (default) = "MyLibrary.MyControl"
+ // \Programmable
+ // \TypeLib
+ // (default) = "{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}"
+
+ wchar_t sBufKey[MAX_PATH];
+ wchar_t sBufVal[MAX_PATH];
+
+ // CLSID
+ swprintf(sBufKey, MAX_PATH, L"CLSID\\%s", m_sCLSID);
+ swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent);
+ HKEY hKeyCLSID;
+ HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, &hKeyCLSID);
+ if (FAILED(hr))
+ return hr;
+ {
+ class HKeyGuard {
+ public:
+ HKeyGuard(HKEY aKey) : m_hKey(aKey) {}
+ ~HKeyGuard() { RegCloseKey(m_hKey); }
+ private:
+ HKEY m_hKey;
+ };
+
+ HKeyGuard hKeyCLSIDGuard(hKeyCLSID);
+
+ // InprocServer32
+ HKEY hKeyInprocServer32;
+ hr = RegWrite(hKeyCLSID, L"InprocServer32", L"", Path, &hKeyInprocServer32);
+ if (FAILED(hr))
+ return hr;
+ {
+ HKeyGuard hKeyInProcServer32Guard(hKeyInprocServer32);
+ hr = RegWrite(hKeyInprocServer32, L"", L"ThreadingModel", L"Apartment");
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // ProgID
+ swprintf(sBufVal, MAX_PATH, L"%s.%s", sProgram, sComponent);
+ hr = RegWrite(hKeyCLSID, L"ProgID", L"", sBufVal);
+ if (FAILED(hr))
+ return hr;
+
+ // Programmable
+ hr = RegWrite(hKeyCLSID, L"Programmable", nullptr, nullptr);
+ if (FAILED(hr))
+ return hr;
+
+ // TypeLib
+ if (::StringFromGUID2(riidTypeLib, sBufVal, nGUIDlen) == 0)
+ return E_UNEXPECTED;
+ hr = RegWrite(hKeyCLSID, L"TypeLib", L"", sBufVal);
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // ProgID
+ return RegisterProgIDs(sProgram, sComponent, aVersions);
+}
+
+HRESULT Registrar::UnRegisterObject(const wchar_t* sProgram, const wchar_t* sComponent,
+ std::initializer_list<int> aVersions)
+{
+ if (FAILED(m_ConstructionResult))
+ return m_ConstructionResult;
+ // ProgID
+ UnRegisterProgIDs(sProgram, sComponent, aVersions);
+ // CLSID
+ wchar_t sBuf[MAX_PATH];
+ swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\InProcServer32", m_sCLSID);
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\ProgId", m_sCLSID);
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\Programmable", m_sCLSID);
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ swprintf(sBuf, MAX_PATH, L"CLSID\\%s\\TypeLib", m_sCLSID);
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ swprintf(sBuf, MAX_PATH, L"CLSID\\%s", m_sCLSID);
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ return S_OK;
+}
+
+HRESULT Registrar::RegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion, bool bSetDefault)
+{
+ // HKEY_CLASSES_ROOT
+ // \MyLibrary.MyControl
+ // (default) = "MyLibrary MyControl Class"
+ // \CurVer
+ // (default) = "MyLibrary.MyControl.1"
+ // \MyLibrary.MyControl.1
+ // (default) = "MyLibrary MyControl Class"
+ // \CLSID
+ // (default) = "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
+ if (FAILED(m_ConstructionResult))
+ return m_ConstructionResult;
+ wchar_t sBufKey[MAX_PATH];
+ swprintf(sBufKey, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion);
+ wchar_t sBufVal[MAX_PATH];
+ swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent);
+ RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal);
+ swprintf(sBufKey, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion);
+ HRESULT hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", m_sCLSID);
+ if (SUCCEEDED(hr) && bSetDefault)
+ {
+ swprintf(sBufKey, MAX_PATH, L"%s.%s", sProgram, sComponent);
+ swprintf(sBufVal, MAX_PATH, L"%s %s Class", sProgram, sComponent);
+ hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal);
+ swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent);
+ swprintf(sBufVal, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion);
+ hr = RegWrite(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal);
+ }
+ return hr;
+}
+
+HRESULT Registrar::RegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent,
+ std::initializer_list<int> aVersions)
+{
+ HRESULT hr = S_OK;
+ bool bDefaultRegistered = false;
+ for (int nVersion : aVersions)
+ {
+ if (SUCCEEDED(hr))
+ {
+ hr = RegisterProgID(sProgram, sComponent, nVersion, !bDefaultRegistered);
+ bDefaultRegistered = true;
+ }
+ }
+ return hr;
+}
+
+HRESULT Registrar::UnRegisterProgID(const wchar_t* sProgram, const wchar_t* sComponent, int nVersion)
+{
+ if (FAILED(m_ConstructionResult))
+ return m_ConstructionResult;
+ wchar_t sBuf[MAX_PATH];
+ swprintf(sBuf, MAX_PATH, L"%s.%s.%d\\CLSID", sProgram, sComponent, nVersion);
+ wchar_t sCurCLSID[nGUIDlen];
+ HRESULT hr = RegRead(HKEY_CLASSES_ROOT, sBuf, L"", sCurCLSID, nGUIDlen);
+ if (FAILED(hr))
+ return hr;
+ if (wcsncmp(sCurCLSID, m_sCLSID, nGUIDlen) != 0)
+ {
+ // The ProgID points to a different CLSID; most probably it's intercepted
+ // by a different application, so don't remove it
+ return S_FALSE;
+ }
+ RegDel(HKEY_CLASSES_ROOT, sBuf);
+ swprintf(sBuf, MAX_PATH, L"%s.%s.%d", sProgram, sComponent, nVersion);
+ hr = RegDel(HKEY_CLASSES_ROOT, sBuf);
+
+ wchar_t sBufKey[MAX_PATH];
+ swprintf(sBufKey, MAX_PATH, L"%s.%s\\CurVer", sProgram, sComponent);
+ wchar_t sBufVal[MAX_PATH];
+ if (SUCCEEDED(RegRead(HKEY_CLASSES_ROOT, sBufKey, L"", sBufVal, MAX_PATH)) && (wcsncmp(sBufVal, sBuf, MAX_PATH) == 0))
+ {
+ // Only unreg default if this version is current default
+ RegDel(HKEY_CLASSES_ROOT, sBufKey);
+ swprintf(sBuf, MAX_PATH, L"%s.%s", sProgram, sComponent);
+ HRESULT hr1 = RegDel(HKEY_CLASSES_ROOT, sBuf);
+ // Always return a failure result if we failed somewhere
+ if (FAILED(hr1))
+ hr = hr1;
+ }
+ return hr;
+}
+
+HRESULT Registrar::UnRegisterProgIDs(const wchar_t* sProgram, const wchar_t* sComponent,
+ std::initializer_list<int> aVersions)
+{
+ HRESULT hr = S_OK;
+ // Try all ProgIDs regardless of error, but make sure to return failure result if some failed
+ for (int nVersion : aVersions)
+ {
+ HRESULT hrLast = UnRegisterProgID(sProgram, sComponent, nVersion);
+ if (SUCCEEDED(hr))
+ hr = hrLast;
+ }
+ return hr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/registrar_x64.cxx b/shell/source/win32/spsupp/registrar_x64.cxx
new file mode 100644
index 000000000..18372bb27
--- /dev/null
+++ b/shell/source/win32/spsupp/registrar_x64.cxx
@@ -0,0 +1,13 @@
+/* -*- 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/.
+*/
+
+// A stub for generating x64 DLL without the need to copy source files to a temporary directory
+#include "registrar.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/res/spsupp.rc b/shell/source/win32/spsupp/res/spsupp.rc
new file mode 100644
index 000000000..20e323e16
--- /dev/null
+++ b/shell/source/win32/spsupp/res/spsupp.rc
@@ -0,0 +1,12 @@
+/* -*- 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/.
+*/
+
+// Type library
+
+1 TYPELIB TLB_FILE
diff --git a/shell/source/win32/spsupp/res/spsuppDlg.h b/shell/source/win32/spsupp/res/spsuppDlg.h
new file mode 100644
index 000000000..f4b6ccb34
--- /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 000000000..f4ce83a15
--- /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_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOPMOST
+CAPTION "Open Document"
+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/spsupp.def b/shell/source/win32/spsupp/spsupp.def
new file mode 100644
index 000000000..f6828a3e8
--- /dev/null
+++ b/shell/source/win32/spsupp/spsupp.def
@@ -0,0 +1,8 @@
+LIBRARY
+
+EXPORTS
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllInstall PRIVATE
diff --git a/shell/source/win32/spsupp/spsupp.idl b/shell/source/win32/spsupp/spsupp.idl
new file mode 100644
index 000000000..45d5f47b1
--- /dev/null
+++ b/shell/source/win32/spsupp/spsupp.idl
@@ -0,0 +1,147 @@
+/* -*- 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/.
+*/
+
+[
+ uuid(580411ED-80EC-4834-BA1F-2EB07A49C80B),
+ version(0.1),
+ helpstring("LibreOffice SharePoint Client Support Type Library v.0.1")
+]
+library spsupp
+{
+ // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
+ importlib("stdole2.tlb");
+
+ // Forward declare all types defined in this typelib
+ interface IOWSNewDocument;
+ interface IOWSNewDocument2;
+ interface IOWSNewDocument3;
+
+ [
+ odl,
+ uuid(7B678CDE-D71C-4954-ACC7-A92A96BF70DB),
+ helpstring("IOWSNewDocument Interface"),
+ dual,
+ oleautomation
+ ]
+ interface IOWSNewDocument : IDispatch {
+ [id(0x60020000)]
+ HRESULT CreateNewDocument(
+ [in] BSTR bstrTemplateLocation,
+ [in] BSTR bstrDefaultSaveLocation,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60020001)]
+ HRESULT EditDocument(
+ [in] BSTR bstrDocumentLocation,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ };
+
+ [
+ odl,
+ uuid(470D72F8-C6E2-40D1-B844-4FF73DB69EC5),
+ helpstring("IOWSNewDocument2 Interface"),
+ dual,
+ oleautomation
+ ]
+ interface IOWSNewDocument2 : IOWSNewDocument {
+ [id(0x60030000)]
+ HRESULT ViewDocument(
+ [in] BSTR bstrDocumentLocation,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60030001)]
+ HRESULT ViewDocument2(
+ [in] IDispatch* pdisp,
+ [in] BSTR bstrDocumentLocation,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60030002)]
+ HRESULT EditDocument2(
+ [in] IDispatch* pdisp,
+ [in] BSTR bstrDocumentLocation,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60030003)]
+ HRESULT CreateNewDocument2(
+ [in] IDispatch* pdisp,
+ [in] BSTR bstrTemplateLocation,
+ [in] BSTR bstrDefaultSaveLocation,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60030004)]
+ HRESULT PromptedOnLastOpen([out, retval] VARIANT_BOOL* pbResult);
+ };
+
+ [
+ odl,
+ uuid(4D144CA3-2336-4E15-A7D1-A4B151D07CC7),
+ helpstring("IOWSNewDocument3 Interface"),
+ dual,
+ oleautomation
+ ]
+ interface IOWSNewDocument3 : IOWSNewDocument2 {
+ [id(0x60040000)]
+ HRESULT ViewDocument3(
+ [in] IDispatch* pdisp,
+ [in] BSTR bstrDocumentLocation,
+ [in] int OpenType,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60040001)]
+ HRESULT CheckinDocument(
+ [in] BSTR bstrDocumentLocation,
+ [in] int CheckinType,
+ [in] BSTR CheckinComment,
+ [in, defaultvalue(FALSE)] VARIANT_BOOL bKeepCheckout,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60040002)]
+ HRESULT DiscardLocalCheckout(
+ [in] BSTR bstrDocumentLocationRaw,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60040003)]
+ HRESULT ViewInExcel(
+ [in] BSTR SiteUrl,
+ [in] BSTR FileName,
+ [in] BSTR SessionId,
+ [in] BSTR Cmd,
+ [in] BSTR Sheet,
+ [in] int Row,
+ [in] int Column,
+ [in, optional] VARIANT varProgID);
+ [id(0x60040004)]
+ HRESULT CheckoutDocumentPrompt(
+ [in] BSTR bstrDocumentLocationRaw,
+ [in] VARIANT_BOOL fEditAfterCheckout,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60040005)]
+ HRESULT EditDocument3(
+ [in] IDispatch* pdisp,
+ [in] BSTR bstrDocumentLocation,
+ [in] VARIANT_BOOL fUseLocalCopy,
+ [in, optional] VARIANT varProgID,
+ [out, retval] VARIANT_BOOL* pbResult);
+ [id(0x60040006)]
+ HRESULT NewBlogPost(
+ [in] BSTR bstrProviderId,
+ [in] BSTR bstrBlogUrl,
+ [in] BSTR bstrBlogName);
+ };
+
+ [
+ uuid(4AD14812-2807-48B1-A27F-BA836D874E45),
+ helpstring("COMOpenDocuments Class")
+ ]
+ coclass COMOpenDocuments {
+ [default] interface IOWSNewDocument;
+ interface IOWSNewDocument2;
+ interface IOWSNewDocument3;
+ };
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/spsuppClassFactory.cxx b/shell/source/win32/spsupp/spsuppClassFactory.cxx
new file mode 100644
index 000000000..737e43b44
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppClassFactory.cxx
@@ -0,0 +1,81 @@
+/* -*- 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 <spsuppClassFactory.hpp>
+#include <COMOpenDocuments.hpp>
+
+long ClassFactory::m_nObjCount = 0;
+long ClassFactory::m_nLockCount = 0;
+
+ClassFactory::ClassFactory()
+{
+ ::InterlockedIncrement(&m_nObjCount);
+};
+
+ClassFactory::~ClassFactory()
+{
+ ::InterlockedDecrement(&m_nObjCount);
+};
+
+// IUnknown methods
+
+STDMETHODIMP ClassFactory::QueryInterface(
+ REFIID riid,
+ void **ppvObject)
+{
+ *ppvObject = nullptr;
+ if (IsEqualIID(riid, __uuidof(IUnknown)) ||
+ IsEqualIID(riid, __uuidof(IClassFactory)))
+ {
+ *ppvObject = static_cast<IClassFactory*>(this);
+ AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+// IClassFactory methods
+
+STDMETHODIMP ClassFactory::CreateInstance(
+ IUnknown *pUnkOuter,
+ REFIID riid,
+ void **ppvObject)
+{
+ *ppvObject = nullptr;
+ if (pUnkOuter)
+ {
+ return CLASS_E_NOAGGREGATION;
+ }
+
+ COMOpenDocuments* pObj;
+ try {
+ pObj = new COMOpenDocuments;
+ }
+ catch (const COMOpenDocuments::Error& e) {
+ return e.val();
+ }
+ catch (...) {
+ return E_OUTOFMEMORY;
+ }
+
+ HRESULT hr = pObj->QueryInterface(riid, ppvObject);
+ pObj->Release();
+ return hr;
+}
+
+STDMETHODIMP ClassFactory::LockServer(BOOL fLock)
+{
+ if (fLock)
+ ::InterlockedIncrement(&m_nLockCount);
+ else
+ ::InterlockedDecrement(&m_nLockCount);
+ return S_OK;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx b/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx
new file mode 100644
index 000000000..099a305d9
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppClassFactory_x64.cxx
@@ -0,0 +1,13 @@
+/* -*- 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/.
+*/
+
+// A stub for generating x64 DLL without the need to copy source files to a temporary directory
+#include "spsuppClassFactory.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/spsuppHelper.cxx b/shell/source/win32/spsupp/spsuppHelper.cxx
new file mode 100644
index 000000000..3f6c9f787
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppHelper.cxx
@@ -0,0 +1,232 @@
+/* -*- 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() + "\" " + o3tl::toU(sModeArg) + " \""
+ + o3tl::toU(sFilePath) + "\"";
+ LPWSTR pCmdLine = const_cast<LPWSTR>(o3tl::toW(sCmdLine.getStr()));
+
+ STARTUPINFOW 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::ReadOnly:
+ break;
+ 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
new file mode 100644
index 000000000..c5b10e53b
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppServ.cxx
@@ -0,0 +1,180 @@
+/* -*- 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 MIDL-generated file
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wextra-tokens"
+ // "#endif !_MIDL_USE_GUIDDEF_" in midl-generated code
+#endif
+#include <spsupp_i.c>
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <olectl.h>
+#include <wchar.h>
+#include <spsuppServ.hpp>
+#include <spsuppClassFactory.hpp>
+#include <COMOpenDocuments.hpp>
+#include <registrar.hpp>
+
+#include <shlwapi.h> // declaration of DllInstall
+
+namespace
+{
+HANDLE g_hModule;
+
+HMODULE GetHModule() { return static_cast<HMODULE>(g_hModule); }
+} // namespace
+
+ITypeLib* GetTypeLib()
+{
+ typedef std::unique_ptr<ITypeLib, void(*)(IUnknown* p)> ITypeLibGuard;
+ static ITypeLibGuard s_aITypeLibGuard = [] {
+ ITypeLibGuard aITypeLibGuard(nullptr, [](IUnknown* p) { if (p) p->Release(); });
+ wchar_t szFile[MAX_PATH];
+ if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0)
+ return aITypeLibGuard;
+ ITypeLib* pTypeLib;
+ if (FAILED(LoadTypeLib(szFile, &pTypeLib)))
+ return aITypeLibGuard;
+ aITypeLibGuard.reset(pTypeLib);
+ return aITypeLibGuard;
+ }();
+ return s_aITypeLibGuard.get();
+}
+
+const wchar_t* GetHelperExe()
+{
+ static wchar_t* s_sPath = []() -> wchar_t* {
+ static wchar_t sPath[MAX_PATH];
+ if (GetModuleFileNameW(GetHModule(), sPath, MAX_PATH) == 0)
+ return nullptr;
+ wchar_t* pSlashPos = wcsrchr(sPath, L'\\');
+ if (pSlashPos == nullptr)
+ return nullptr;
+ wcscpy(pSlashPos + 1, L"spsupp_helper.exe");
+ return sPath;
+ }();
+ return s_sPath;
+}
+
+BOOL APIENTRY DllMain( HANDLE hinstDLL,
+ DWORD fdwReason,
+ LPVOID /*lpvReserved*/ )
+{
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ g_hModule = hinstDLL;
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+namespace {
+ // {F1924D0C-9B35-4A46-BCDE-CFEF2CE67A17}
+ static const IID CLSID_spsupp =
+ { 0xf1924d0c, 0x9b35, 0x4a46, { 0xbc, 0xde, 0xcf, 0xef, 0x2c, 0xe6, 0x7a, 0x17 } };
+}
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
+{
+ *ppvOut = nullptr;
+ if (IsEqualIID(rclsid, CLSID_spsupp))
+ {
+ ClassFactory *pCf = new ClassFactory;
+ HRESULT hr = pCf->QueryInterface(riid, ppvOut);
+ pCf->Release();
+ return hr;
+ }
+ return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+STDAPI DllCanUnloadNow(void)
+{
+ if (ClassFactory::GetLockCount() == 0 &&
+ ClassFactory::GetObjectCount() == 0 &&
+ COMOpenDocuments::GetObjectCount() == 0)
+ return S_OK;
+ else
+ return S_FALSE;
+}
+
+STDAPI DllRegisterServer(void)
+{
+ ITypeLib* pTypeLib = GetTypeLib();
+ if (!pTypeLib)
+ return ResultFromScode(SELFREG_E_TYPELIB);
+
+ wchar_t szFile[MAX_PATH];
+ if (GetModuleFileNameW(GetHModule(), szFile, MAX_PATH) == 0)
+ return HRESULT_FROM_WIN32(GetLastError());
+
+ HRESULT hr = RegisterTypeLib(pTypeLib, szFile, nullptr);
+ if (FAILED(hr))
+ return hr;
+
+ // Default is v.5
+ return Registrar(CLSID_spsupp)
+ .RegisterObject(LIBID_spsupp, L"LOSPSupport", L"OpenDocuments", { 5, 1, 2, 3, 4 }, szFile);
+}
+
+STDAPI DllUnregisterServer(void)
+{
+ ITypeLib* pTypeLib = GetTypeLib();
+ if (!pTypeLib)
+ return ResultFromScode(SELFREG_E_TYPELIB);
+ TLIBATTR* pLibAttr;
+ HRESULT hr = pTypeLib->GetLibAttr(&pLibAttr);
+ if (FAILED(hr))
+ return hr;
+ auto ReleaseFunc = [pTypeLib](TLIBATTR* p) { if (p) pTypeLib->ReleaseTLibAttr(p); };
+ typedef std::unique_ptr<TLIBATTR, decltype(ReleaseFunc)> TLIBATTRGuard;
+ static TLIBATTRGuard aTLIBATTRGuard(nullptr, ReleaseFunc);
+
+ hr = UnRegisterTypeLib(pLibAttr->guid, pLibAttr->wMajorVerNum, pLibAttr->wMinorVerNum, pLibAttr->lcid, pLibAttr->syskind);
+ if (FAILED(hr))
+ return hr;
+
+ return Registrar(CLSID_spsupp)
+ .UnRegisterObject(L"LOSPSupport", L"OpenDocuments", { 1, 2, 3, 4, 5 });
+}
+
+// This is called when regsvr32.exe is called with "/i" flag
+// pszCmdLine is the string passed to "/i:<string>"
+// See https://msdn.microsoft.com/library/windows/desktop/bb759846
+STDAPI DllInstall(BOOL bInstall, _In_opt_ PCWSTR pszCmdLine)
+{
+ if (wcscmp(pszCmdLine, L"Substitute_OWSSUPP") == 0)
+ {
+ HRESULT hr;
+ Registrar registrar(CLSID_spsupp);
+ if (bInstall)
+ {
+ // Default is v.5
+ hr = registrar.RegisterProgIDs(L"SharePoint", L"OpenDocuments", { 5, 1, 2, 3, 4 });
+ }
+ else
+ {
+ hr = registrar.UnRegisterProgIDs(L"SharePoint", L"OpenDocuments", { 1, 2, 3, 4, 5 });
+ }
+ return hr;
+ }
+ return E_INVALIDARG;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/shell/source/win32/spsupp/spsuppServ_x64.cxx b/shell/source/win32/spsupp/spsuppServ_x64.cxx
new file mode 100644
index 000000000..41046bf13
--- /dev/null
+++ b/shell/source/win32/spsupp/spsuppServ_x64.cxx
@@ -0,0 +1,13 @@
+/* -*- 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/.
+*/
+
+// A stub for generating x64 DLL without the need to copy source files to a temporary directory
+#include "spsuppServ.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */