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     hRet = psfDesktop->CreateViewObject(m_hWnd, IID_PPV_ARG(IShellView, &m_ShellView));
230     if (FAILED_UNEXPECTEDLY(hRet))
231         return hRet;
232 
233     m_Tray->RegisterDesktopWindow(m_hWnd);
234     if (FAILED_UNEXPECTEDLY(hRet))
235         return hRet;
236 
237     BOOL fHideIcons = SHELL_GetSetting(SSF_HIDEICONS, fHideIcons);
238     FOLDERSETTINGS fs;
239     RECT rcShellView = {0,0,0,0};
240     fs.ViewMode = FVM_ICON;
241     fs.fFlags = FWF_DESKTOP | FWF_NOCLIENTEDGE | FWF_NOSCROLL | FWF_TRANSPARENT |
242                 FWF_AUTOARRANGE | (fHideIcons ? FWF_NOICONS : 0);
243     hRet = m_ShellView->CreateViewWindow(NULL, &fs, (IShellBrowser *)this, &rcShellView, &m_hWndShellView);
244     if (FAILED_UNEXPECTEDLY(hRet))
245         return hRet;
246 
247     _Resize();
248 
249     HWND hwndListView = FindWindowExW(m_hWndShellView, NULL, WC_LISTVIEW, NULL);
250 
251     m_hAccel = LoadAcceleratorsW(shell32_hInstance, MAKEINTRESOURCEW(IDA_DESKBROWSER));
252 
253 #if 1
254     /* A Windows8+ specific hack */
255     ::ShowWindow(m_hWndShellView, SW_SHOW);
256     ::ShowWindow(hwndListView, SW_SHOW);
257 #endif
258     ShowWindow(SW_SHOW);
259     UpdateWindow();
260 
261     return hRet;
262 }
263 
264 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetWindow(HWND *lphwnd)
265 {
266     if (lphwnd == NULL)
267         return E_POINTER;
268     *lphwnd = m_hWnd;
269     return S_OK;
270 }
271 
272 HRESULT STDMETHODCALLTYPE CDesktopBrowser::ContextSensitiveHelp(BOOL fEnterMode)
273 {
274     return E_NOTIMPL;
275 }
276 
277 HRESULT STDMETHODCALLTYPE CDesktopBrowser::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
278 {
279     return E_NOTIMPL;
280 }
281 
282 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuRes, HWND hwndActiveObject)
283 {
284     return E_NOTIMPL;
285 }
286 
287 HRESULT STDMETHODCALLTYPE CDesktopBrowser::RemoveMenusSB(HMENU hmenuShared)
288 {
289     return E_NOTIMPL;
290 }
291 
292 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetStatusTextSB(LPCOLESTR lpszStatusText)
293 {
294     return E_NOTIMPL;
295 }
296 
297 HRESULT STDMETHODCALLTYPE CDesktopBrowser::EnableModelessSB(BOOL fEnable)
298 {
299     return E_NOTIMPL;
300 }
301 
302 HRESULT STDMETHODCALLTYPE CDesktopBrowser::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
303 {
304     if (!::TranslateAcceleratorW(m_hWnd, m_hAccel, lpmsg))
305         return S_FALSE;
306     return S_OK;
307 }
308 
309 HRESULT STDMETHODCALLTYPE CDesktopBrowser::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
310 {
311     /*
312      * We should use IShellWindows interface here in order to attempt to
313      * find an open shell window that shows the requested pidl and activate it
314      */
315 
316     DWORD dwFlags = ((wFlags & SBSP_EXPLOREMODE) != 0) ? SH_EXPLORER_CMDLINE_FLAG_E : 0;
317     return SHOpenNewFrame(ILClone(pidl), NULL, 0, dwFlags);
318 }
319 
320 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetViewStateStream(DWORD grfMode, IStream **ppStrm)
321 {
322     return E_NOTIMPL;
323 }
324 
325 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetControlWindow(UINT id, HWND *lphwnd)
326 {
327     if (lphwnd == NULL)
328         return E_POINTER;
329     return E_NOTIMPL;
330 }
331 
332 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pret)
333 {
334     if (pret == NULL)
335         return E_POINTER;
336     return E_NOTIMPL;
337 }
338 
339 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryActiveShellView(IShellView **ppshv)
340 {
341     if (ppshv == NULL)
342         return E_POINTER;
343     *ppshv = m_ShellView;
344     if (*ppshv != NULL)
345         (*ppshv)->AddRef();
346 
347     return S_OK;
348 }
349 
350 HRESULT STDMETHODCALLTYPE CDesktopBrowser::OnViewWindowActive(IShellView *ppshv)
351 {
352     return E_NOTIMPL;
353 }
354 
355 HRESULT STDMETHODCALLTYPE CDesktopBrowser::SetToolbarItems(LPTBBUTTON lpButtons, UINT nButtons, UINT uFlags)
356 {
357     return E_NOTIMPL;
358 }
359 
360 HRESULT STDMETHODCALLTYPE CDesktopBrowser::GetPropertyBag(long flags, REFIID riid, void **ppv)
361 {
362     ITEMIDLIST deskpidl = {};
363     return SHGetViewStatePropertyBag(&deskpidl, L"Desktop", flags | SHGVSPB_ROAM, riid, ppv);
364 }
365 
366 HRESULT STDMETHODCALLTYPE CDesktopBrowser::QueryService(REFGUID guidService, REFIID riid, PVOID *ppv)
367 {
368     /* FIXME - handle guidService (SID_STopLevelBrowser for IShellBrowserService etc) */
369     return QueryInterface(riid, ppv);
370 }
371 
372 LRESULT CDesktopBrowser::_NotifyTray(UINT uMsg, WPARAM wParam, LPARAM lParam)
373 {
374     HWND hWndTray;
375     HRESULT hRet;
376 
377     hRet = m_Tray->GetTrayWindow(&hWndTray);
378     if (SUCCEEDED(hRet))
379         ::PostMessageW(hWndTray, uMsg, wParam, lParam);
380 
381     return 0;
382 }
383 
384 LRESULT CDesktopBrowser::OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
385 {
386     switch (LOWORD(wParam))
387     {
388         case FCIDM_DESKBROWSER_CLOSE:
389             return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
390         case FCIDM_DESKBROWSER_FOCUS:
391             if (GetKeyState(VK_SHIFT))
392                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 0xFFFFFFFF);
393             else
394                 return _NotifyTray(TWM_CYCLEFOCUS, 1, 1);
395         case FCIDM_DESKBROWSER_SEARCH:
396             SHFindFiles(NULL, NULL);
397             break;
398         case FCIDM_DESKBROWSER_REFRESH:
399             if (m_ShellView)
400                 m_ShellView->Refresh();
401             break;
402     }
403 
404     return 0;
405 }
406 
407 
408 LRESULT CDesktopBrowser::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
409 {
410     return (LRESULT)PaintDesktop((HDC)wParam);
411 }
412 
413 LRESULT CDesktopBrowser::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
414 {
415     if (wParam == SIZE_MINIMIZED)
416     {
417         /* Hey, we're the desktop!!! */
418         ::ShowWindow(m_hWnd, SW_RESTORE);
419     }
420 
421     ::InvalidateRect(m_hWndShellView, NULL, TRUE);
422 
423     return 0;
424 }
425 
426 LRESULT CDesktopBrowser::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
427 {
428     if (uMsg == WM_SETTINGCHANGE /* == WM_WININICHANGE */ &&
429         lstrcmpiW((LPCWSTR)lParam, L"Environment") == 0)
430     {
431         LPVOID lpEnvironment;
432         RegenerateUserEnvironment(&lpEnvironment, TRUE);
433     }
434 
435     if (m_hWndShellView)
436     {
437         /* Forward the message */
438         ::SendMessageW(m_hWndShellView, uMsg, wParam, lParam);
439     }
440 
441     if (uMsg == WM_SETTINGCHANGE && wParam == SPI_SETWORKAREA && m_hWndShellView != NULL)
442     {
443         _Resize();
444     }
445 
446     return 0;
447 }
448 
449 LRESULT CDesktopBrowser::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
450 {
451     return _NotifyTray(TWM_DOEXITWINDOWS, 0, 0);
452 }
453 
454 LRESULT CDesktopBrowser::OnOpenNewWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
455 {
456     TRACE("Proxy Desktop message 1035 received.\n");
457     SHOnCWMCommandLine((HANDLE)lParam);
458     return 0;
459 }
460 
461 LRESULT CDesktopBrowser::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
462 {
463     ::SetFocus(m_hWndShellView);
464     return 0;
465 }
466 
467 // Message WM_DESKTOP_GET_CNOTIFY_SERVER: Get or create the change notification server.
468 //   wParam: BOOL bCreate; The flag whether it creates or not.
469 //   lParam: Ignored.
470 //   return: The window handle of the server window.
471 LRESULT CDesktopBrowser::OnGetChangeNotifyServer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
472 {
473     BOOL bCreate = (BOOL)wParam;
474     if (bCreate && !::IsWindow(m_hwndChangeNotifyServer))
475     {
476         HRESULT hres = CChangeNotifyServer_CreateInstance(IID_PPV_ARG(IOleWindow, &m_ChangeNotifyServer));
477         if (FAILED_UNEXPECTEDLY(hres))
478             return NULL;
479 
480         hres = m_ChangeNotifyServer->GetWindow(&m_hwndChangeNotifyServer);
481         if (FAILED_UNEXPECTEDLY(hres))
482             return NULL;
483     }
484     return (LRESULT)m_hwndChangeNotifyServer;
485 }
486 
487 // Detect DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE
488 LRESULT CDesktopBrowser::OnDeviceChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
489 {
490     if (wParam != DBT_DEVICEARRIVAL && wParam != DBT_DEVICEREMOVECOMPLETE)
491         return 0;
492 
493     DWORD dwDrives = ::GetLogicalDrives();
494     for (INT iDrive = 0; iDrive <= 'Z' - 'A'; ++iDrive)
495     {
496         WCHAR szPath[MAX_PATH];
497         DWORD dwBit = (1 << iDrive);
498         if (!(m_dwDrives & dwBit) && (dwDrives & dwBit)) // The drive is added
499         {
500             PathBuildRootW(szPath, iDrive);
501             SHChangeNotify(SHCNE_DRIVEADD, SHCNF_PATHW, szPath, NULL);
502         }
503         else if ((m_dwDrives & dwBit) && !(dwDrives & dwBit)) // The drive is removed
504         {
505             PathBuildRootW(szPath, iDrive);
506             SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_PATHW, szPath, NULL);
507         }
508     }
509 
510     m_dwDrives = dwDrives;
511     return 0;
512 }
513 
514 extern VOID WINAPI ShowFolderOptionsDialog(UINT Page, BOOL Async);
515 
516 LRESULT CDesktopBrowser::OnShowOptionsDlg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
517 {
518     switch (wParam)
519     {
520         case 0:
521 #if (NTDDI_VERSION >= NTDDI_VISTA)
522         case 2:
523         case 7:
524 #endif
525             ShowFolderOptionsDialog((UINT)(UINT_PTR)wParam, TRUE);
526             break;
527         case 1:
528             _NotifyTray(WM_COMMAND, TRAYCMD_TASKBAR_PROPERTIES, 0);
529             break;
530     }
531     return 0;
532 }
533 
534 LRESULT CDesktopBrowser::OnSaveState(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
535 {
536     if (m_ShellView && !SHRestricted(REST_NOSAVESET))
537         m_ShellView->SaveViewState();
538     return 0;
539 }
540 
541 HRESULT CDesktopBrowser_CreateInstance(IShellDesktopTray *Tray, REFIID riid, void **ppv)
542 {
543     return ShellObjectCreatorInit<CDesktopBrowser, IShellDesktopTray*>(Tray, riid, ppv);
544 }
545 
546 /*************************************************************************
547  * SHCreateDesktop            [SHELL32.200]
548  *
549  */
550 HANDLE WINAPI SHCreateDesktop(IShellDesktopTray *Tray)
551 {
552     if (Tray == NULL)
553     {
554         SetLastError(ERROR_INVALID_PARAMETER);
555         return NULL;
556     }
557 
558     CComPtr<IShellBrowser> Browser;
559     HRESULT hr = CDesktopBrowser_CreateInstance(Tray, IID_PPV_ARG(IShellBrowser, &Browser));
560     if (FAILED_UNEXPECTEDLY(hr))
561         return NULL;
562 
563     return static_cast<HANDLE>(Browser.Detach());
564 }
565 
566 /*************************************************************************
567  * SHCreateDesktop            [SHELL32.201]
568  *
569  */
570 BOOL WINAPI SHDesktopMessageLoop(HANDLE hDesktop)
571 {
572     if (hDesktop == NULL)
573     {
574         SetLastError(ERROR_INVALID_PARAMETER);
575         return FALSE;
576     }
577 
578     MSG Msg;
579     BOOL bRet;
580 
581     CComPtr<IShellBrowser> browser;
582     CComPtr<IShellView> shellView;
583 
584     browser.Attach(static_cast<IShellBrowser*>(hDesktop));
585     HRESULT hr = browser->QueryActiveShellView(&shellView);
586     if (FAILED_UNEXPECTEDLY(hr))
587         return FALSE;
588 
589     while ((bRet = ::GetMessageW(&Msg, NULL, 0, 0)) != 0)
590     {
591         if (bRet != -1)
592         {
593             if (shellView->TranslateAcceleratorW(&Msg) != S_OK)
594             {
595                 ::TranslateMessage(&Msg);
596                 ::DispatchMessageW(&Msg);
597             }
598         }
599     }
600 
601     return TRUE;
602 }
603 
604 /*************************************************************************
605  *  SHIsTempDisplayMode [SHELL32.724]
606  *
607  * Is the current display settings temporary?
608  */
609 EXTERN_C BOOL WINAPI SHIsTempDisplayMode(VOID)
610 {
611     TRACE("\n");
612 
613     if (GetSystemMetrics(SM_REMOTESESSION) || GetSystemMetrics(SM_REMOTECONTROL))
614         return FALSE;
615 
616     DEVMODEW DevMode;
617     ZeroMemory(&DevMode, sizeof(DevMode));
618     DevMode.dmSize = sizeof(DevMode);
619 
620     if (!EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &DevMode))
621         return FALSE;
622 
623     if (!DevMode.dmPelsWidth || !DevMode.dmPelsHeight)
624         return FALSE;
625 
626     HDC hDC = GetDC(NULL);
627     DWORD cxWidth = GetDeviceCaps(hDC, HORZRES);
628     DWORD cyHeight = GetDeviceCaps(hDC, VERTRES);
629     ReleaseDC(NULL, hDC);
630 
631     return (cxWidth != DevMode.dmPelsWidth || cyHeight != DevMode.dmPelsHeight);
632 }
633