Reactos
at master 589 lines 16 kB view raw
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