summaryrefslogtreecommitdiff
path: root/vcl/win/app/fileregistration.cxx
diff options
context:
space:
mode:
authorMatt K <mattkse@gmail.com>2021-03-25 00:40:33 -0500
committerMike Kaganski <mike.kaganski@collabora.com>2021-04-01 06:30:36 +0200
commit6a6cd129f34220fadf5d134a2dc2c1e368acbc4f (patch)
tree4a7793b504a34875969f72b6f9f2997ec6712170 /vcl/win/app/fileregistration.cxx
parentd10a49d64469411a23b3660a82106405808da028 (diff)
tdf#45735 New UI dialog at app startup to check default file formats
On Windows only, and only on a non-admin installation, check on application startup whether the file formats ".ods", ".odt", and ".odp" are registered to be opened by LibreOffice by default. If any of the formats are not default, show a UI dialog informing the user which formats are not default and ask the user to set the defaults. If the user selects "OK" to set defaults then the Windows UI corresponding to the user's Windows version is opened for selecting defaults per program. There is also a checkbox on the dialog to select whether checking is performed on application startup. Also, in Tools -> Options -> General, add a UI checkbox for performing this check on application startup, and refactor the existing button "Windows Default apps" to use the same Windows UI Launch APIs. Change-Id: I5e7258d111ff7da8f68805e60405aec064ddcf7c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/112370 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Diffstat (limited to 'vcl/win/app/fileregistration.cxx')
-rw-r--r--vcl/win/app/fileregistration.cxx198
1 files changed, 198 insertions, 0 deletions
diff --git a/vcl/win/app/fileregistration.cxx b/vcl/win/app/fileregistration.cxx
new file mode 100644
index 000000000000..863e66d0a3b1
--- /dev/null
+++ b/vcl/win/app/fileregistration.cxx
@@ -0,0 +1,198 @@
+/* -*- 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/.
+ */
+
+#if !defined(NTDDI_VERSION) || NTDDI_VERSION < NTDDI_WIN8
+#define NTDDI_VERSION NTDDI_WIN8 // needed for IApplicationActivationManager
+#endif
+
+#include <sal/config.h>
+
+#include <comphelper/scopeguard.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <vcl/abstdlg.hxx>
+#include <vcl/fileregistration.hxx>
+
+#include <strings.hrc>
+
+#include <prewin.h>
+#include <Shobjidl.h>
+#include <systools/win32/comtools.hxx>
+#include <versionhelpers.h>
+#include <postwin.h>
+
+#define MAX_LONG_PATH 32767
+
+namespace vcl::fileregistration
+{
+static void LaunchModernSettingsDialogDefaultApps()
+{
+ auto pIf = sal::systools::COMReference<IApplicationActivationManager>().CoCreateInstance(
+ CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC_SERVER);
+
+ DWORD pid;
+ HRESULT hr = pIf->ActivateApplication(L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
+ if (SUCCEEDED(hr))
+ {
+ // Do not check error because we could at least open
+ // the "Default apps" setting.
+ pIf->ActivateApplication(L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults"
+ L"&target=SettingsPageAppsDefaultsDefaultAppsListView",
+ AO_NONE, &pid);
+ }
+}
+
+static HRESULT
+IsPathDefaultForClass(sal::systools::COMReference<IApplicationAssociationRegistration>& pAAR,
+ LPCWSTR aClassName, LPCWSTR progID)
+{
+ // Make sure the Prog ID matches what we have
+ LPWSTR registeredApp;
+ HRESULT hr
+ = pAAR->QueryCurrentDefault(aClassName, AT_FILEEXTENSION, AL_EFFECTIVE, &registeredApp);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ if (!wcsnicmp(registeredApp, progID, wcslen(progID)))
+ hr = S_OK;
+ else
+ hr = S_FALSE;
+
+ CoTaskMemFree(registeredApp);
+
+ return hr;
+}
+
+static bool IsDefaultAppInstalledInReg()
+{
+ const wchar_t* keyPath = L"SOFTWARE\\LibreOffice\\UNO\\InstallPath";
+
+ WCHAR szRegPath[MAX_LONG_PATH];
+ DWORD cbData = static_cast<DWORD>(MAX_LONG_PATH * sizeof(WCHAR));
+ auto rc = RegGetValueW(HKEY_LOCAL_MACHINE, keyPath, nullptr, RRF_RT_REG_SZ, nullptr,
+ static_cast<PVOID>(szRegPath), &cbData);
+ if (rc != ERROR_SUCCESS)
+ return false;
+
+ WCHAR szProcPath[MAX_LONG_PATH];
+ if (!GetModuleFileNameW(NULL, szProcPath, MAX_LONG_PATH))
+ return false;
+
+ WCHAR szFullProcPath[MAX_LONG_PATH];
+ if (!GetFullPathNameW(szProcPath, MAX_LONG_PATH, szFullProcPath, NULL))
+ return false;
+
+ if (!GetLongPathNameW(szFullProcPath, szFullProcPath, MAX_LONG_PATH))
+ return false;
+
+ if (!GetLongPathNameW(szRegPath, szRegPath, MAX_LONG_PATH))
+ return false;
+
+ if (wcslen(szRegPath) > 0 && wcsstr(szFullProcPath, szRegPath) != NULL)
+ return true;
+
+ return false;
+}
+
+void LaunchRegistrationUI()
+{
+ const bool bUninit = SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
+ comphelper::ScopeGuard g([bUninit]() {
+ if (bUninit)
+ CoUninitialize();
+ });
+
+ try
+ {
+ if (IsWindows10OrGreater())
+ {
+ LaunchModernSettingsDialogDefaultApps();
+ }
+ else
+ {
+ auto pIf = sal::systools::COMReference<IApplicationAssociationRegistrationUI>()
+ .CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI, nullptr,
+ CLSCTX_INPROC_SERVER);
+
+ // LaunchAdvancedAssociationUI only works for applications registered under
+ // Software\RegisteredApplications. See scp2/source/ooo/registryitem_ooo.scp
+ const OUString expanded = Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION");
+ pIf->LaunchAdvancedAssociationUI(o3tl::toW(expanded.getStr()));
+ }
+ }
+ catch (...)
+ {
+ // Just ignore any error here: this is not something we need to make sure to succeed
+ }
+}
+
+void CheckFileExtRegistration(weld::Window* pDialogParent)
+{
+ if (!officecfg::Office::Common::Misc::PerformFileExtCheck::get())
+ return;
+
+ if (!IsDefaultAppInstalledInReg())
+ return;
+
+ const bool bUninit = SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
+ comphelper::ScopeGuard g([bUninit]() {
+ if (bUninit)
+ CoUninitialize();
+ });
+ sal::systools::COMReference<IApplicationAssociationRegistration> pAAR = nullptr;
+ try
+ {
+ pAAR = sal::systools::COMReference<IApplicationAssociationRegistration>().CoCreateInstance(
+ CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC_SERVER);
+ }
+ catch (...)
+ {
+ // Just return on any error here: this is not something we need to make sure to succeed
+ return;
+ }
+
+ std::map<OUString, OUString> formats = {
+ { ".odp", "LibreOffice.ImpressDocument.1" },
+ { ".odt", "LibreOffice.WriterDocument.1" },
+ { ".ods", "LibreOffice.CalcDocument.1" },
+ };
+ OUString aNonDefaults;
+ bool isNotDefault = false;
+
+ for (std::map<OUString, OUString>::iterator it = formats.begin(); it != formats.end(); it++)
+ {
+ if (IsPathDefaultForClass(pAAR, o3tl::toW(it->first.getStr()),
+ o3tl::toW(it->second.getStr()))
+ == S_FALSE)
+ {
+ isNotDefault = true;
+ aNonDefaults += it->first;
+ aNonDefaults += "\n";
+ }
+ }
+
+ if (isNotDefault)
+ {
+ OUString aMsg(VclResId(STR_FILEEXT_NONDEFAULT_ASK_MSG));
+ aMsg = aMsg.replaceFirst("$1", aNonDefaults);
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateFileExtCheckDialog(
+ pDialogParent, VclResId(STR_FILEEXT_NONDEFAULT_ASK_TITLE), aMsg));
+ pDlg->Execute();
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */