Reactos
1/*
2 * PROJECT: shell32
3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4 * PURPOSE: IShellDispatch implementation
5 * COPYRIGHT: Copyright 2015-2018 Mark Jansen (mark.jansen@reactos.org)
6 * Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
7 * Copyright 2023 Whindmar Saksit (whindsaks@proton.me)
8 */
9
10#include "precomp.h"
11#include "winsvc.h"
12
13WINE_DEFAULT_DEBUG_CHANNEL(shell);
14
15
16EXTERN_C DWORD WINAPI SHGetRestriction(LPCWSTR lpSubKey, LPCWSTR lpSubName, LPCWSTR lpValue);
17
18static HRESULT PostTrayCommand(UINT cmd)
19{
20 HWND hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
21 return hTrayWnd && PostMessageW(hTrayWnd, WM_COMMAND, cmd, 0) ? S_OK : S_FALSE;
22}
23
24CShellDispatch::CShellDispatch()
25{
26}
27
28CShellDispatch::~CShellDispatch()
29{
30}
31
32HRESULT CShellDispatch::Initialize()
33{
34 return S_OK;
35}
36
37// *** IShellDispatch methods ***
38HRESULT STDMETHODCALLTYPE CShellDispatch::get_Application(IDispatch **ppid)
39{
40 TRACE("(%p, %p)\n", this, ppid);
41
42 if (!ppid)
43 return E_INVALIDARG;
44
45 *ppid = this;
46 AddRef();
47
48 return S_OK;
49}
50
51HRESULT STDMETHODCALLTYPE CShellDispatch::get_Parent(IDispatch **ppid)
52{
53 TRACE("(%p, %p)\n", this, ppid);
54
55 if (ppid)
56 {
57 *ppid = static_cast<IDispatch*>(this);
58 AddRef();
59 }
60
61 return S_OK;
62}
63
64HRESULT VariantToIdlist(VARIANT* var, LPITEMIDLIST* idlist)
65{
66 HRESULT hr = E_FAIL;
67 if(V_VT(var) == VT_I4)
68 {
69 hr = SHGetSpecialFolderLocation(NULL, V_I4(var), idlist);
70 }
71 else if(V_VT(var) == VT_BSTR)
72 {
73 hr = SHILCreateFromPathW(V_BSTR(var), idlist, NULL);
74 }
75 return hr;
76}
77
78HRESULT STDMETHODCALLTYPE CShellDispatch::NameSpace(VARIANT vDir, Folder **ppsdf)
79{
80 TRACE("(%p, %s, %p)\n", this, debugstr_variant(&vDir), ppsdf);
81 if (!ppsdf)
82 return E_POINTER;
83 *ppsdf = NULL;
84 HRESULT hr;
85
86 if (V_VT(&vDir) == VT_I2)
87 {
88 hr = VariantChangeType(&vDir, &vDir, 0, VT_I4);
89 if (FAILED_UNEXPECTEDLY(hr))
90 return hr;
91 }
92
93 CComHeapPtr<ITEMIDLIST> idlist;
94 hr = VariantToIdlist(&vDir, &idlist);
95 if (!SUCCEEDED(hr))
96 return S_FALSE;
97
98 return ShellObjectCreatorInit<CFolder>(static_cast<LPITEMIDLIST>(idlist), IID_PPV_ARG(Folder, ppsdf));
99}
100
101static BOOL is_optional_argument(const VARIANT *arg)
102{
103 return V_VT(arg) == VT_ERROR && V_ERROR(arg) == DISP_E_PARAMNOTFOUND;
104}
105
106HRESULT STDMETHODCALLTYPE CShellDispatch::BrowseForFolder(LONG Hwnd, BSTR Title, LONG Options, VARIANT RootFolder, Folder **ppsdf)
107{
108 TRACE("(%p, %lu, %ls, %lu, %s, %p)\n", this, Hwnd, Title, Options, debugstr_variant(&RootFolder), ppsdf);
109
110 *ppsdf = NULL;
111
112 BROWSEINFOW bi = { 0 };
113 bi.hwndOwner = reinterpret_cast<HWND>(LongToHandle(Hwnd));
114 bi.lpszTitle = Title;
115 bi.ulFlags = Options | BIF_NEWDIALOGSTYLE;
116
117 CComHeapPtr<ITEMIDLIST> idlist;
118 if (!is_optional_argument(&RootFolder) && VariantToIdlist(&RootFolder, &idlist) == S_OK)
119 bi.pidlRoot = idlist;
120
121 CComHeapPtr<ITEMIDLIST> selection;
122 selection.Attach(SHBrowseForFolderW(&bi));
123 if (!selection)
124 return S_FALSE;
125
126 return ShellObjectCreatorInit<CFolder>(static_cast<LPITEMIDLIST>(selection), IID_PPV_ARG(Folder, ppsdf));
127}
128
129HRESULT STDMETHODCALLTYPE CShellDispatch::Windows(IDispatch **ppid)
130{
131 TRACE("(%p, %p)\n", this, ppid);
132 return CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IDispatch, ppid));
133}
134
135static HRESULT SHELL_OpenFolder(LPCITEMIDLIST pidl, LPCWSTR verb = NULL)
136{
137 SHELLEXECUTEINFOW sei;
138 sei.cbSize = sizeof(sei);
139 sei.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_DDEWAIT;
140 sei.hwnd = NULL;
141 sei.lpVerb = verb;
142 sei.lpFile = sei.lpParameters = sei.lpDirectory = NULL;
143 sei.nShow = SW_SHOW;
144 sei.lpIDList = const_cast<LPITEMIDLIST>(pidl);
145 if (ShellExecuteExW(&sei))
146 return S_OK;
147 DWORD error = GetLastError();
148 return HRESULT_FROM_WIN32(error);
149}
150
151static HRESULT OpenFolder(VARIANT vDir, LPCWSTR verb = NULL)
152{
153 CComHeapPtr<ITEMIDLIST> idlist;
154 HRESULT hr = VariantToIdlist(&vDir, &idlist);
155 if (hr == S_OK && SHELL_OpenFolder(idlist, verb) == S_OK)
156 {
157 return S_OK;
158 }
159 return S_FALSE;
160}
161
162HRESULT STDMETHODCALLTYPE CShellDispatch::Open(VARIANT vDir)
163{
164 TRACE("(%p, %s)\n", this, debugstr_variant(&vDir));
165 return OpenFolder(vDir);
166}
167
168HRESULT STDMETHODCALLTYPE CShellDispatch::Explore(VARIANT vDir)
169{
170 TRACE("(%p, %s)\n", this, debugstr_variant(&vDir));
171 return OpenFolder(vDir, L"explore");
172}
173
174HRESULT STDMETHODCALLTYPE CShellDispatch::MinimizeAll()
175{
176 TRACE("(%p)\n", this);
177 return PostTrayCommand(TRAYCMD_MINIMIZE_ALL);
178}
179
180HRESULT STDMETHODCALLTYPE CShellDispatch::UndoMinimizeALL()
181{
182 TRACE("(%p)\n", this);
183 return PostTrayCommand(TRAYCMD_RESTORE_ALL);
184}
185
186HRESULT STDMETHODCALLTYPE CShellDispatch::FileRun()
187{
188 TRACE("(%p)\n", this);
189 return PostTrayCommand(TRAYCMD_RUN_DIALOG);
190}
191
192HRESULT STDMETHODCALLTYPE CShellDispatch::CascadeWindows()
193{
194 TRACE("(%p)\n", this);
195 return PostTrayCommand(TRAYCMD_CASCADE);
196}
197
198HRESULT STDMETHODCALLTYPE CShellDispatch::TileVertically()
199{
200 TRACE("(%p)\n", this);
201 return PostTrayCommand(TRAYCMD_TILE_V);
202}
203
204HRESULT STDMETHODCALLTYPE CShellDispatch::TileHorizontally()
205{
206 TRACE("(%p)\n", this);
207 return PostTrayCommand(TRAYCMD_TILE_H);
208}
209
210HRESULT STDMETHODCALLTYPE CShellDispatch::ShutdownWindows()
211{
212 ExitWindowsDialog(NULL);
213 return S_OK;
214}
215
216HRESULT STDMETHODCALLTYPE CShellDispatch::Suspend()
217{
218 TRACE("(%p)\n", this);
219 return E_NOTIMPL;
220}
221
222HRESULT STDMETHODCALLTYPE CShellDispatch::EjectPC()
223{
224 TRACE("(%p)\n", this);
225 return E_NOTIMPL;
226}
227
228HRESULT STDMETHODCALLTYPE CShellDispatch::SetTime()
229{
230 TRACE("(%p)\n", this);
231 return PostTrayCommand(TRAYCMD_DATE_AND_TIME);
232}
233
234HRESULT STDMETHODCALLTYPE CShellDispatch::TrayProperties()
235{
236 TRACE("(%p)\n", this);
237 return PostTrayCommand(TRAYCMD_TASKBAR_PROPERTIES);
238}
239
240HRESULT STDMETHODCALLTYPE CShellDispatch::Help()
241{
242 TRACE("(%p)\n", this);
243 return PostTrayCommand(TRAYCMD_HELP_AND_SUPPORT);
244}
245
246HRESULT STDMETHODCALLTYPE CShellDispatch::FindFiles()
247{
248 TRACE("(%p)\n", this);
249 return PostTrayCommand(TRAYCMD_SEARCH_FILES);
250}
251
252HRESULT STDMETHODCALLTYPE CShellDispatch::FindComputer()
253{
254 TRACE("(%p)\n", this);
255 return PostTrayCommand(TRAYCMD_SEARCH_COMPUTERS);
256}
257
258HRESULT STDMETHODCALLTYPE CShellDispatch::RefreshMenu()
259{
260 TRACE("(%p)\n", this);
261 return E_NOTIMPL;
262}
263
264HRESULT STDMETHODCALLTYPE CShellDispatch::ControlPanelItem(BSTR szDir)
265{
266 TRACE("(%p, %ls)\n", this, szDir);
267 return SHRunControlPanel(szDir, NULL) ? S_OK : S_FALSE;
268}
269
270
271// *** IShellDispatch2 methods ***
272HRESULT STDMETHODCALLTYPE CShellDispatch::IsRestricted(BSTR group, BSTR restriction, LONG *value)
273{
274 TRACE("(%p, %ls, %ls, %p)\n", this, group, restriction, value);
275
276 if (!value)
277 return E_INVALIDARG;
278 *value = SHGetRestriction(NULL, group, restriction);
279 return S_OK;
280}
281
282HRESULT STDMETHODCALLTYPE CShellDispatch::ShellExecute(BSTR file, VARIANT v_args, VARIANT v_dir, VARIANT v_op, VARIANT v_show)
283{
284 CComVariant args_str, dir_str, op_str, show_int;
285 WCHAR *args = NULL, *dir = NULL, *op = NULL;
286 INT show = SW_SHOW;
287 HINSTANCE ret;
288
289 TRACE("(%s, %s, %s, %s, %s)\n", debugstr_w(file), debugstr_variant(&v_args),
290 debugstr_variant(&v_dir), debugstr_variant(&v_op), debugstr_variant(&v_show));
291
292 args_str.ChangeType(VT_BSTR, &v_args);
293 if (V_VT(&args_str) == VT_BSTR)
294 args = V_BSTR(&args_str);
295
296 dir_str.ChangeType(VT_BSTR, &v_dir);
297 if (V_VT(&dir_str) == VT_BSTR)
298 dir = V_BSTR(&dir_str);
299
300 op_str.ChangeType(VT_BSTR, &v_op);
301 if (V_VT(&op_str) == VT_BSTR)
302 op = V_BSTR(&op_str);
303
304 show_int.ChangeType(VT_I4, &v_show);
305 if (V_VT(&show_int) == VT_I4)
306 show = V_I4(&show_int);
307
308 ret = ShellExecuteW(NULL, op, file, args, dir, show);
309
310 return (ULONG_PTR)ret > 32 ? S_OK : S_FALSE;
311}
312
313HRESULT STDMETHODCALLTYPE CShellDispatch::FindPrinter(BSTR name, BSTR location, BSTR model)
314{
315 TRACE("(%p, %ls, %ls, %ls)\n", this, name, location, model);
316 return E_NOTIMPL;
317}
318
319HRESULT STDMETHODCALLTYPE CShellDispatch::GetSystemInformation(BSTR name, VARIANT *ret)
320{
321 TRACE("(%p, %ls, %p)\n", this, name, ret);
322
323 if (!lstrcmpiW(name, L"ProcessorArchitecture"))
324 {
325 SYSTEM_INFO si;
326 GetSystemInfo(&si);
327 V_VT(ret) = VT_I4;
328 V_UI4(ret) = si.wProcessorArchitecture;
329 return S_OK;
330 }
331
332 UINT os = 0;
333 if (!lstrcmpiW(name, L"IsOS_Professional"))
334 os = OS_PROFESSIONAL;
335 else if (!lstrcmpiW(name, L"IsOS_Personal"))
336 os = OS_HOME;
337 else if (!lstrcmpiW(name, L"IsOS_DomainMember"))
338 os = OS_DOMAINMEMBER;
339 if (os)
340 {
341 V_VT(ret) = VT_BOOL;
342 V_BOOL(ret) = IsOS(os) ? VARIANT_TRUE : VARIANT_FALSE;
343 return S_OK;
344 }
345
346 return E_NOTIMPL;
347}
348
349static HRESULT OpenServiceHelper(LPCWSTR name, DWORD access, SC_HANDLE &hSvc)
350{
351 hSvc = NULL;
352 SC_HANDLE hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
353 if (!hScm)
354 return HResultFromWin32(GetLastError());
355 HRESULT hr = S_OK;
356 hSvc = OpenServiceW(hScm, name, access);
357 if (!hSvc)
358 hr = HResultFromWin32(GetLastError());
359 CloseServiceHandle(hScm);
360 return hr;
361}
362
363static HRESULT SHELL32_ControlService(BSTR name, DWORD control, VARIANT &persistent)
364{
365 BOOL persist = V_VT(&persistent) == VT_BOOL && V_BOOL(&persistent);
366 DWORD access = persist ? SERVICE_CHANGE_CONFIG : 0;
367 switch (control)
368 {
369 case 0:
370 access |= SERVICE_START;
371 break;
372 case SERVICE_CONTROL_STOP:
373 access |= SERVICE_STOP;
374 break;
375 }
376 SC_HANDLE hSvc;
377 HRESULT hr = OpenServiceHelper(name, access, hSvc);
378 if (SUCCEEDED(hr))
379 {
380 BOOL success;
381 DWORD error, already;
382 if (control)
383 {
384 SERVICE_STATUS ss;
385 success = ControlService(hSvc, control, &ss);
386 error = GetLastError();
387 already = ERROR_SERVICE_NOT_ACTIVE;
388 }
389 else
390 {
391 success = StartService(hSvc, 0, NULL);
392 error = GetLastError();
393 already = ERROR_SERVICE_ALREADY_RUNNING;
394 }
395 hr = success ? S_OK : error == already ? S_FALSE : HRESULT_FROM_WIN32(error);
396 if (SUCCEEDED(hr) && persist)
397 {
398 ChangeServiceConfigW(hSvc, SERVICE_NO_CHANGE,
399 control ? SERVICE_DEMAND_START : SERVICE_AUTO_START,
400 SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
401 }
402 CloseServiceHandle(hSvc);
403 }
404 return hr;
405}
406
407HRESULT STDMETHODCALLTYPE CShellDispatch::ServiceStart(BSTR service, VARIANT persistent, VARIANT *ret)
408{
409 TRACE("(%p, %ls, %s, %p)\n", this, service, wine_dbgstr_variant(&persistent), ret);
410
411 HRESULT hr = SHELL32_ControlService(service, 0, persistent);
412 V_VT(ret) = VT_BOOL;
413 V_BOOL(ret) = (hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE);
414 return hr == S_OK ? S_OK : S_FALSE;
415}
416
417HRESULT STDMETHODCALLTYPE CShellDispatch::ServiceStop(BSTR service, VARIANT persistent, VARIANT *ret)
418{
419 TRACE("(%p, %ls, %s, %p)\n", this, service, wine_dbgstr_variant(&persistent), ret);
420
421 HRESULT hr = SHELL32_ControlService(service, SERVICE_CONTROL_STOP, persistent);
422 V_VT(ret) = VT_BOOL;
423 V_BOOL(ret) = (hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE);
424 return hr == S_OK ? S_OK : S_FALSE;
425}
426
427HRESULT STDMETHODCALLTYPE CShellDispatch::IsServiceRunning(BSTR name, VARIANT *running)
428{
429 SERVICE_STATUS_PROCESS status;
430 SC_HANDLE scm, service;
431 DWORD dummy;
432
433 TRACE("(%s, %p)\n", debugstr_w(name), running);
434
435 V_VT(running) = VT_BOOL;
436 V_BOOL(running) = VARIANT_FALSE;
437
438 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
439 if (!scm)
440 {
441 ERR("failed to connect to service manager\n");
442 return S_OK;
443 }
444
445 service = OpenServiceW(scm, name, SERVICE_QUERY_STATUS);
446 if (!service)
447 {
448 ERR("Failed to open service %s (%u)\n", debugstr_w(name), GetLastError());
449 CloseServiceHandle(scm);
450 return S_OK;
451 }
452
453 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (BYTE *)&status,
454 sizeof(SERVICE_STATUS_PROCESS), &dummy))
455 {
456 TRACE("failed to query service status (%u)\n", GetLastError());
457 CloseServiceHandle(service);
458 CloseServiceHandle(scm);
459 return S_OK;
460 }
461
462 if (status.dwCurrentState == SERVICE_RUNNING)
463 V_BOOL(running) = VARIANT_TRUE;
464
465 CloseServiceHandle(service);
466 CloseServiceHandle(scm);
467
468 return S_OK;
469}
470
471HRESULT STDMETHODCALLTYPE CShellDispatch::CanStartStopService(BSTR service, VARIANT *ret)
472{
473 TRACE("(%p, %ls, %p)\n", this, service, ret);
474
475 SC_HANDLE hSvc;
476 HRESULT hr = OpenServiceHelper(service, SERVICE_START | SERVICE_STOP, hSvc);
477 if (SUCCEEDED(hr))
478 CloseServiceHandle(hSvc);
479 V_VT(ret) = VT_BOOL;
480 V_BOOL(ret) = (hr == S_OK ? VARIANT_TRUE : VARIANT_FALSE);
481 return S_OK;
482}
483
484HRESULT STDMETHODCALLTYPE CShellDispatch::ShowBrowserBar(BSTR clsid, VARIANT show, VARIANT *ret)
485{
486 TRACE("(%p, %ls, %s, %p)\n", this, clsid, wine_dbgstr_variant(&show), ret);
487 return E_NOTIMPL;
488}
489
490
491// *** IShellDispatch3 methods ***
492HRESULT STDMETHODCALLTYPE CShellDispatch::AddToRecent(VARIANT file, BSTR category)
493{
494 TRACE("(%p, %s, %ls)\n", this, wine_dbgstr_variant(&file), category);
495
496 CComHeapPtr<ITEMIDLIST> idlist;
497 HRESULT hr = VariantToIdlist(&file, &idlist);
498 if (hr == S_OK)
499 SHAddToRecentDocs(SHARD_PIDL, (LPCITEMIDLIST)idlist);
500 else
501 hr = S_FALSE;
502 return hr;
503}
504
505
506// *** IShellDispatch4 methods ***
507#define IDM_SECURITY 5001 // From base/shell/explorer/resource.h
508HRESULT STDMETHODCALLTYPE CShellDispatch::WindowsSecurity()
509{
510 TRACE("(%p)\n", this);
511 return PostTrayCommand(IDM_SECURITY);
512}
513
514HRESULT STDMETHODCALLTYPE CShellDispatch::ToggleDesktop()
515{
516 TRACE("(%p)\n", this);
517 return PostTrayCommand(TRAYCMD_TOGGLE_DESKTOP);
518}
519
520HRESULT STDMETHODCALLTYPE CShellDispatch::ExplorerPolicy(BSTR policy, VARIANT *value)
521{
522 TRACE("(%p, %ls, %p)\n", this, policy, value);
523 return E_NOTIMPL;
524}
525
526#ifndef SSF_SERVERADMINUI
527#define SSF_SERVERADMINUI 4
528#endif
529HRESULT STDMETHODCALLTYPE CShellDispatch::GetSetting(LONG setting, VARIANT_BOOL *result)
530{
531 TRACE("(%p, %lu, %p)\n", this, setting, result);
532
533 int flag = -1;
534 SHELLSTATE ss = { };
535 SHGetSetSettings(&ss, setting, FALSE);
536 switch (setting)
537 {
538 case SSF_SHOWALLOBJECTS: flag = ss.fShowAllObjects; break;
539 case SSF_SHOWEXTENSIONS: flag = ss.fShowExtensions; break;
540 case SSF_SHOWSYSFILES: flag = ss.fShowSysFiles; break;
541 case SSF_DONTPRETTYPATH: flag = ss.fDontPrettyPath; break;
542 case SSF_NOCONFIRMRECYCLE: flag = ss.fNoConfirmRecycle; break;
543 case SSF_SHOWSUPERHIDDEN: flag = ss.fShowSuperHidden; break;
544 case SSF_SEPPROCESS: flag = ss.fSepProcess; break;
545 case SSF_STARTPANELON: flag = ss.fStartPanelOn; break;
546 case SSF_SERVERADMINUI: flag = IsOS(OS_SERVERADMINUI); break;
547 }
548 if (flag >= 0)
549 {
550 *result = flag ? VARIANT_TRUE : VARIANT_FALSE;
551 return S_OK;
552 }
553
554 return S_FALSE;
555}
556
557
558// *** IObjectSafety methods ***
559HRESULT STDMETHODCALLTYPE CShellDispatch::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions)
560{
561 TRACE("(%p, %s, %p, %p)\n", this, wine_dbgstr_guid(&riid), pdwSupportedOptions, pdwEnabledOptions);
562 return E_NOTIMPL;
563}
564
565HRESULT STDMETHODCALLTYPE CShellDispatch::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, DWORD dwEnabledOptions)
566{
567 TRACE("(%p, %s, %lu, %lu)\n", this, wine_dbgstr_guid(&riid), dwOptionSetMask, dwEnabledOptions);
568 return E_NOTIMPL;
569}
570
571
572// *** IObjectWithSite methods ***
573HRESULT STDMETHODCALLTYPE CShellDispatch::SetSite(IUnknown *pUnkSite)
574{
575 TRACE("(%p, %p)\n", this, pUnkSite);
576 return E_NOTIMPL;
577}
578
579HRESULT STDMETHODCALLTYPE CShellDispatch::GetSite(REFIID riid, PVOID *ppvSite)
580{
581 TRACE("(%p, %s, %p)\n", this, wine_dbgstr_guid(&riid), ppvSite);
582 return E_NOTIMPL;
583}
584
585HRESULT WINAPI CShellDispatch_Constructor(REFIID riid, LPVOID * ppvOut)
586{
587 return ShellObjectCreatorInit<CShellDispatch>(riid, ppvOut);
588}
589