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 13 WINE_DEFAULT_DEBUG_CHANNEL(shell); 14 15 16 EXTERN_C DWORD WINAPI SHGetRestriction(LPCWSTR lpSubKey, LPCWSTR lpSubName, LPCWSTR lpValue); 17 18 static 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 24 CShellDispatch::CShellDispatch() 25 { 26 } 27 28 CShellDispatch::~CShellDispatch() 29 { 30 } 31 32 HRESULT CShellDispatch::Initialize() 33 { 34 return S_OK; 35 } 36 37 // *** IShellDispatch methods *** 38 HRESULT 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 51 HRESULT 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 64 HRESULT 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 78 HRESULT 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 101 static BOOL is_optional_argument(const VARIANT *arg) 102 { 103 return V_VT(arg) == VT_ERROR && V_ERROR(arg) == DISP_E_PARAMNOTFOUND; 104 } 105 106 HRESULT 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 129 HRESULT 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 135 static 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 151 static 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 162 HRESULT STDMETHODCALLTYPE CShellDispatch::Open(VARIANT vDir) 163 { 164 TRACE("(%p, %s)\n", this, debugstr_variant(&vDir)); 165 return OpenFolder(vDir); 166 } 167 168 HRESULT STDMETHODCALLTYPE CShellDispatch::Explore(VARIANT vDir) 169 { 170 TRACE("(%p, %s)\n", this, debugstr_variant(&vDir)); 171 return OpenFolder(vDir, L"explore"); 172 } 173 174 HRESULT STDMETHODCALLTYPE CShellDispatch::MinimizeAll() 175 { 176 TRACE("(%p)\n", this); 177 return PostTrayCommand(TRAYCMD_MINIMIZE_ALL); 178 } 179 180 HRESULT STDMETHODCALLTYPE CShellDispatch::UndoMinimizeALL() 181 { 182 TRACE("(%p)\n", this); 183 return PostTrayCommand(TRAYCMD_RESTORE_ALL); 184 } 185 186 HRESULT STDMETHODCALLTYPE CShellDispatch::FileRun() 187 { 188 TRACE("(%p)\n", this); 189 return PostTrayCommand(TRAYCMD_RUN_DIALOG); 190 } 191 192 HRESULT STDMETHODCALLTYPE CShellDispatch::CascadeWindows() 193 { 194 TRACE("(%p)\n", this); 195 return PostTrayCommand(TRAYCMD_CASCADE); 196 } 197 198 HRESULT STDMETHODCALLTYPE CShellDispatch::TileVertically() 199 { 200 TRACE("(%p)\n", this); 201 return PostTrayCommand(TRAYCMD_TILE_V); 202 } 203 204 HRESULT STDMETHODCALLTYPE CShellDispatch::TileHorizontally() 205 { 206 TRACE("(%p)\n", this); 207 return PostTrayCommand(TRAYCMD_TILE_H); 208 } 209 210 HRESULT STDMETHODCALLTYPE CShellDispatch::ShutdownWindows() 211 { 212 ExitWindowsDialog(NULL); 213 return S_OK; 214 } 215 216 HRESULT STDMETHODCALLTYPE CShellDispatch::Suspend() 217 { 218 TRACE("(%p)\n", this); 219 return E_NOTIMPL; 220 } 221 222 HRESULT STDMETHODCALLTYPE CShellDispatch::EjectPC() 223 { 224 TRACE("(%p)\n", this); 225 return E_NOTIMPL; 226 } 227 228 HRESULT STDMETHODCALLTYPE CShellDispatch::SetTime() 229 { 230 TRACE("(%p)\n", this); 231 return PostTrayCommand(TRAYCMD_DATE_AND_TIME); 232 } 233 234 HRESULT STDMETHODCALLTYPE CShellDispatch::TrayProperties() 235 { 236 TRACE("(%p)\n", this); 237 return PostTrayCommand(TRAYCMD_TASKBAR_PROPERTIES); 238 } 239 240 HRESULT STDMETHODCALLTYPE CShellDispatch::Help() 241 { 242 TRACE("(%p)\n", this); 243 return PostTrayCommand(TRAYCMD_HELP_AND_SUPPORT); 244 } 245 246 HRESULT STDMETHODCALLTYPE CShellDispatch::FindFiles() 247 { 248 TRACE("(%p)\n", this); 249 return PostTrayCommand(TRAYCMD_SEARCH_FILES); 250 } 251 252 HRESULT STDMETHODCALLTYPE CShellDispatch::FindComputer() 253 { 254 TRACE("(%p)\n", this); 255 return PostTrayCommand(TRAYCMD_SEARCH_COMPUTERS); 256 } 257 258 HRESULT STDMETHODCALLTYPE CShellDispatch::RefreshMenu() 259 { 260 TRACE("(%p)\n", this); 261 return E_NOTIMPL; 262 } 263 264 HRESULT 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 *** 272 HRESULT 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 282 HRESULT 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 313 HRESULT 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 319 HRESULT 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 349 static 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 363 static 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 407 HRESULT 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 417 HRESULT 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 427 HRESULT 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 471 HRESULT 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 484 HRESULT 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 *** 492 HRESULT 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 508 HRESULT STDMETHODCALLTYPE CShellDispatch::WindowsSecurity() 509 { 510 TRACE("(%p)\n", this); 511 return PostTrayCommand(IDM_SECURITY); 512 } 513 514 HRESULT STDMETHODCALLTYPE CShellDispatch::ToggleDesktop() 515 { 516 TRACE("(%p)\n", this); 517 return PostTrayCommand(TRAYCMD_TOGGLE_DESKTOP); 518 } 519 520 HRESULT 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 529 HRESULT 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 *** 559 HRESULT 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 565 HRESULT 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 *** 573 HRESULT STDMETHODCALLTYPE CShellDispatch::SetSite(IUnknown *pUnkSite) 574 { 575 TRACE("(%p, %p)\n", this, pUnkSite); 576 return E_NOTIMPL; 577 } 578 579 HRESULT 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 585 HRESULT WINAPI CShellDispatch_Constructor(REFIID riid, LPVOID * ppvOut) 586 { 587 return ShellObjectCreatorInit<CShellDispatch>(riid, ppvOut); 588 } 589 590