1 /*
2  * Shell Desktop
3  *
4  * Copyright 2008 Thomas Bluemel
5  * Copyright 2020 Katayama Hirofumi MZ
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "shelldesktop.h"
23 
24 // Support for multiple monitors is disabled till LVM_SETWORKAREAS gets implemented
25 #ifdef MULTIMONITOR_SUPPORT
26 #include <atlcoll.h>
27 #endif
28 
29 #include <dbt.h>
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(desktop);
32 
33 static const WCHAR szProgmanClassName[]  = L"Progman";
34 static const WCHAR szProgmanWindowName[] = L"Program Manager";
35 
36 class CDesktopBrowser :
37     public CWindowImpl<CDesktopBrowser, CWindow, CFrameWinTraits>,
38     public CComObjectRootEx<CComMultiThreadModelNoCS>,
39     public IShellBrowser,
40     public IShellBrowserService,
41     public IServiceProvider
42 {
43 private:
44     HACCEL m_hAccel;
45     HWND m_hWndShellView;
46     CComPtr<IShellDesktopTray> m_Tray;
47     CComPtr<IShellView>        m_ShellView;
48 
49     CComPtr<IOleWindow>        m_ChangeNotifyServer;
50     HWND                       m_hwndChangeNotifyServer;
51     DWORD m_dwDrives;
52 
53     LRESULT _NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam);
54     HRESULT _Resize();
55 
56 public:
57     CDesktopBrowser();
58     ~CDesktopBrowser();
59     HRESULT Initialize(IShellDesktopTray *ShellDeskx);
60 
61     // *** IOleWindow methods ***
62     STDMETHOD(GetWindow)(HWND *lphwnd) override;
63     STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode) override;
64 
65     // *** IShellBrowser methods ***
66     STDMETHOD(InsertMenusSB)(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) override;
67     STDMETHOD(SetMenuSB)(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject) override;
68     STDMETHOD(RemoveMenusSB)(HMENU hmenuShared) override;
69     STDMETHOD(SetStatusTextSB)(LPCOLESTR pszStatusText) override;
70     STDMETHOD(EnableModelessSB)(BOOL fEnable) override;
71     STDMETHOD(TranslateAcceleratorSB)(MSG *pmsg, WORD wID) override;
72     STDMETHOD(BrowseObject)(LPCITEMIDLIST pidl, UINT wFlags) override;
73     STDMETHOD(GetViewStateStream)(DWORD grfMode, IStream **ppStrm) override;
74     STDMETHOD(GetControlWindow)(UINT id, HWND *lphwnd) override;
75     STDMETHOD(SendControlMsg)(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret) override;
76     STDMETHOD(QueryActiveShellView)(struct IShellView **ppshv) override;
77     STDMETHOD(OnViewWindowActive)(struct IShellView *ppshv) override;
78     STDMETHOD(SetToolbarItems)(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags) override;
79 
80     // *** IShellBrowserService methods ***
81     STDMETHOD(GetPropertyBag)(long flags, REFIID riid, void **ppv) override;
82 
83     // *** IBrowserService2 methods (fake for now) ***
84     inline void SetTopBrowser() const {}
85 
86     // *** IServiceProvider methods ***
87     STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppvObject) override;
88 
89     // message handlers
90     LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
91     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
92     LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
93     LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
94     LRESULT OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
95     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
96     LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
97     LRESULT OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
98     LRESULT OnDeviceChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
99     LRESULT OnShowOptionsDlg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
100     LRESULT OnSaveState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
101 
102 DECLARE_WND_CLASS_EX(szProgmanClassName, CS_DBLCLKS, COLOR_DESKTOP)
103 
104 BEGIN_MSG_MAP(CBaseBar)
105     MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
106     MESSAGE_HANDLER(WM_SIZE, OnSize)
107     MESSAGE_HANDLER(WM_SYSCOLORCHANGE, OnSettingChange)
108     MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
109     MESSAGE_HANDLER(WM_CLOSE, OnClose)
110     MESSAGE_HANDLER(WM_EXPLORER_OPEN_NEW_WINDOW, OnOpenNewWindow)
111     MESSAGE_HANDLER(WM_COMMAND, OnCommand)
112     MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
113     MESSAGE_HANDLER(WM_DESKTOP_GET_CNOTIFY_SERVER, OnGetChangeNotifyServer)
114     MESSAGE_HANDLER(WM_DEVICECHANGE, OnDeviceChange)
115     MESSAGE_HANDLER(WM_PROGMAN_OPENSHELLSETTINGS, OnShowOptionsDlg)
116     MESSAGE_HANDLER(WM_PROGMAN_SAVESTATE, OnSaveState)
117 END_MSG_MAP()
118 
119 BEGIN_COM_MAP(CDesktopBrowser)
120     COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
121     COM_INTERFACE_ENTRY_IID(IID_IShellBrowser, IShellBrowser)
122     COM_INTERFACE_ENTRY_IID(IID_IShellBrowserService, IShellBrowserService)
123     COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProvider)
124 END_COM_MAP()
125 };
126 
127 CDesktopBrowser::CDesktopBrowser():
128     m_hAccel(NULL),
129     m_hWndShellView(NULL),
130     m_hwndChangeNotifyServer(NULL),
131     m_dwDrives(::GetLogicalDrives())
132 {
133     SetTopBrowser();
134 }
135 
136 CDesktopBrowser::~CDesktopBrowser()
137 {
138     if (m_ShellView.p != NULL && m_hWndShellView != NULL)
139     {
140         m_ShellView->DestroyViewWindow();
141     }
142 
143     if (m_hwndChangeNotifyServer)
144     {
145         ::DestroyWindow(m_hwndChangeNotifyServer);
146     }
147 }
148 
149 #ifdef MULTIMONITOR_SUPPORT
150 BOOL CALLBACK MonitorEnumProc(
151   _In_ HMONITOR hMonitor,
152   _In_ HDC      hdcMonitor,
153   _In_ LPRECT   lprcMonitor,
154   _In_ LPARAM   dwData
155 )
156 {
157     CAtlList<RECT> *list = (CAtlList<RECT>*)dwData;
158     MONITORINFO MonitorInfo;
159     MonitorInfo.cbSize = sizeof(MonitorInfo);
160     if (::GetMonitorInfoW(hMonitor, &MonitorInfo))
161     {
162         list->AddTail(MonitorInfo.rcWork);
163     }
164 
165     return TRUE;
166 }
167 #endif
168 
169 HRESULT CDesktopBrowser::_Resize()
170 {
171     RECT rcNewSize;
172 
173 #ifdef MULTIMONITOR_SUPPORT
174 
175     UINT cMonitors = GetSystemMetrics(SM_CMONITORS);
176     if (cMonitors == 1)
177     {
178         SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0);
179     }
180     else
181     {
182         SetRect(&rcNewSize,
183                 GetSystemMetrics(SM_XVIRTUALSCREEN),
184                 GetSystemMetrics(SM_YVIRTUALSCREEN),
185                 GetSystemMetrics(SM_XVIRTUALSCREEN) + GetSystemMetrics(SM_CXVIRTUALSCREEN),
186                 GetSystemMetrics(SM_YVIRTUALSCREEN) + GetSystemMetrics(SM_CYVIRTUALSCREEN));
187     }
188 
189     ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
190     ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
191 
192     if (cMonitors != 1)
193     {
194         CAtlList<RECT> list;
195         EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&list);
196         RECT* prcWorkAreas = new RECT[list.GetCount()];
197         int i = 0;
198         for (POSITION it = list.GetHeadPosition(); it; list.GetNext(it))
199             prcWorkAreas[i++] = list.GetAt(it);
200 
201         HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL);
202 
203         ::SendMessageW(hwndListView, LVM_SETWORKAREAS , i, (LPARAM)prcWorkAreas);
204     }
205 
206 #else
207      SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcNewSize, 0);
208     ::MoveWindow(m_hWnd, rcNewSize.left, rcNewSize.top, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
209     ::MoveWindow(m_hWndShellView, 0, 0, rcNewSize.right - rcNewSize.left, rcNewSize.bottom - rcNewSize.top, TRUE);
210 
211 #endif
212     return S_OK;
213 }
214 
215 HRESULT CDesktopBrowser::Initialize(IShellDesktopTray *ShellDesk)
216 {
217     CComPtr<IShellFolder> psfDesktop;
218     HRESULT hRet;
219     hRet = SHGetDesktopFolder(&psfDesktop);
220     if (FAILED_UNEXPECTEDLY(hRet))
221         return hRet;
222 
223     m_Tray = ShellDesk;
224 
225     Create(NULL, NULL, szProgmanWindowName, WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_TOOLWINDOW);
226     if (!m_hWnd)
227         return E_FAIL;
228 
229     CSFV csfv = {sizeof(CSFV), psfDesktop};
230     hRet = SHCreateShellFolderViewEx(&csfv, &m_ShellView);
231     if (FAILED_UNEXPECTEDLY(hRet))
232         return hRet;
233 
234     m_Tray->RegisterDesktopWindow(m_hWnd);
235     if (FAILED_UNEXPECTEDLY(hRet))
236         return hRet;
237 
238     BOOL fHideIcons = SHELL_GetSetting(SSF_HIDEICONS, fHideIcons);
239     FOLDERSETTINGS fs;
240     RECT rcShellView = {0,0,0,0};
241     fs.ViewMode = FVM_ICON;
242     fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT |
243                 FWF_AUTOARRANGE | (fHideIcons ? FWF_NOICONS : 0);
244     hRet = m_ShellView->CreateViewWindow(NULL, &fs, (IShellBrowser *)this, &rcShellView, &m_hWndShellView);
245     if (FAILED_UNEXPECTEDLY(hRet))
246         return hRet;
247 
248     _Resize();
249 
250     HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL);
251 
252     m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_DESKBROWSER));
253 
254 #if 1
255     /* A Windows8+ specific hack */
256     ::ShowWindow(m_hWndShellView, SW_SHOW);
257     ::ShowWindow(hwndListView, SW_SHOW);
258 #endif
259     ShowWindow(SW_SHOW);
260     UpdateWindow();
261 
262     return hRet;
263 }
264 
265 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *lphwnd)
266 {
267     if (lphwnd == NULL)
268         return E_POINTER;
269     *lphwnd = m_hWnd;
270     return S_OK;
271 }
272 
273 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode)
274 {
275     return E_NOTIMPL;
276 }
277 
278 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
279 {
280     return E_NOTIMPL;
281 }
282 
283 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject)
284 {
285     return E_NOTIMPL;
286 }
287 
288 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared)
289 {
290     return E_NOTIMPL;
291 }
292 
293 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText)
294 {
295     return E_NOTIMPL;
296 }
297 
298 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable)
299 {
300     return E_NOTIMPL;
301 }
302 
303 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
304 {
305     if (!::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg))
306         return S_FALSE;
307     return S_OK;
308 }
309 
310 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
311 {
312     /*
313      * We should use IShellWindows interface here in order to attempt to
314      * find an open shell window that shows the requested pidl and activate it
315      */
316 
317     DWORD dwFlags = ((wFlags & SBSP_EXPLOREMODE) != 0) ? SH_EXPLORER_CMDLINE_FLAG_E : 0;
318     return SHOpenNewFrame(ILClone(pidl), NULL, 0, dwFlags);
319 }
320 
321 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm)
322 {
323     return E_NOTIMPL;
324 }
325 
326 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd)
327 {
328     if (lphwnd == NULL)
329         return E_POINTER;
330     return E_NOTIMPL;
331 }
332 
333 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret)
334 {
335     if (pret == NULL)
336         return E_POINTER;
337     return E_NOTIMPL;
338 }
339 
340 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv)
341 {
342     if (ppshv == NULL)
343         return E_POINTER;
344     *ppshv = m_ShellView;
345     if (*ppshv != NULL)
346         (*ppshv)->AddRef();
347 
348     return S_OK;
349 }
350 
351 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv)
352 {
353     return E_NOTIMPL;
354 }
355 
356 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
357 {
358     return E_NOTIMPL;
359 }
360 
361 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetPropertyBag(long flags, REFIID riid, void **ppv)
362 {
363     ITEMIDLIST deskpidl = {};
364     return SHGetViewStatePropertyBag(&deskpidl, L"Desktop", flags | SHGVSPB_ROAM, riid, ppv);
365 }
366 
367 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
368 {
369     /* FIXME - handle guidService (SID_STopLevelBrowser for IShellBrowserService etc) */
370     return QueryInterface(riid, ppv);
371 }
372 
373 LRESULT CDesktopBrowser::_NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam)
374 {
375     HWND hWndTray;
376     HRESULT hRet;
377 
378     hRet = m_Tray->GetTrayWindow(&hWndTray);
379     if (SUCCEEDED(hRet))
380         ::PostMessageW(hWndTray, uMsg, wParam, lParam);
381 
382     return 0;
383 }
384 
385 LRESULT CDesktopBrowser::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
386 {
387     switch (LOWORD(wParam))
388     {
389         case FCIDM_DESKBROWSER_CLOSE:
390             return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
391         case FCIDM_DESKBROWSER_FOCUS:
392             if (GetKeyState(VK_SHIFT))
393                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 0xFFFFFFFF);
394             else
395                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 1);
396         case FCIDM_DESKBROWSER_SEARCH:
397             SHFindFiles(NULL, NULL);
398             break;
399         case FCIDM_DESKBROWSER_REFRESH:
400             if (m_ShellView)
401                 m_ShellView->Refresh();
402             break;
403     }
404 
405     return 0;
406 }
407 
408 
409 LRESULT CDesktopBrowser::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
410 {
411     return (LRESULT)PaintDesktop((HDC)wParam);
412 }
413 
414 LRESULT CDesktopBrowser::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
415 {
416     if (wParam == SIZE_MINIMIZED)
417     {
418         /* Hey, we're the desktop!!! */
419         ::ShowWindow(m_hWnd, SW_RESTORE);
420     }
421 
422     ::InvalidateRect(m_hWndShellView, NULL, TRUE);
423 
424     return 0;
425 }
426 
427 LRESULT CDesktopBrowser::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
428 {
429     if (uMsg == WM_SETTINGCHANGE /* == WM_WININICHANGE */ &&
430         lstrcmpiW((LPCWSTR)lParam, L"Environment") == 0)
431     {
432         LPVOID lpEnvironment;
433         RegenerateUserEnvironment(&lpEnvironment, TRUE);
434     }
435 
436     if (m_hWndShellView)
437     {
438         /* Forward the message */
439         ::SendMessageW(m_hWndShellView, uMsg, wParam, lParam);
440     }
441 
442     if (uMsg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA && m_hWndShellView != NULL)
443     {
444         _Resize();
445     }
446 
447     return 0;
448 }
449 
450 LRESULT CDesktopBrowser::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
451 {
452     return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
453 }
454 
455 LRESULT CDesktopBrowser::OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
456 {
457     TRACE("Proxy Desktop message 1035 received.\n");
458     SHOnCWMCommandLine((HANDLE)lParam);
459     return 0;
460 }
461 
462 LRESULT CDesktopBrowser::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
463 {
464     ::SetFocus(m_hWndShellView);
465     return 0;
466 }
467 
468 // Message WM_DESKTOP_GET_CNOTIFY_SERVER: Get or create the change notification server.
469 //   wParam: BOOL bCreate; The flag whether it creates or not.
470 //   lParam: Ignored.
471 //   return: The window handle of the server window.
472 LRESULT CDesktopBrowser::OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
473 {
474     BOOL bCreate = (BOOL)wParam;
475     if (bCreate && !::IsWindow(m_hwndChangeNotifyServer))
476     {
477         HRESULT hres = CChangeNotifyServer_CreateInstance(IID_PPV_ARG(IOleWindow, &m_ChangeNotifyServer));
478         if (FAILED_UNEXPECTEDLY(hres))
479             return NULL;
480 
481         hres = m_ChangeNotifyServer->GetWindow(&m_hwndChangeNotifyServer);
482         if (FAILED_UNEXPECTEDLY(hres))
483             return NULL;
484     }
485     return (LRESULT)m_hwndChangeNotifyServer;
486 }
487 
488 // Detect DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE
489 LRESULT CDesktopBrowser::OnDeviceChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
490 {
491     if (wParam != DBT_DEVICEARRIVAL && wParam != DBT_DEVICEREMOVECOMPLETE)
492         return 0;
493 
494     DWORD dwDrives = ::GetLogicalDrives();
495     for (INT iDrive = 0; iDrive <= 'Z' - 'A'; ++iDrive)
496     {
497         WCHAR szPath[MAX_PATH];
498         DWORD dwBit = (1 << iDrive);
499         if (!(m_dwDrives & dwBit) && (dwDrives & dwBit)) // The drive is added
500         {
501             PathBuildRootW(szPath, iDrive);
502             SHChangeNotify(SHCNE_DRIVEADD, SHCNF_PATHW, szPath, NULL);
503         }
504         else if ((m_dwDrives & dwBit) && !(dwDrives & dwBit)) // The drive is removed
505         {
506             PathBuildRootW(szPath, iDrive);
507             SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW, szPath, NULL);
508         }
509     }
510 
511     m_dwDrives = dwDrives;
512     return 0;
513 }
514 
515 extern VOID WINAPI ShowFolderOptionsDialog(UINT Page, BOOL Async);
516 
517 LRESULT CDesktopBrowser::OnShowOptionsDlg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
518 {
519     switch (wParam)
520     {
521         case 0:
522 #if (NTDDI_VERSION >= NTDDI_VISTA)
523         case 2:
524         case 7:
525 #endif
526             ShowFolderOptionsDialog((UINT)(UINT_PTR)wParam, TRUE);
527             break;
528         case 1:
529             _NotifyTray(WM_COMMAND, TRAYCMD_TASKBAR_PROPERTIES, 0);
530             break;
531     }
532     return 0;
533 }
534 
535 LRESULT CDesktopBrowser::OnSaveState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
536 {
537     if (m_ShellView && !SHRestricted(REST_NOSAVESET))
538         m_ShellView->SaveViewState();
539     return 0;
540 }
541 
542 HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv)
543 {
544     return ShellObjectCreatorInit<CDesktopBrowser, IShellDesktopTray*>(Tray, riid, ppv);
545 }
546 
547 /*************************************************************************
548  * SHCreateDesktop            [SHELL32.200]
549  *
550  */
551 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *Tray)
552 {
553     if (Tray == NULL)
554     {
555         SetLastError(ERROR_INVALID_PARAMETER);
556         return NULL;
557     }
558 
559     CComPtr<IShellBrowser> Browser;
560     HRESULT hr = CDesktopBrowser_CreateInstance(Tray, IID_PPV_ARG(IShellBrowser, &Browser));
561     if (FAILED_UNEXPECTEDLY(hr))
562         return NULL;
563 
564     return static_cast<HANDLE>(Browser.Detach());
565 }
566 
567 /*************************************************************************
568  * SHCreateDesktop            [SHELL32.201]
569  *
570  */
571 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop)
572 {
573     if (hDesktop == NULL)
574     {
575         SetLastError(ERROR_INVALID_PARAMETER);
576         return FALSE;
577     }
578 
579     MSG Msg;
580     BOOL bRet;
581 
582     CComPtr<IShellBrowser> browser;
583     CComPtr<IShellView> shellView;
584 
585     browser.Attach(static_cast<IShellBrowser*>(hDesktop));
586     HRESULT hr = browser->QueryActiveShellView(&shellView);
587     if (FAILED_UNEXPECTEDLY(hr))
588         return FALSE;
589 
590     while ((bRet = ::GetMessageW(&Msg, NULL, 0, 0)) != 0)
591     {
592         if (bRet != -1)
593         {
594             if (shellView->TranslateAcceleratorW(&Msg) != S_OK)
595             {
596                 ::TranslateMessage(&Msg);
597                 ::DispatchMessageW(&Msg);
598             }
599         }
600     }
601 
602     return TRUE;
603 }
604 
605 /*************************************************************************
606  *  SHIsTempDisplayMode [SHELL32.724]
607  *
608  * Is the current display settings temporary?
609  */
610 EXTERN_C BOOL WINAPI SHIsTempDisplayMode(VOID)
611 {
612     TRACE("\n");
613 
614     if (GetSystemMetrics(SM_REMOTESESSION) || GetSystemMetrics(SM_REMOTECONTROL))
615         return FALSE;
616 
617     DEVMODEW DevMode;
618     ZeroMemory(&DevMode, sizeof(DevMode));
619     DevMode.dmSize = sizeof(DevMode);
620 
621     if (!EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &DevMode))
622         return FALSE;
623 
624     if (!DevMode.dmPelsWidth || !DevMode.dmPelsHeight)
625         return FALSE;
626 
627     HDC hDC = GetDC(NULL);
628     DWORD cxWidth = GetDeviceCaps(hDC, HORZRES);
629     DWORD cyHeight = GetDeviceCaps(hDC, VERTRES);
630     ReleaseDC(NULL, hDC);
631 
632     return (cxWidth != DevMode.dmPelsWidth || cyHeight != DevMode.dmPelsHeight);
633 }
634