Bug 1763573: avoid loading CLSID_SHELL when getting a handle on an IShellDispatch to pin to taskbar on Windows 7 r=rkraesig

It is believed that loading this COM interface is causing crashes by bringing in shell extension DLLS. This patch retrieves it through alternate means (the same way that ShellExecuteByExplorer does already).

It also fixes BSTR handling and does some general cleanup that was requested (and needed).

Differential Revision: https://phabricator.services.mozilla.com/D143776
This commit is contained in:
Ben Hearsum
2022-04-26 19:26:08 +00:00
parent 0b34b1b5bb
commit 664d036f60

View File

@@ -52,6 +52,7 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
# include <Lmcons.h> // For UNLEN
#endif
#include <comutil.h>
#include <objbase.h>
#include <shlobj.h>
#include <knownfolders.h>
@@ -93,6 +94,11 @@ PSSTDAPI PropVariantToString(REFPROPVARIANT propvar, PWSTR psz, UINT cch);
using mozilla::IsWin8OrLater;
using namespace mozilla;
struct SysFreeStringDeleter {
void operator()(BSTR aPtr) { ::SysFreeString(aPtr); }
};
using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>;
NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIToolkitShellService,
nsIShellService, nsIWindowsShellService)
@@ -1042,47 +1048,84 @@ static nsresult GetMatchingShortcut(int aCSIDL, const nsAutoString& aAUMID,
}
static nsresult PinCurrentAppToTaskbarWin7(bool aCheckOnly,
nsAutoString aShortcutPath) {
// Currently disabled due to
// https://bugzilla.mozilla.org/show_bug.cgi?id=1763573 At the time of
// writing, the only callers eat any errors from here so it's safe to return
// one. This is a short term workaround to confirm that this code is indeed
// causing the bug above. If it is, we'll rework this code to avoid it.
return NS_ERROR_ABORT;
// This is a less generalized version of the code from the NSIS
// Invoke Shell Verb plugin.
// https://nsis.sourceforge.io/Invoke_Shell_Verb_plugin
VARIANT dir;
RefPtr<Folder> folder = nullptr;
RefPtr<FolderItem> folderItem = nullptr;
RefPtr<FolderItemVerbs> verbs = nullptr;
RefPtr<FolderItemVerb> fiVerb = nullptr;
RefPtr<IShellDispatch> shellDisp = nullptr;
nsModuleHandle shellInst(LoadLibraryW(L"shell32.dll"));
wchar_t shortcutDir[MAX_PATH + 1], linkName[MAX_PATH + 1];
WCHAR verbName[100];
// Pull the actual verb name that we need to use later
int rv = LoadStringW(shellInst.get(), PIN_TO_TASKBAR_SHELL_VERB, verbName,
sizeof(verbName) / sizeof(verbName[0]));
if (!rv) return NS_ERROR_NOT_AVAILABLE;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
IID_IShellDispatch, getter_AddRefs(shellDisp));
RefPtr<IShellWindows> shellWindows;
HRESULT hr =
::CoCreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER,
IID_IShellWindows, getter_AddRefs(shellWindows));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wcscpy_s(shortcutDir, MAX_PATH + 1, aShortcutPath.get());
PathRemoveFileSpecW(shortcutDir);
// 1. Find the shell view for the desktop.
_variant_t loc(int(CSIDL_DESKTOP));
_variant_t empty;
long hwnd;
RefPtr<IDispatch> dispDesktop;
hr = shellWindows->FindWindowSW(&loc, &empty, SWC_DESKTOP, &hwnd,
SWFO_NEEDDISPATCH,
getter_AddRefs(dispDesktop));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IServiceProvider> servProv;
hr = dispDesktop->QueryInterface(IID_IServiceProvider,
getter_AddRefs(servProv));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellBrowser> browser;
hr = servProv->QueryService(SID_STopLevelBrowser, IID_IShellBrowser,
getter_AddRefs(browser));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellView> activeShellView;
hr = browser->QueryActiveShellView(getter_AddRefs(activeShellView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
// 2. Get the automation object for the desktop.
RefPtr<IDispatch> dispView;
hr = activeShellView->GetItemObject(SVGIO_BACKGROUND, IID_IDispatch,
getter_AddRefs(dispView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellFolderViewDual> folderView;
hr = dispView->QueryInterface(IID_IShellFolderViewDual,
getter_AddRefs(folderView));
if (FAILED(hr)) return NS_ERROR_FAILURE;
// 3. Get the interface to IShellDispatch
RefPtr<IDispatch> dispShell;
hr = folderView->get_Application(getter_AddRefs(dispShell));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<IShellDispatch2> shellDisp;
hr =
dispShell->QueryInterface(IID_IShellDispatch2, getter_AddRefs(shellDisp));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wchar_t shortcutDir[MAX_PATH + 1];
wcscpy_s(shortcutDir, MAX_PATH + 1, aShortcutPath.get());
if (!PathRemoveFileSpecW(shortcutDir)) return NS_ERROR_FAILURE;
VARIANT dir;
dir.vt = VT_BSTR;
dir.bstrVal = shortcutDir;
BStrPtr bstrShortcutDir = BStrPtr(SysAllocString(shortcutDir));
if (bstrShortcutDir.get() == NULL) return NS_ERROR_FAILURE;
dir.bstrVal = bstrShortcutDir.get();
RefPtr<Folder> folder;
hr = shellDisp->NameSpace(dir, getter_AddRefs(folder));
if (FAILED(hr)) return NS_ERROR_FAILURE;
wchar_t linkName[MAX_PATH + 1];
wcscpy_s(linkName, MAX_PATH + 1, aShortcutPath.get());
PathStripPathW(linkName);
hr = folder->ParseName(linkName, getter_AddRefs(folderItem));
BStrPtr bstrLinkName = BStrPtr(SysAllocString(linkName));
if (bstrLinkName.get() == NULL) return NS_ERROR_FAILURE;
RefPtr<FolderItem> folderItem;
hr = folder->ParseName(bstrLinkName.get(), getter_AddRefs(folderItem));
if (FAILED(hr)) return NS_ERROR_FAILURE;
RefPtr<FolderItemVerbs> verbs;
hr = folderItem->Verbs(getter_AddRefs(verbs));
if (FAILED(hr)) return NS_ERROR_FAILURE;
@@ -1090,22 +1133,29 @@ static nsresult PinCurrentAppToTaskbarWin7(bool aCheckOnly,
hr = verbs->get_Count(&count);
if (FAILED(hr)) return NS_ERROR_FAILURE;
int i;
WCHAR verbName[100];
if (!LoadStringW(shellInst.get(), PIN_TO_TASKBAR_SHELL_VERB, verbName,
ARRAYSIZE(verbName))) {
return NS_ERROR_NOT_AVAILABLE;
}
VARIANT v;
v.vt = VT_I4;
BSTR name;
for (i = 0; i < count; ++i) {
BStrPtr name;
for (long i = 0; i < count; ++i) {
RefPtr<FolderItemVerb> fiVerb;
v.lVal = i;
hr = verbs->Item(v, getter_AddRefs(fiVerb));
if (FAILED(hr) || fiVerb == nullptr) {
continue;
}
hr = fiVerb->get_Name(&name);
if (FAILED(hr)) {
continue;
}
if (!wcscmp((WCHAR*)name, verbName)) {
BSTR tmpName;
hr = fiVerb->get_Name(&tmpName);
if (FAILED(hr)) {
continue;
}
name = BStrPtr(tmpName);
if (!wcscmp((WCHAR*)name.get(), verbName)) {
if (aCheckOnly) {
// we've done as much as we can without actually
// changing anything