1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_ShellHeaderOnlyUtils_h
8 #define mozilla_ShellHeaderOnlyUtils_h
10 #if defined(LIBXUL) && !defined(UNICODE)
12 "UNICODE not set - must be set to prevent compile failure in `comdef.h` due to us deleting `FormatMessage` when absent."
15 #include "mozilla/WinHeaderOnlyUtils.h"
25 // NB: include this after shldisp.h so its macros do not conflict with COM
26 // interfaces defined by shldisp.h
28 #include <type_traits>
33 #include "mozilla/RefPtr.h"
34 #include "mozilla/UniquePtr.h"
39 * Ask the current user's Desktop to ShellExecute on our behalf, thus causing
40 * the resulting launched process to inherit its security priviliges from
41 * Explorer instead of our process.
43 * This is useful in two scenarios, in particular:
44 * * We are running as an elevated user and we want to start something as the
46 * * We are starting a process that is incompatible with our process's
47 * process mitigation policies. By delegating to Explorer, the child process
48 * will not be affected by our process mitigations.
50 * Since this communication happens over DCOM, Explorer's COM DACL governs
51 * whether or not we can execute against it, thus avoiding privilege escalation.
53 inline LauncherVoidResult
ShellExecuteByExplorer(const _bstr_t
& aPath
,
54 const _variant_t
& aArgs
,
55 const _variant_t
& aVerb
,
56 const _variant_t
& aWorkingDir
,
57 const _variant_t
& aShowCmd
) {
58 // NB: Explorer may be a local server, not an inproc server
59 RefPtr
<IShellWindows
> shellWindows
;
60 HRESULT hr
= ::CoCreateInstance(
61 CLSID_ShellWindows
, nullptr, CLSCTX_INPROC_SERVER
| CLSCTX_LOCAL_SERVER
,
62 IID_IShellWindows
, getter_AddRefs(shellWindows
));
64 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
67 // 1. Find the shell view for the desktop.
68 _variant_t
loc(int(CSIDL_DESKTOP
));
71 RefPtr
<IDispatch
> dispDesktop
;
72 hr
= shellWindows
->FindWindowSW(&loc
, &empty
, SWC_DESKTOP
, &hwnd
,
74 getter_AddRefs(dispDesktop
));
76 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
80 // The call succeeded but the window was not found.
81 return LAUNCHER_ERROR_FROM_WIN32(ERROR_NOT_FOUND
);
84 RefPtr
<IServiceProvider
> servProv
;
85 hr
= dispDesktop
->QueryInterface(IID_IServiceProvider
,
86 getter_AddRefs(servProv
));
88 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
91 RefPtr
<IShellBrowser
> browser
;
92 hr
= servProv
->QueryService(SID_STopLevelBrowser
, IID_IShellBrowser
,
93 getter_AddRefs(browser
));
95 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
98 RefPtr
<IShellView
> activeShellView
;
99 hr
= browser
->QueryActiveShellView(getter_AddRefs(activeShellView
));
101 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
104 // 2. Get the automation object for the desktop.
105 RefPtr
<IDispatch
> dispView
;
106 hr
= activeShellView
->GetItemObject(SVGIO_BACKGROUND
, IID_IDispatch
,
107 getter_AddRefs(dispView
));
109 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
112 RefPtr
<IShellFolderViewDual
> folderView
;
113 hr
= dispView
->QueryInterface(IID_IShellFolderViewDual
,
114 getter_AddRefs(folderView
));
116 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
119 // 3. Get the interface to IShellDispatch2
120 RefPtr
<IDispatch
> dispShell
;
121 hr
= folderView
->get_Application(getter_AddRefs(dispShell
));
123 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
126 RefPtr
<IShellDispatch2
> shellDisp
;
128 dispShell
->QueryInterface(IID_IShellDispatch2
, getter_AddRefs(shellDisp
));
130 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
133 // Passing the foreground privilege so that the shell can launch an
134 // application in the foreground. This fails with E_ACCESSDENIED if the
135 // current window is shown in the background. We keep a soft assert for
136 // the other failures to investigate how it happened.
137 hr
= ::CoAllowSetForegroundWindow(shellDisp
, nullptr);
138 MOZ_ASSERT(SUCCEEDED(hr
) || hr
== E_ACCESSDENIED
);
140 // shellapi.h macros interfere with the correct naming of the method being
141 // called on IShellDispatch2. Temporarily remove that definition.
142 #if defined(ShellExecute)
143 # define MOZ_REDEFINE_SHELLEXECUTE
145 #endif // defined(ShellExecute)
147 // 4. Now call IShellDispatch2::ShellExecute to ask Explorer to execute.
148 hr
= shellDisp
->ShellExecute(aPath
, aArgs
, aWorkingDir
, aVerb
, aShowCmd
);
150 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
153 // Restore the macro that was removed prior to IShellDispatch2::ShellExecute
154 #if defined(MOZ_REDEFINE_SHELLEXECUTE)
155 # if defined(UNICODE)
156 # define ShellExecute ShellExecuteW
158 # define ShellExecute ShellExecuteA
160 # undef MOZ_REDEFINE_SHELLEXECUTE
161 #endif // defined(MOZ_REDEFINE_SHELLEXECUTE)
166 using UniqueAbsolutePidl
=
167 UniquePtr
<std::remove_pointer_t
<PIDLIST_ABSOLUTE
>, CoTaskMemFreeDeleter
>;
169 inline LauncherResult
<UniqueAbsolutePidl
> ShellParseDisplayName(
170 const wchar_t* aPath
) {
171 PIDLIST_ABSOLUTE rawAbsPidl
= nullptr;
173 HRESULT hr
= ::SHParseDisplayName(aPath
, nullptr, &rawAbsPidl
, 0, &sfgao
);
175 return LAUNCHER_ERROR_FROM_HRESULT(hr
);
178 return UniqueAbsolutePidl(rawAbsPidl
);
181 } // namespace mozilla
183 #endif // mozilla_ShellHeaderOnlyUtils_h