xref: /reactos/base/shell/explorer/taskswnd.cpp (revision 139a3d66)
1 /*
2  * ReactOS Explorer
3  *
4  * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "precomp.h"
22 #include <commoncontrols.h>
23 
24 /* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
25    5 seconds */
26 #define DUMP_TASKS  0
27 #define DEBUG_SHELL_HOOK 0
28 
29 #define MAX_TASKS_COUNT (0x7FFF)
30 #define TASK_ITEM_ARRAY_ALLOC   64
31 
32 const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
33 const WCHAR szRunningApps[] = L"Running Applications";
34 
35 #if DEBUG_SHELL_HOOK
36 const struct {
37     INT msg;
38     LPCWSTR msg_name;
39 } hshell_msg [] = {
40         { HSHELL_WINDOWCREATED, L"HSHELL_WINDOWCREATED" },
41         { HSHELL_WINDOWDESTROYED, L"HSHELL_WINDOWDESTROYED" },
42         { HSHELL_ACTIVATESHELLWINDOW, L"HSHELL_ACTIVATESHELLWINDOW" },
43         { HSHELL_WINDOWACTIVATED, L"HSHELL_WINDOWACTIVATED" },
44         { HSHELL_GETMINRECT, L"HSHELL_GETMINRECT" },
45         { HSHELL_REDRAW, L"HSHELL_REDRAW" },
46         { HSHELL_TASKMAN, L"HSHELL_TASKMAN" },
47         { HSHELL_LANGUAGE, L"HSHELL_LANGUAGE" },
48         { HSHELL_SYSMENU, L"HSHELL_SYSMENU" },
49         { HSHELL_ENDTASK, L"HSHELL_ENDTASK" },
50         { HSHELL_ACCESSIBILITYSTATE, L"HSHELL_ACCESSIBILITYSTATE" },
51         { HSHELL_APPCOMMAND, L"HSHELL_APPCOMMAND" },
52         { HSHELL_WINDOWREPLACED, L"HSHELL_WINDOWREPLACED" },
53         { HSHELL_WINDOWREPLACING, L"HSHELL_WINDOWREPLACING" },
54         { HSHELL_RUDEAPPACTIVATED, L"HSHELL_RUDEAPPACTIVATED" },
55 };
56 #endif
57 
58 typedef struct _TASK_GROUP
59 {
60     /* We have to use a linked list instead of an array so we don't have to
61        update all pointers to groups in the task item array when removing
62        groups. */
63     struct _TASK_GROUP *Next;
64 
65     DWORD dwTaskCount;
66     DWORD dwProcessId;
67     INT Index;
68     union
69     {
70         DWORD dwFlags;
71         struct
72         {
73 
74             DWORD IsCollapsed : 1;
75         };
76     };
77 } TASK_GROUP, *PTASK_GROUP;
78 
79 typedef struct _TASK_ITEM
80 {
81     HWND hWnd;
82     PTASK_GROUP Group;
83     INT Index;
84     INT IconIndex;
85 
86     union
87     {
88         DWORD dwFlags;
89         struct
90         {
91 
92             /* IsFlashing is TRUE when the task bar item should be flashing. */
93             DWORD IsFlashing : 1;
94 
95             /* RenderFlashed is only TRUE if the task bar item should be
96                drawn with a flash. */
97             DWORD RenderFlashed : 1;
98         };
99     };
100 } TASK_ITEM, *PTASK_ITEM;
101 
102 
103 class CHardErrorThread
104 {
105     DWORD m_ThreadId;
106     HANDLE m_hThread;
107     LONG m_bThreadRunning;
108     DWORD m_Status;
109     DWORD m_dwType;
110     CStringW m_Title;
111     CStringW m_Text;
112 public:
113 
114     CHardErrorThread():
115         m_ThreadId(0),
116         m_hThread(NULL),
117         m_bThreadRunning(FALSE),
118         m_Status(NULL),
119         m_dwType(NULL)
120     {
121     }
122 
123     ~CHardErrorThread()
124     {
125         if (m_bThreadRunning)
126         {
127             /* Try to unstuck Show */
128             PostThreadMessage(m_ThreadId, WM_QUIT, 0, 0);
129             DWORD ret = WaitForSingleObject(m_hThread, 3*1000);
130             if (ret == WAIT_TIMEOUT)
131                 TerminateThread(m_hThread, 0);
132             CloseHandle(m_hThread);
133         }
134     }
135 
136     HRESULT ThreadProc()
137     {
138         HRESULT hr;
139         CComPtr<IUserNotification> pnotification;
140 
141         hr = OleInitialize(NULL);
142         if (FAILED_UNEXPECTEDLY(hr))
143             return hr;
144 
145         hr = CoCreateInstance(CLSID_UserNotification,
146                               NULL,
147                               CLSCTX_INPROC_SERVER,
148                               IID_PPV_ARG(IUserNotification, &pnotification));
149         if (FAILED_UNEXPECTEDLY(hr))
150             return hr;
151 
152         hr = pnotification->SetBalloonInfo(m_Title, m_Text, NIIF_WARNING);
153         if (FAILED_UNEXPECTEDLY(hr))
154             return hr;
155 
156         hr = pnotification->SetIconInfo(NULL, NULL);
157         if (FAILED_UNEXPECTEDLY(hr))
158             return hr;
159 
160         /* Show will block until the balloon closes */
161         hr = pnotification->Show(NULL, 0);
162         if (FAILED_UNEXPECTEDLY(hr))
163             return hr;
164 
165         return S_OK;
166     }
167 
168     static DWORD CALLBACK s_HardErrorThreadProc(IN OUT LPVOID lpParameter)
169     {
170         CHardErrorThread* pThis = reinterpret_cast<CHardErrorThread*>(lpParameter);
171         pThis->ThreadProc();
172         CloseHandle(pThis->m_hThread);
173         OleUninitialize();
174         InterlockedExchange(&pThis->m_bThreadRunning, FALSE);
175         return 0;
176     }
177 
178     void StartThread(PBALLOON_HARD_ERROR_DATA pData)
179     {
180         BOOL bIsRunning = InterlockedExchange(&m_bThreadRunning, TRUE);
181 
182         /* Ignore the new message if we are already showing one */
183         if (bIsRunning)
184             return;
185 
186         m_Status = pData->Status;
187         m_dwType = pData->dwType;
188         m_Title = (PWCHAR)((ULONG_PTR)pData + pData->TitleOffset);
189         m_Text = (PWCHAR)((ULONG_PTR)pData + pData->MessageOffset);
190         m_hThread = CreateThread(NULL, 0, s_HardErrorThreadProc, this, 0, &m_ThreadId);
191         if (!m_hThread)
192         {
193             m_bThreadRunning = FALSE;
194             CloseHandle(m_hThread);
195         }
196     }
197 };
198 
199 class CTaskToolbar :
200     public CWindowImplBaseT< CToolbar<TASK_ITEM>, CControlWinTraits >
201 {
202 public:
203     INT UpdateTbButtonSpacing(IN BOOL bHorizontal, IN BOOL bThemed, IN UINT uiRows = 0, IN UINT uiBtnsPerLine = 0)
204     {
205         TBMETRICS tbm;
206 
207         tbm.cbSize = sizeof(tbm);
208         tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING;
209 
210         tbm.cxBarPad = tbm.cyBarPad = 0;
211 
212         if (bThemed)
213         {
214             tbm.cxButtonSpacing = 0;
215             tbm.cyButtonSpacing = 0;
216         }
217         else
218         {
219             if (bHorizontal || uiBtnsPerLine > 1)
220                 tbm.cxButtonSpacing = (3 * GetSystemMetrics(SM_CXEDGE) / 2);
221             else
222                 tbm.cxButtonSpacing = 0;
223 
224             if (!bHorizontal || uiRows > 1)
225                 tbm.cyButtonSpacing = (3 * GetSystemMetrics(SM_CYEDGE) / 2);
226             else
227                 tbm.cyButtonSpacing = 0;
228         }
229 
230         SetMetrics(&tbm);
231 
232         return tbm.cxButtonSpacing;
233     }
234 
235     VOID BeginUpdate()
236     {
237         SetRedraw(FALSE);
238     }
239 
240     VOID EndUpdate()
241     {
242         SendMessageW(WM_SETREDRAW, TRUE);
243         InvalidateRect(NULL, TRUE);
244     }
245 
246     BOOL SetButtonCommandId(IN INT iButtonIndex, IN INT iCommandId)
247     {
248         TBBUTTONINFO tbbi;
249 
250         tbbi.cbSize = sizeof(tbbi);
251         tbbi.dwMask = TBIF_BYINDEX | TBIF_COMMAND;
252         tbbi.idCommand = iCommandId;
253 
254         return SetButtonInfo(iButtonIndex, &tbbi) != 0;
255     }
256 
257     LRESULT OnNcHitTestToolbar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
258     {
259         POINT pt;
260 
261         /* See if the mouse is on a button */
262         pt.x = GET_X_LPARAM(lParam);
263         pt.y = GET_Y_LPARAM(lParam);
264         ScreenToClient(&pt);
265 
266         INT index = HitTest(&pt);
267         if (index < 0)
268         {
269             /* Make the control appear to be transparent outside of any buttons */
270             return HTTRANSPARENT;
271         }
272 
273         bHandled = FALSE;
274         return 0;
275     }
276 
277 public:
278     BEGIN_MSG_MAP(CNotifyToolbar)
279         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTestToolbar)
280     END_MSG_MAP()
281 
282     BOOL Initialize(HWND hWndParent)
283     {
284         DWORD styles = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |
285             TBSTYLE_TOOLTIPS | TBSTYLE_WRAPABLE | TBSTYLE_LIST | TBSTYLE_TRANSPARENT |
286             CCS_TOP | CCS_NORESIZE | CCS_NODIVIDER;
287 
288         HWND toolbar = CToolbar::Create(hWndParent, styles);
289         SetDrawTextFlags(DT_NOPREFIX, DT_NOPREFIX);
290         return SubclassWindow(toolbar);
291     }
292 };
293 
294 class CTaskSwitchWnd :
295     public CComCoClass<CTaskSwitchWnd>,
296     public CComObjectRootEx<CComMultiThreadModelNoCS>,
297     public CWindowImpl < CTaskSwitchWnd, CWindow, CControlWinTraits >,
298     public IOleWindow
299 {
300     CTaskToolbar m_TaskBar;
301 
302     CComPtr<ITrayWindow> m_Tray;
303 
304     UINT m_ShellHookMsg;
305 
306     WORD m_TaskItemCount;
307     WORD m_AllocatedTaskItems;
308 
309     PTASK_GROUP m_TaskGroups;
310     PTASK_ITEM m_TaskItems;
311     PTASK_ITEM m_ActiveTaskItem;
312 
313     HTHEME m_Theme;
314     UINT m_ButtonsPerLine;
315     WORD m_ButtonCount;
316 
317     HIMAGELIST m_ImageList;
318 
319     BOOL m_IsGroupingEnabled;
320     BOOL m_IsDestroying;
321 
322     SIZE m_ButtonSize;
323 
324     UINT m_uHardErrorMsg;
325     CHardErrorThread m_HardErrorThread;
326 
327 public:
328     CTaskSwitchWnd() :
329         m_ShellHookMsg(NULL),
330         m_TaskItemCount(0),
331         m_AllocatedTaskItems(0),
332         m_TaskGroups(NULL),
333         m_TaskItems(NULL),
334         m_ActiveTaskItem(NULL),
335         m_Theme(NULL),
336         m_ButtonsPerLine(0),
337         m_ButtonCount(0),
338         m_ImageList(NULL),
339         m_IsGroupingEnabled(FALSE),
340         m_IsDestroying(FALSE)
341     {
342         ZeroMemory(&m_ButtonSize, sizeof(m_ButtonSize));
343         m_uHardErrorMsg = RegisterWindowMessageW(L"HardError");
344     }
345     virtual ~CTaskSwitchWnd() { }
346 
347     INT GetWndTextFromTaskItem(IN PTASK_ITEM TaskItem, LPWSTR szBuf, DWORD cchBuf)
348     {
349         /* Get the window text without sending a message so we don't hang if an
350            application isn't responding! */
351         return InternalGetWindowText(TaskItem->hWnd, szBuf, cchBuf);
352     }
353 
354 
355 #if DUMP_TASKS != 0
356     VOID DumpTasks()
357     {
358         PTASK_GROUP CurrentGroup;
359         PTASK_ITEM CurrentTaskItem, LastTaskItem;
360 
361         TRACE("Tasks dump:\n");
362         if (m_IsGroupingEnabled)
363         {
364             CurrentGroup = m_TaskGroups;
365             while (CurrentGroup != NULL)
366             {
367                 TRACE("- Group PID: 0x%p Tasks: %d Index: %d\n", CurrentGroup->dwProcessId, CurrentGroup->dwTaskCount, CurrentGroup->Index);
368 
369                 CurrentTaskItem = m_TaskItems;
370                 LastTaskItem = CurrentTaskItem + m_TaskItemCount;
371                 while (CurrentTaskItem != LastTaskItem)
372                 {
373                     if (CurrentTaskItem->Group == CurrentGroup)
374                     {
375                         TRACE("  + Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
376                     }
377                     CurrentTaskItem++;
378                 }
379 
380                 CurrentGroup = CurrentGroup->Next;
381             }
382 
383             CurrentTaskItem = m_TaskItems;
384             LastTaskItem = CurrentTaskItem + m_TaskItemCount;
385             while (CurrentTaskItem != LastTaskItem)
386             {
387                 if (CurrentTaskItem->Group == NULL)
388                 {
389                     TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
390                 }
391                 CurrentTaskItem++;
392             }
393         }
394         else
395         {
396             CurrentTaskItem = m_TaskItems;
397             LastTaskItem = CurrentTaskItem + m_TaskItemCount;
398             while (CurrentTaskItem != LastTaskItem)
399             {
400                 TRACE("- Task hwnd: 0x%p Index: %d\n", CurrentTaskItem->hWnd, CurrentTaskItem->Index);
401                 CurrentTaskItem++;
402             }
403         }
404     }
405 #endif
406 
407     VOID UpdateIndexesAfter(IN INT iIndex, BOOL bInserted)
408     {
409         PTASK_GROUP CurrentGroup;
410         PTASK_ITEM CurrentTaskItem, LastTaskItem;
411         INT NewIndex;
412 
413         int offset = bInserted ? +1 : -1;
414 
415         if (m_IsGroupingEnabled)
416         {
417             /* Update all affected groups */
418             CurrentGroup = m_TaskGroups;
419             while (CurrentGroup != NULL)
420             {
421                 if (CurrentGroup->IsCollapsed &&
422                     CurrentGroup->Index >= iIndex)
423                 {
424                     /* Update the toolbar buttons */
425                     NewIndex = CurrentGroup->Index + offset;
426                     if (m_TaskBar.SetButtonCommandId(CurrentGroup->Index + offset, NewIndex))
427                     {
428                         CurrentGroup->Index = NewIndex;
429                     }
430                     else
431                         CurrentGroup->Index = -1;
432                 }
433 
434                 CurrentGroup = CurrentGroup->Next;
435             }
436         }
437 
438         /* Update all affected task items */
439         CurrentTaskItem = m_TaskItems;
440         LastTaskItem = CurrentTaskItem + m_TaskItemCount;
441         while (CurrentTaskItem != LastTaskItem)
442         {
443             CurrentGroup = CurrentTaskItem->Group;
444             if (CurrentGroup != NULL)
445             {
446                 if (!CurrentGroup->IsCollapsed &&
447                     CurrentTaskItem->Index >= iIndex)
448                 {
449                     goto UpdateTaskItemBtn;
450                 }
451             }
452             else if (CurrentTaskItem->Index >= iIndex)
453             {
454             UpdateTaskItemBtn:
455                 /* Update the toolbar buttons */
456                 NewIndex = CurrentTaskItem->Index + offset;
457                 if (m_TaskBar.SetButtonCommandId(CurrentTaskItem->Index + offset, NewIndex))
458                 {
459                     CurrentTaskItem->Index = NewIndex;
460                 }
461                 else
462                     CurrentTaskItem->Index = -1;
463             }
464 
465             CurrentTaskItem++;
466         }
467     }
468 
469 
470     INT UpdateTaskGroupButton(IN PTASK_GROUP TaskGroup)
471     {
472         ASSERT(TaskGroup->Index >= 0);
473 
474         /* FIXME: Implement */
475 
476         return TaskGroup->Index;
477     }
478 
479     VOID ExpandTaskGroup(IN PTASK_GROUP TaskGroup)
480     {
481         ASSERT(TaskGroup->dwTaskCount > 0);
482         ASSERT(TaskGroup->IsCollapsed);
483         ASSERT(TaskGroup->Index >= 0);
484 
485         /* FIXME: Implement */
486     }
487 
488     HICON GetWndIcon(HWND hwnd)
489     {
490         HICON hIcon = 0;
491 
492         SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
493         if (hIcon)
494             return hIcon;
495 
496         SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
497         if (hIcon)
498             return hIcon;
499 
500         SendMessageTimeout(hwnd, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR) &hIcon);
501         if (hIcon)
502             return hIcon;
503 
504         hIcon = (HICON) GetClassLongPtr(hwnd, GCL_HICONSM);
505         if (hIcon)
506             return hIcon;
507 
508         hIcon = (HICON) GetClassLongPtr(hwnd, GCL_HICON);
509 
510         return hIcon;
511     }
512 
513     INT UpdateTaskItemButton(IN PTASK_ITEM TaskItem)
514     {
515         TBBUTTONINFO tbbi = { 0 };
516         HICON icon;
517         WCHAR windowText[255];
518 
519         ASSERT(TaskItem->Index >= 0);
520 
521         tbbi.cbSize = sizeof(tbbi);
522         tbbi.dwMask = TBIF_BYINDEX | TBIF_STATE | TBIF_TEXT | TBIF_IMAGE;
523         tbbi.fsState = TBSTATE_ENABLED;
524         if (m_ActiveTaskItem == TaskItem)
525             tbbi.fsState |= TBSTATE_CHECKED;
526 
527         if (TaskItem->RenderFlashed)
528             tbbi.fsState |= TBSTATE_MARKED;
529 
530         /* Check if we're updating a button that is the last one in the
531            line. If so, we need to set the TBSTATE_WRAP flag! */
532         if (!m_Tray->IsHorizontal() || (m_ButtonsPerLine != 0 &&
533             (TaskItem->Index + 1) % m_ButtonsPerLine == 0))
534         {
535             tbbi.fsState |= TBSTATE_WRAP;
536         }
537 
538         if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
539         {
540             tbbi.pszText = windowText;
541         }
542 
543         icon = GetWndIcon(TaskItem->hWnd);
544         if (!icon)
545             icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
546         TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, TaskItem->IconIndex, icon);
547         tbbi.iImage = TaskItem->IconIndex;
548 
549         if (!m_TaskBar.SetButtonInfo(TaskItem->Index, &tbbi))
550         {
551             TaskItem->Index = -1;
552             return -1;
553         }
554 
555         TRACE("Updated button %d for hwnd 0x%p\n", TaskItem->Index, TaskItem->hWnd);
556         return TaskItem->Index;
557     }
558 
559     VOID RemoveIcon(IN PTASK_ITEM TaskItem)
560     {
561         TBBUTTONINFO tbbi;
562         PTASK_ITEM currentTaskItem, LastItem;
563 
564         if (TaskItem->IconIndex == -1)
565             return;
566 
567         tbbi.cbSize = sizeof(tbbi);
568         tbbi.dwMask = TBIF_IMAGE;
569 
570         currentTaskItem = m_TaskItems;
571         LastItem = currentTaskItem + m_TaskItemCount;
572         while (currentTaskItem != LastItem)
573         {
574             if (currentTaskItem->IconIndex > TaskItem->IconIndex)
575             {
576                 currentTaskItem->IconIndex--;
577                 tbbi.iImage = currentTaskItem->IconIndex;
578 
579                 m_TaskBar.SetButtonInfo(currentTaskItem->Index, &tbbi);
580             }
581             currentTaskItem++;
582         }
583 
584         ImageList_Remove(m_ImageList, TaskItem->IconIndex);
585     }
586 
587     PTASK_ITEM FindLastTaskItemOfGroup(
588         IN PTASK_GROUP TaskGroup  OPTIONAL,
589         IN PTASK_ITEM NewTaskItem  OPTIONAL)
590     {
591         PTASK_ITEM TaskItem, LastTaskItem, FoundTaskItem = NULL;
592         DWORD dwTaskCount;
593 
594         ASSERT(m_IsGroupingEnabled);
595 
596         TaskItem = m_TaskItems;
597         LastTaskItem = TaskItem + m_TaskItemCount;
598 
599         dwTaskCount = (TaskGroup != NULL ? TaskGroup->dwTaskCount : MAX_TASKS_COUNT);
600 
601         ASSERT(dwTaskCount > 0);
602 
603         while (TaskItem != LastTaskItem)
604         {
605             if (TaskItem->Group == TaskGroup)
606             {
607                 if ((NewTaskItem != NULL && TaskItem != NewTaskItem) || NewTaskItem == NULL)
608                 {
609                     FoundTaskItem = TaskItem;
610                 }
611 
612                 if (--dwTaskCount == 0)
613                 {
614                     /* We found the last task item in the group! */
615                     break;
616                 }
617             }
618 
619             TaskItem++;
620         }
621 
622         return FoundTaskItem;
623     }
624 
625     INT CalculateTaskItemNewButtonIndex(IN PTASK_ITEM TaskItem)
626     {
627         PTASK_GROUP TaskGroup;
628         PTASK_ITEM LastTaskItem;
629 
630         /* NOTE: This routine assumes that the group is *not* collapsed! */
631 
632         TaskGroup = TaskItem->Group;
633         if (m_IsGroupingEnabled)
634         {
635             if (TaskGroup != NULL)
636             {
637                 ASSERT(TaskGroup->Index < 0);
638                 ASSERT(!TaskGroup->IsCollapsed);
639 
640                 if (TaskGroup->dwTaskCount > 1)
641                 {
642                     LastTaskItem = FindLastTaskItemOfGroup(TaskGroup, TaskItem);
643                     if (LastTaskItem != NULL)
644                     {
645                         /* Since the group is expanded the task items must have an index */
646                         ASSERT(LastTaskItem->Index >= 0);
647 
648                         return LastTaskItem->Index + 1;
649                     }
650                 }
651             }
652             else
653             {
654                 /* Find the last NULL group button. NULL groups are added at the end of the
655                    task item list when grouping is enabled */
656                 LastTaskItem = FindLastTaskItemOfGroup(NULL, TaskItem);
657                 if (LastTaskItem != NULL)
658                 {
659                     ASSERT(LastTaskItem->Index >= 0);
660 
661                     return LastTaskItem->Index + 1;
662                 }
663             }
664         }
665 
666         return m_ButtonCount;
667     }
668 
669     INT AddTaskItemButton(IN OUT PTASK_ITEM TaskItem)
670     {
671         WCHAR windowText[255];
672         TBBUTTON tbBtn = { 0 };
673         INT iIndex;
674         HICON icon;
675 
676         if (TaskItem->Index >= 0)
677         {
678             return UpdateTaskItemButton(TaskItem);
679         }
680 
681         if (TaskItem->Group != NULL &&
682             TaskItem->Group->IsCollapsed)
683         {
684             /* The task group is collapsed, we only need to update the group button */
685             return UpdateTaskGroupButton(TaskItem->Group);
686         }
687 
688         icon = GetWndIcon(TaskItem->hWnd);
689         if (!icon)
690             icon = static_cast<HICON>(LoadImageW(NULL, MAKEINTRESOURCEW(OIC_SAMPLE), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
691         TaskItem->IconIndex = ImageList_ReplaceIcon(m_ImageList, -1, icon);
692 
693         tbBtn.iBitmap = TaskItem->IconIndex;
694         tbBtn.fsState = TBSTATE_ENABLED | TBSTATE_ELLIPSES;
695         tbBtn.fsStyle = BTNS_CHECK | BTNS_NOPREFIX | BTNS_SHOWTEXT;
696         tbBtn.dwData = TaskItem->Index;
697 
698         if (GetWndTextFromTaskItem(TaskItem, windowText, _countof(windowText)) > 0)
699         {
700             tbBtn.iString = (DWORD_PTR) windowText;
701         }
702 
703         /* Find out where to insert the new button */
704         iIndex = CalculateTaskItemNewButtonIndex(TaskItem);
705         ASSERT(iIndex >= 0);
706         tbBtn.idCommand = iIndex;
707 
708         m_TaskBar.BeginUpdate();
709 
710         if (m_TaskBar.InsertButton(iIndex, &tbBtn))
711         {
712             UpdateIndexesAfter(iIndex, TRUE);
713 
714             TRACE("Added button %d for hwnd 0x%p\n", iIndex, TaskItem->hWnd);
715 
716             TaskItem->Index = iIndex;
717             m_ButtonCount++;
718 
719             /* Update button sizes and fix the button wrapping */
720             UpdateButtonsSize(TRUE);
721             return iIndex;
722         }
723 
724         m_TaskBar.EndUpdate();
725 
726         return -1;
727     }
728 
729     BOOL DeleteTaskItemButton(IN OUT PTASK_ITEM TaskItem)
730     {
731         PTASK_GROUP TaskGroup;
732         INT iIndex;
733 
734         TaskGroup = TaskItem->Group;
735 
736         if (TaskItem->Index >= 0)
737         {
738             if ((TaskGroup != NULL && !TaskGroup->IsCollapsed) ||
739                 TaskGroup == NULL)
740             {
741                 m_TaskBar.BeginUpdate();
742 
743                 RemoveIcon(TaskItem);
744                 iIndex = TaskItem->Index;
745                 if (m_TaskBar.DeleteButton(iIndex))
746                 {
747                     TaskItem->Index = -1;
748                     m_ButtonCount--;
749 
750                     UpdateIndexesAfter(iIndex, FALSE);
751 
752                     /* Update button sizes and fix the button wrapping */
753                     UpdateButtonsSize(TRUE);
754                     return TRUE;
755                 }
756 
757                 m_TaskBar.EndUpdate();
758             }
759         }
760 
761         return FALSE;
762     }
763 
764     PTASK_GROUP AddToTaskGroup(IN HWND hWnd)
765     {
766         DWORD dwProcessId;
767         PTASK_GROUP TaskGroup, *PrevLink;
768 
769         if (!GetWindowThreadProcessId(hWnd,
770             &dwProcessId))
771         {
772             TRACE("Cannot get process id of hwnd 0x%p\n", hWnd);
773             return NULL;
774         }
775 
776         /* Try to find an existing task group */
777         TaskGroup = m_TaskGroups;
778         PrevLink = &m_TaskGroups;
779         while (TaskGroup != NULL)
780         {
781             if (TaskGroup->dwProcessId == dwProcessId)
782             {
783                 TaskGroup->dwTaskCount++;
784                 return TaskGroup;
785             }
786 
787             PrevLink = &TaskGroup->Next;
788             TaskGroup = TaskGroup->Next;
789         }
790 
791         /* Allocate a new task group */
792         TaskGroup = (PTASK_GROUP) HeapAlloc(hProcessHeap,
793             HEAP_ZERO_MEMORY,
794             sizeof(*TaskGroup));
795         if (TaskGroup != NULL)
796         {
797             TaskGroup->dwTaskCount = 1;
798             TaskGroup->dwProcessId = dwProcessId;
799             TaskGroup->Index = -1;
800 
801             /* Add the task group to the list */
802             *PrevLink = TaskGroup;
803         }
804 
805         return TaskGroup;
806     }
807 
808     VOID RemoveTaskFromTaskGroup(IN OUT PTASK_ITEM TaskItem)
809     {
810         PTASK_GROUP TaskGroup, CurrentGroup, *PrevLink;
811 
812         TaskGroup = TaskItem->Group;
813         if (TaskGroup != NULL)
814         {
815             DWORD dwNewTaskCount = --TaskGroup->dwTaskCount;
816             if (dwNewTaskCount == 0)
817             {
818                 /* Find the previous pointer in the chain */
819                 CurrentGroup = m_TaskGroups;
820                 PrevLink = &m_TaskGroups;
821                 while (CurrentGroup != TaskGroup)
822                 {
823                     PrevLink = &CurrentGroup->Next;
824                     CurrentGroup = CurrentGroup->Next;
825                 }
826 
827                 /* Remove the group from the list */
828                 ASSERT(TaskGroup == CurrentGroup);
829                 *PrevLink = TaskGroup->Next;
830 
831                 /* Free the task group */
832                 HeapFree(hProcessHeap,
833                     0,
834                     TaskGroup);
835             }
836             else if (TaskGroup->IsCollapsed &&
837                 TaskGroup->Index >= 0)
838             {
839                 if (dwNewTaskCount > 1)
840                 {
841                     /* FIXME: Check if we should expand the group */
842                     /* Update the task group button */
843                     UpdateTaskGroupButton(TaskGroup);
844                 }
845                 else
846                 {
847                     /* Expand the group of one task button to a task button */
848                     ExpandTaskGroup(TaskGroup);
849                 }
850             }
851         }
852     }
853 
854     PTASK_ITEM FindTaskItem(IN HWND hWnd)
855     {
856         PTASK_ITEM TaskItem, LastItem;
857 
858         TaskItem = m_TaskItems;
859         LastItem = TaskItem + m_TaskItemCount;
860         while (TaskItem != LastItem)
861         {
862             if (TaskItem->hWnd == hWnd)
863                 return TaskItem;
864 
865             TaskItem++;
866         }
867 
868         return NULL;
869     }
870 
871     PTASK_ITEM FindOtherTaskItem(IN HWND hWnd)
872     {
873         PTASK_ITEM LastItem, TaskItem;
874         PTASK_GROUP TaskGroup;
875         DWORD dwProcessId;
876 
877         if (!GetWindowThreadProcessId(hWnd, &dwProcessId))
878         {
879             return NULL;
880         }
881 
882         /* Try to find another task that belongs to the same
883            process as the given window */
884         TaskItem = m_TaskItems;
885         LastItem = TaskItem + m_TaskItemCount;
886         while (TaskItem != LastItem)
887         {
888             TaskGroup = TaskItem->Group;
889             if (TaskGroup != NULL)
890             {
891                 if (TaskGroup->dwProcessId == dwProcessId)
892                     return TaskItem;
893             }
894             else
895             {
896                 DWORD dwProcessIdTask;
897 
898                 if (GetWindowThreadProcessId(TaskItem->hWnd,
899                     &dwProcessIdTask) &&
900                     dwProcessIdTask == dwProcessId)
901                 {
902                     return TaskItem;
903                 }
904             }
905 
906             TaskItem++;
907         }
908 
909         return NULL;
910     }
911 
912     PTASK_ITEM AllocTaskItem()
913     {
914         if (m_TaskItemCount >= MAX_TASKS_COUNT)
915         {
916             /* We need the most significant bit in 16 bit command IDs to indicate whether it
917                is a task group or task item. WM_COMMAND limits command IDs to 16 bits! */
918             return NULL;
919         }
920 
921         ASSERT(m_AllocatedTaskItems >= m_TaskItemCount);
922 
923         if (m_TaskItemCount == 0)
924         {
925             m_TaskItems = (PTASK_ITEM) HeapAlloc(hProcessHeap,
926                 0,
927                 TASK_ITEM_ARRAY_ALLOC * sizeof(*m_TaskItems));
928             if (m_TaskItems != NULL)
929             {
930                 m_AllocatedTaskItems = TASK_ITEM_ARRAY_ALLOC;
931             }
932             else
933                 return NULL;
934         }
935         else if (m_TaskItemCount >= m_AllocatedTaskItems)
936         {
937             PTASK_ITEM NewArray;
938             SIZE_T NewArrayLength, ActiveTaskItemIndex;
939 
940             NewArrayLength = m_AllocatedTaskItems + TASK_ITEM_ARRAY_ALLOC;
941 
942             NewArray = (PTASK_ITEM) HeapReAlloc(hProcessHeap,
943                 0,
944                 m_TaskItems,
945                 NewArrayLength * sizeof(*m_TaskItems));
946             if (NewArray != NULL)
947             {
948                 if (m_ActiveTaskItem != NULL)
949                 {
950                     /* Fixup the ActiveTaskItem pointer */
951                     ActiveTaskItemIndex = m_ActiveTaskItem - m_TaskItems;
952                     m_ActiveTaskItem = NewArray + ActiveTaskItemIndex;
953                 }
954                 m_AllocatedTaskItems = (WORD) NewArrayLength;
955                 m_TaskItems = NewArray;
956             }
957             else
958                 return NULL;
959         }
960 
961         return m_TaskItems + m_TaskItemCount++;
962     }
963 
964     VOID FreeTaskItem(IN OUT PTASK_ITEM TaskItem)
965     {
966         WORD wIndex;
967 
968         if (TaskItem == m_ActiveTaskItem)
969             m_ActiveTaskItem = NULL;
970 
971         wIndex = (WORD) (TaskItem - m_TaskItems);
972         if (wIndex + 1 < m_TaskItemCount)
973         {
974             MoveMemory(TaskItem,
975                 TaskItem + 1,
976                 (m_TaskItemCount - wIndex - 1) * sizeof(*TaskItem));
977         }
978 
979         m_TaskItemCount--;
980     }
981 
982     VOID DeleteTaskItem(IN OUT PTASK_ITEM TaskItem)
983     {
984         if (!m_IsDestroying)
985         {
986             /* Delete the task button from the toolbar */
987             DeleteTaskItemButton(TaskItem);
988         }
989 
990         /* Remove the task from it's group */
991         RemoveTaskFromTaskGroup(TaskItem);
992 
993         /* Free the task item */
994         FreeTaskItem(TaskItem);
995     }
996 
997     VOID CheckActivateTaskItem(IN OUT PTASK_ITEM TaskItem)
998     {
999         PTASK_ITEM CurrentTaskItem;
1000         PTASK_GROUP TaskGroup = NULL;
1001 
1002         CurrentTaskItem = m_ActiveTaskItem;
1003 
1004         if (TaskItem != NULL)
1005             TaskGroup = TaskItem->Group;
1006 
1007         if (m_IsGroupingEnabled &&
1008             TaskGroup != NULL &&
1009             TaskGroup->IsCollapsed)
1010         {
1011             /* FIXME */
1012             return;
1013         }
1014 
1015         if (CurrentTaskItem != NULL)
1016         {
1017             PTASK_GROUP CurrentTaskGroup;
1018 
1019             if (CurrentTaskItem == TaskItem)
1020                 return;
1021 
1022             CurrentTaskGroup = CurrentTaskItem->Group;
1023 
1024             if (m_IsGroupingEnabled &&
1025                 CurrentTaskGroup != NULL &&
1026                 CurrentTaskGroup->IsCollapsed)
1027             {
1028                 if (CurrentTaskGroup == TaskGroup)
1029                     return;
1030 
1031                 /* FIXME */
1032             }
1033             else
1034             {
1035                 m_ActiveTaskItem = NULL;
1036                 if (CurrentTaskItem->Index >= 0)
1037                 {
1038                     UpdateTaskItemButton(CurrentTaskItem);
1039                 }
1040             }
1041         }
1042 
1043         m_ActiveTaskItem = TaskItem;
1044 
1045         if (TaskItem != NULL && TaskItem->Index >= 0)
1046         {
1047             UpdateTaskItemButton(TaskItem);
1048         }
1049         else if (TaskItem == NULL)
1050         {
1051             TRACE("Active TaskItem now NULL\n");
1052         }
1053     }
1054 
1055     PTASK_ITEM FindTaskItemByIndex(IN INT Index)
1056     {
1057         PTASK_ITEM TaskItem, LastItem;
1058 
1059         TaskItem = m_TaskItems;
1060         LastItem = TaskItem + m_TaskItemCount;
1061         while (TaskItem != LastItem)
1062         {
1063             if (TaskItem->Index == Index)
1064                 return TaskItem;
1065 
1066             TaskItem++;
1067         }
1068 
1069         return NULL;
1070     }
1071 
1072     PTASK_GROUP FindTaskGroupByIndex(IN INT Index)
1073     {
1074         PTASK_GROUP CurrentGroup;
1075 
1076         CurrentGroup = m_TaskGroups;
1077         while (CurrentGroup != NULL)
1078         {
1079             if (CurrentGroup->Index == Index)
1080                 break;
1081 
1082             CurrentGroup = CurrentGroup->Next;
1083         }
1084 
1085         return CurrentGroup;
1086     }
1087 
1088     BOOL AddTask(IN HWND hWnd)
1089     {
1090         PTASK_ITEM TaskItem;
1091 
1092         if (!::IsWindow(hWnd) || m_Tray->IsSpecialHWND(hWnd))
1093             return FALSE;
1094 
1095         TaskItem = FindTaskItem(hWnd);
1096         if (TaskItem == NULL)
1097         {
1098             TRACE("Add window 0x%p\n", hWnd);
1099             TaskItem = AllocTaskItem();
1100             if (TaskItem != NULL)
1101             {
1102                 ZeroMemory(TaskItem, sizeof(*TaskItem));
1103                 TaskItem->hWnd = hWnd;
1104                 TaskItem->Index = -1;
1105                 TaskItem->Group = AddToTaskGroup(hWnd);
1106 
1107                 if (!m_IsDestroying)
1108                 {
1109                     AddTaskItemButton(TaskItem);
1110                 }
1111             }
1112         }
1113 
1114         return TaskItem != NULL;
1115     }
1116 
1117     BOOL ActivateTaskItem(IN OUT PTASK_ITEM TaskItem  OPTIONAL)
1118     {
1119         if (TaskItem != NULL)
1120         {
1121             TRACE("Activate window 0x%p on button %d\n", TaskItem->hWnd, TaskItem->Index);
1122         }
1123 
1124         CheckActivateTaskItem(TaskItem);
1125 
1126         return FALSE;
1127     }
1128 
1129     BOOL ActivateTask(IN HWND hWnd)
1130     {
1131         PTASK_ITEM TaskItem;
1132 
1133         if (!hWnd)
1134         {
1135             return ActivateTaskItem(NULL);
1136         }
1137 
1138         TaskItem = FindTaskItem(hWnd);
1139         if (TaskItem == NULL)
1140         {
1141             TaskItem = FindOtherTaskItem(hWnd);
1142         }
1143 
1144         if (TaskItem == NULL)
1145         {
1146             WARN("Activate window 0x%p, could not find task\n", hWnd);
1147             RefreshWindowList();
1148         }
1149 
1150         return ActivateTaskItem(TaskItem);
1151     }
1152 
1153     BOOL DeleteTask(IN HWND hWnd)
1154     {
1155         PTASK_ITEM TaskItem;
1156 
1157         TaskItem = FindTaskItem(hWnd);
1158         if (TaskItem != NULL)
1159         {
1160             TRACE("Delete window 0x%p on button %d\n", hWnd, TaskItem->Index);
1161             DeleteTaskItem(TaskItem);
1162             return TRUE;
1163         }
1164         //else
1165         //TRACE("Failed to delete window 0x%p\n", hWnd);
1166 
1167         return FALSE;
1168     }
1169 
1170     VOID DeleteAllTasks()
1171     {
1172         PTASK_ITEM CurrentTask;
1173 
1174         if (m_TaskItemCount > 0)
1175         {
1176             CurrentTask = m_TaskItems + m_TaskItemCount;
1177             do
1178             {
1179                 DeleteTaskItem(--CurrentTask);
1180             } while (CurrentTask != m_TaskItems);
1181         }
1182     }
1183 
1184     VOID FlashTaskItem(IN OUT PTASK_ITEM TaskItem)
1185     {
1186         TaskItem->RenderFlashed = 1;
1187         UpdateTaskItemButton(TaskItem);
1188     }
1189 
1190     BOOL FlashTask(IN HWND hWnd)
1191     {
1192         PTASK_ITEM TaskItem;
1193 
1194         TaskItem = FindTaskItem(hWnd);
1195         if (TaskItem != NULL)
1196         {
1197             TRACE("Flashing window 0x%p on button %d\n", hWnd, TaskItem->Index);
1198             FlashTaskItem(TaskItem);
1199             return TRUE;
1200         }
1201 
1202         return FALSE;
1203     }
1204 
1205     VOID RedrawTaskItem(IN OUT PTASK_ITEM TaskItem)
1206     {
1207         PTASK_GROUP TaskGroup;
1208 
1209         TaskGroup = TaskItem->Group;
1210         if (m_IsGroupingEnabled && TaskGroup != NULL)
1211         {
1212             if (TaskGroup->IsCollapsed && TaskGroup->Index >= 0)
1213             {
1214                 UpdateTaskGroupButton(TaskGroup);
1215             }
1216             else if (TaskItem->Index >= 0)
1217             {
1218                 goto UpdateTaskItem;
1219             }
1220         }
1221         else if (TaskItem->Index >= 0)
1222         {
1223         UpdateTaskItem:
1224             TaskItem->RenderFlashed = 0;
1225             UpdateTaskItemButton(TaskItem);
1226         }
1227     }
1228 
1229 
1230     BOOL RedrawTask(IN HWND hWnd)
1231     {
1232         PTASK_ITEM TaskItem;
1233 
1234         TaskItem = FindTaskItem(hWnd);
1235         if (TaskItem != NULL)
1236         {
1237             RedrawTaskItem(TaskItem);
1238             return TRUE;
1239         }
1240 
1241         return FALSE;
1242     }
1243 
1244     VOID UpdateButtonsSize(IN BOOL bRedrawDisabled)
1245     {
1246         RECT rcClient;
1247         UINT uiRows, uiMax, uiMin, uiBtnsPerLine, ui;
1248         LONG NewBtnSize;
1249         BOOL Horizontal;
1250 
1251         /* Update the size of the image list if needed */
1252         int cx, cy;
1253         ImageList_GetIconSize(m_ImageList, &cx, &cy);
1254         if (cx != GetSystemMetrics(SM_CXSMICON) || cy != GetSystemMetrics(SM_CYSMICON))
1255         {
1256             ImageList_SetIconSize(m_ImageList, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
1257 
1258             /* SetIconSize removes all icons so we have to reinsert them */
1259             PTASK_ITEM TaskItem = m_TaskItems;
1260             PTASK_ITEM LastTaskItem = m_TaskItems + m_TaskItemCount;
1261             while (TaskItem != LastTaskItem)
1262             {
1263                 TaskItem->IconIndex = -1;
1264                 UpdateTaskItemButton(TaskItem);
1265 
1266                 TaskItem++;
1267             }
1268             m_TaskBar.SetImageList(m_ImageList);
1269         }
1270 
1271         if (GetClientRect(&rcClient) && !IsRectEmpty(&rcClient))
1272         {
1273             if (m_ButtonCount > 0)
1274             {
1275                 Horizontal = m_Tray->IsHorizontal();
1276 
1277                 if (Horizontal)
1278                 {
1279                     TBMETRICS tbm = { 0 };
1280                     tbm.cbSize = sizeof(tbm);
1281                     tbm.dwMask = TBMF_BUTTONSPACING;
1282                     m_TaskBar.GetMetrics(&tbm);
1283 
1284                     if (m_ButtonSize.cy + tbm.cyButtonSpacing != 0)
1285                         uiRows = (rcClient.bottom + tbm.cyButtonSpacing) / (m_ButtonSize.cy + tbm.cyButtonSpacing);
1286                     else
1287                         uiRows = 1;
1288 
1289                     if (uiRows == 0)
1290                         uiRows = 1;
1291 
1292                     uiBtnsPerLine = (m_ButtonCount + uiRows - 1) / uiRows;
1293                 }
1294                 else
1295                 {
1296                     uiBtnsPerLine = 1;
1297                     uiRows = m_ButtonCount;
1298                 }
1299 
1300                 if (!bRedrawDisabled)
1301                     m_TaskBar.BeginUpdate();
1302 
1303                 /* We might need to update the button spacing */
1304                 int cxButtonSpacing = m_TaskBar.UpdateTbButtonSpacing(
1305                     Horizontal, m_Theme != NULL,
1306                     uiRows, uiBtnsPerLine);
1307 
1308                 /* Determine the minimum and maximum width of a button */
1309                 uiMin = GetSystemMetrics(SM_CXSIZE) + (2 * GetSystemMetrics(SM_CXEDGE));
1310                 if (Horizontal)
1311                 {
1312                     uiMax = GetSystemMetrics(SM_CXMINIMIZED);
1313 
1314                     /* Calculate the ideal width and make sure it's within the allowed range */
1315                     NewBtnSize = (rcClient.right - (uiBtnsPerLine * cxButtonSpacing)) / uiBtnsPerLine;
1316 
1317                     if (NewBtnSize < (LONG) uiMin)
1318                         NewBtnSize = uiMin;
1319                     if (NewBtnSize >(LONG)uiMax)
1320                         NewBtnSize = uiMax;
1321 
1322                     /* Recalculate how many buttons actually fit into one line */
1323                     uiBtnsPerLine = rcClient.right / (NewBtnSize + cxButtonSpacing);
1324                     if (uiBtnsPerLine == 0)
1325                         uiBtnsPerLine++;
1326                 }
1327                 else
1328                 {
1329                     NewBtnSize = uiMax = rcClient.right;
1330                 }
1331 
1332                 m_ButtonSize.cx = NewBtnSize;
1333 
1334                 m_ButtonsPerLine = uiBtnsPerLine;
1335 
1336                 for (ui = 0; ui != m_ButtonCount; ui++)
1337                 {
1338                     TBBUTTONINFOW tbbi = { 0 };
1339                     tbbi.cbSize = sizeof(tbbi);
1340                     tbbi.dwMask = TBIF_BYINDEX | TBIF_SIZE | TBIF_STATE;
1341                     tbbi.cx = (INT) NewBtnSize;
1342                     tbbi.fsState = TBSTATE_ENABLED;
1343 
1344                     /* Check if we're updating a button that is the last one in the
1345                        line. If so, we need to set the TBSTATE_WRAP flag! */
1346                     if (Horizontal)
1347                     {
1348                         if ((ui + 1) % uiBtnsPerLine == 0)
1349                             tbbi.fsState |= TBSTATE_WRAP;
1350                     }
1351                     else
1352                     {
1353                         tbbi.fsState |= TBSTATE_WRAP;
1354                     }
1355 
1356                     if (m_ActiveTaskItem != NULL &&
1357                         m_ActiveTaskItem->Index == (INT)ui)
1358                     {
1359                         tbbi.fsState |= TBSTATE_CHECKED;
1360                     }
1361 
1362                     m_TaskBar.SetButtonInfo(ui, &tbbi);
1363                 }
1364             }
1365             else
1366             {
1367                 m_ButtonsPerLine = 0;
1368                 m_ButtonSize.cx = 0;
1369             }
1370         }
1371 
1372         // FIXME: This seems to be enabling redraws prematurely, but moving it to its right place doesn't work!
1373         m_TaskBar.EndUpdate();
1374     }
1375 
1376     BOOL CALLBACK EnumWindowsProc(IN HWND hWnd)
1377     {
1378         /* Only show windows that still exist and are visible and none of explorer's
1379         special windows (such as the desktop or the tray window) */
1380         if (::IsWindow(hWnd) && ::IsWindowVisible(hWnd) &&
1381             !m_Tray->IsSpecialHWND(hWnd))
1382         {
1383             DWORD exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
1384             /* Don't list popup windows and also no tool windows */
1385             if ((::GetWindow(hWnd, GW_OWNER) == NULL || exStyle & WS_EX_APPWINDOW) &&
1386                 !(exStyle & WS_EX_TOOLWINDOW))
1387             {
1388                 TRACE("Adding task for %p...\n", hWnd);
1389                 AddTask(hWnd);
1390             }
1391 
1392         }
1393 
1394         return TRUE;
1395     }
1396 
1397     static BOOL CALLBACK s_EnumWindowsProc(IN HWND hWnd, IN LPARAM lParam)
1398     {
1399         CTaskSwitchWnd * This = (CTaskSwitchWnd *) lParam;
1400 
1401         return This->EnumWindowsProc(hWnd);
1402     }
1403 
1404     BOOL RefreshWindowList()
1405     {
1406         TRACE("Refreshing window list...\n");
1407         /* Add all windows to the toolbar */
1408         return EnumWindows(s_EnumWindowsProc, (LPARAM)this);
1409     }
1410 
1411     LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1412     {
1413         TRACE("OmThemeChanged\n");
1414 
1415         if (m_Theme)
1416             CloseThemeData(m_Theme);
1417 
1418         if (IsThemeActive())
1419             m_Theme = OpenThemeData(m_hWnd, L"TaskBand");
1420         else
1421             m_Theme = NULL;
1422 
1423         return TRUE;
1424     }
1425 
1426     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1427     {
1428         if (!m_TaskBar.Initialize(m_hWnd))
1429             return FALSE;
1430 
1431         SetWindowTheme(m_TaskBar.m_hWnd, L"TaskBand", NULL);
1432 
1433         m_ImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 0, 1000);
1434         m_TaskBar.SetImageList(m_ImageList);
1435 
1436         /* Set proper spacing between buttons */
1437         m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1438 
1439         /* Register the shell hook */
1440         m_ShellHookMsg = RegisterWindowMessageW(L"SHELLHOOK");
1441 
1442         TRACE("ShellHookMsg got assigned number %d\n", m_ShellHookMsg);
1443 
1444         RegisterShellHook(m_hWnd, 3); /* 1 if no NT! We're targeting NT so we don't care! */
1445 
1446         RefreshWindowList();
1447 
1448         /* Recalculate the button size */
1449         UpdateButtonsSize(FALSE);
1450 
1451 #if DUMP_TASKS != 0
1452         SetTimer(hwnd, 1, 5000, NULL);
1453 #endif
1454         return TRUE;
1455     }
1456 
1457     LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1458     {
1459         m_IsDestroying = TRUE;
1460 
1461         /* Unregister the shell hook */
1462         RegisterShellHook(m_hWnd, FALSE);
1463 
1464         CloseThemeData(m_Theme);
1465         DeleteAllTasks();
1466         return TRUE;
1467     }
1468 
1469     BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam)
1470     {
1471         BOOL Ret = FALSE;
1472 
1473         switch (GET_APPCOMMAND_LPARAM(lParam))
1474         {
1475         case APPCOMMAND_BROWSER_SEARCH:
1476             Ret = SHFindFiles(NULL,
1477                 NULL);
1478             break;
1479 
1480         case APPCOMMAND_BROWSER_HOME:
1481         case APPCOMMAND_LAUNCH_MAIL:
1482         default:
1483             TRACE("Shell app command %d unhandled!\n", (INT) GET_APPCOMMAND_LPARAM(lParam));
1484             break;
1485         }
1486 
1487         return Ret;
1488     }
1489 
1490     LRESULT OnShellHook(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1491     {
1492         BOOL Ret = FALSE;
1493 
1494         /* In case the shell hook wasn't registered properly, ignore WM_NULLs*/
1495         if (uMsg == 0)
1496         {
1497             bHandled = FALSE;
1498             return 0;
1499         }
1500 
1501         TRACE("Received shell hook message: wParam=%08lx, lParam=%08lx\n", wParam, lParam);
1502 
1503         switch ((INT) wParam)
1504         {
1505         case HSHELL_APPCOMMAND:
1506             Ret = HandleAppCommand(wParam, lParam);
1507             break;
1508 
1509         case HSHELL_WINDOWCREATED:
1510             AddTask((HWND) lParam);
1511             break;
1512 
1513         case HSHELL_WINDOWDESTROYED:
1514             /* The window still exists! Delay destroying it a bit */
1515             DeleteTask((HWND) lParam);
1516             break;
1517 
1518         case HSHELL_RUDEAPPACTIVATED:
1519         case HSHELL_WINDOWACTIVATED:
1520             ActivateTask((HWND) lParam);
1521             break;
1522 
1523         case HSHELL_FLASH:
1524             FlashTask((HWND) lParam);
1525             break;
1526 
1527         case HSHELL_REDRAW:
1528             RedrawTask((HWND) lParam);
1529             break;
1530 
1531         case HSHELL_TASKMAN:
1532             ::PostMessage(m_Tray->GetHWND(), TWM_OPENSTARTMENU, 0, 0);
1533             break;
1534 
1535         case HSHELL_ACTIVATESHELLWINDOW:
1536         case HSHELL_LANGUAGE:
1537         case HSHELL_SYSMENU:
1538         case HSHELL_ENDTASK:
1539         case HSHELL_ACCESSIBILITYSTATE:
1540         case HSHELL_WINDOWREPLACED:
1541         case HSHELL_WINDOWREPLACING:
1542 
1543         case HSHELL_GETMINRECT:
1544         default:
1545         {
1546 #if DEBUG_SHELL_HOOK
1547             int i, found;
1548             for (i = 0, found = 0; i != _countof(hshell_msg); i++)
1549             {
1550                 if (hshell_msg[i].msg == (INT) wParam)
1551                 {
1552                     TRACE("Shell message %ws unhandled (lParam = 0x%p)!\n", hshell_msg[i].msg_name, lParam);
1553                     found = 1;
1554                     break;
1555                 }
1556             }
1557             if (found)
1558                 break;
1559 #endif
1560             TRACE("Shell message %d unhandled (lParam = 0x%p)!\n", (INT) wParam, lParam);
1561             break;
1562         }
1563         }
1564 
1565         return Ret;
1566     }
1567 
1568     VOID HandleTaskItemClick(IN OUT PTASK_ITEM TaskItem)
1569     {
1570         BOOL bIsMinimized;
1571         BOOL bIsActive;
1572 
1573         if (::IsWindow(TaskItem->hWnd))
1574         {
1575             bIsMinimized = ::IsIconic(TaskItem->hWnd);
1576             bIsActive = (TaskItem == m_ActiveTaskItem);
1577 
1578             TRACE("Active TaskItem %p, selected TaskItem %p\n", m_ActiveTaskItem, TaskItem);
1579             if (m_ActiveTaskItem)
1580                 TRACE("Active TaskItem hWnd=%p, TaskItem hWnd %p\n", m_ActiveTaskItem->hWnd, TaskItem->hWnd);
1581 
1582             TRACE("Valid button clicked. HWND=%p, IsMinimized=%s, IsActive=%s...\n",
1583                 TaskItem->hWnd, bIsMinimized ? "Yes" : "No", bIsActive ? "Yes" : "No");
1584 
1585             if (!bIsMinimized && bIsActive)
1586             {
1587                 ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE);
1588                 TRACE("Valid button clicked. App window Minimized.\n");
1589             }
1590             else
1591             {
1592                 ::SwitchToThisWindow(TaskItem->hWnd, TRUE);
1593                 TRACE("Valid button clicked. App window Restored.\n");
1594             }
1595         }
1596     }
1597 
1598     VOID HandleTaskGroupClick(IN OUT PTASK_GROUP TaskGroup)
1599     {
1600         /* TODO: Show task group menu */
1601     }
1602 
1603     BOOL HandleButtonClick(IN WORD wIndex)
1604     {
1605         PTASK_ITEM TaskItem;
1606         PTASK_GROUP TaskGroup;
1607 
1608         if (m_IsGroupingEnabled)
1609         {
1610             TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1611             if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1612             {
1613                 HandleTaskGroupClick(TaskGroup);
1614                 return TRUE;
1615             }
1616         }
1617 
1618         TaskItem = FindTaskItemByIndex((INT) wIndex);
1619         if (TaskItem != NULL)
1620         {
1621             HandleTaskItemClick(TaskItem);
1622             return TRUE;
1623         }
1624 
1625         return FALSE;
1626     }
1627 
1628 
1629     VOID HandleTaskItemRightClick(IN OUT PTASK_ITEM TaskItem)
1630     {
1631         POINT pt;
1632         GetCursorPos(&pt);
1633 
1634         SetForegroundWindow(TaskItem->hWnd);
1635 
1636         ActivateTask(TaskItem->hWnd);
1637 
1638        /* Wait up to 2 seconds for the window to process the foreground notification. */
1639         DWORD_PTR resultDummy;
1640         if (!SendMessageTimeout(TaskItem->hWnd, WM_NULL, 0, 0, 0, 2000, &resultDummy))
1641             ERR("HandleTaskItemRightClick detected the window was unresponsive for 2 seconds, or was destroyed\n");
1642         if (GetForegroundWindow() != TaskItem->hWnd)
1643             ERR("HandleTaskItemRightClick detected the window did not become foreground\n");
1644 
1645         ::SendMessageW(TaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(pt.x, pt.y));
1646     }
1647 
1648     VOID HandleTaskGroupRightClick(IN OUT PTASK_GROUP TaskGroup)
1649     {
1650         /* TODO: Show task group right click menu */
1651     }
1652 
1653     BOOL HandleButtonRightClick(IN WORD wIndex)
1654     {
1655         PTASK_ITEM TaskItem;
1656         PTASK_GROUP TaskGroup;
1657         if (m_IsGroupingEnabled)
1658         {
1659             TaskGroup = FindTaskGroupByIndex((INT) wIndex);
1660             if (TaskGroup != NULL && TaskGroup->IsCollapsed)
1661             {
1662                 HandleTaskGroupRightClick(TaskGroup);
1663                 return TRUE;
1664             }
1665         }
1666 
1667         TaskItem = FindTaskItemByIndex((INT) wIndex);
1668 
1669         if (TaskItem != NULL)
1670         {
1671             HandleTaskItemRightClick(TaskItem);
1672             return TRUE;
1673         }
1674 
1675         return FALSE;
1676     }
1677 
1678 
1679     LRESULT HandleItemPaint(IN OUT NMTBCUSTOMDRAW *nmtbcd)
1680     {
1681         LRESULT Ret = CDRF_DODEFAULT;
1682         PTASK_GROUP TaskGroup;
1683         PTASK_ITEM TaskItem;
1684 
1685         TaskItem = FindTaskItemByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1686         TaskGroup = FindTaskGroupByIndex((INT) nmtbcd->nmcd.dwItemSpec);
1687         if (TaskGroup == NULL && TaskItem != NULL)
1688         {
1689             ASSERT(TaskItem != NULL);
1690 
1691             if (TaskItem != NULL && ::IsWindow(TaskItem->hWnd))
1692             {
1693                 /* Make the entire button flashing if necessary */
1694                 if (nmtbcd->nmcd.uItemState & CDIS_MARKED)
1695                 {
1696                     Ret = TBCDRF_NOBACKGROUND;
1697                     if (!m_Theme)
1698                     {
1699                         SelectObject(nmtbcd->nmcd.hdc, GetSysColorBrush(COLOR_HIGHLIGHT));
1700                         Rectangle(nmtbcd->nmcd.hdc,
1701                             nmtbcd->nmcd.rc.left,
1702                             nmtbcd->nmcd.rc.top,
1703                             nmtbcd->nmcd.rc.right,
1704                             nmtbcd->nmcd.rc.bottom);
1705                     }
1706                     else
1707                     {
1708                         DrawThemeBackground(m_Theme, nmtbcd->nmcd.hdc, TDP_FLASHBUTTON, 0, &nmtbcd->nmcd.rc, 0);
1709                     }
1710                     nmtbcd->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
1711                     return Ret;
1712                 }
1713             }
1714         }
1715         else if (TaskGroup != NULL)
1716         {
1717             /* FIXME: Implement painting for task groups */
1718         }
1719         return Ret;
1720     }
1721 
1722     LRESULT HandleToolbarNotification(IN const NMHDR *nmh)
1723     {
1724         LRESULT Ret = 0;
1725 
1726         switch (nmh->code)
1727         {
1728         case NM_CUSTOMDRAW:
1729         {
1730             LPNMTBCUSTOMDRAW nmtbcd = (LPNMTBCUSTOMDRAW) nmh;
1731 
1732             switch (nmtbcd->nmcd.dwDrawStage)
1733             {
1734 
1735             case CDDS_ITEMPREPAINT:
1736                 Ret = HandleItemPaint(nmtbcd);
1737                 break;
1738 
1739             case CDDS_PREPAINT:
1740                 Ret = CDRF_NOTIFYITEMDRAW;
1741                 break;
1742 
1743             default:
1744                 Ret = CDRF_DODEFAULT;
1745                 break;
1746             }
1747             break;
1748         }
1749         }
1750 
1751         return Ret;
1752     }
1753 
1754     LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1755     {
1756         HDC hdc = (HDC) wParam;
1757 
1758         if (!IsAppThemed())
1759         {
1760             bHandled = FALSE;
1761             return 0;
1762         }
1763 
1764         RECT rect;
1765         GetClientRect(&rect);
1766         DrawThemeParentBackground(m_hWnd, hdc, &rect);
1767 
1768         return TRUE;
1769     }
1770 
1771     LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1772     {
1773         SIZE szClient;
1774 
1775         szClient.cx = LOWORD(lParam);
1776         szClient.cy = HIWORD(lParam);
1777         if (m_TaskBar.m_hWnd != NULL)
1778         {
1779             m_TaskBar.SetWindowPos(NULL, 0, 0, szClient.cx, szClient.cy, SWP_NOZORDER);
1780 
1781             UpdateButtonsSize(FALSE);
1782         }
1783         return TRUE;
1784     }
1785 
1786     LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1787     {
1788         LRESULT Ret = TRUE;
1789         /* We want the tray window to be draggable everywhere, so make the control
1790         appear transparent */
1791         Ret = DefWindowProc(uMsg, wParam, lParam);
1792         if (Ret != HTVSCROLL && Ret != HTHSCROLL)
1793             Ret = HTTRANSPARENT;
1794         return Ret;
1795     }
1796 
1797     LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1798     {
1799         LRESULT Ret = TRUE;
1800         if (lParam != 0 && (HWND) lParam == m_TaskBar.m_hWnd)
1801         {
1802             HandleButtonClick(LOWORD(wParam));
1803         }
1804         return Ret;
1805     }
1806 
1807     LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1808     {
1809         LRESULT Ret = TRUE;
1810         const NMHDR *nmh = (const NMHDR *) lParam;
1811 
1812         if (nmh->hwndFrom == m_TaskBar.m_hWnd)
1813         {
1814             Ret = HandleToolbarNotification(nmh);
1815         }
1816         return Ret;
1817     }
1818 
1819     LRESULT OnUpdateTaskbarPos(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1820     {
1821         /* Update the button spacing */
1822         m_TaskBar.UpdateTbButtonSpacing(m_Tray->IsHorizontal(), m_Theme != NULL);
1823         return TRUE;
1824     }
1825 
1826     LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1827     {
1828         TaskbarSettings* newSettings = (TaskbarSettings*)lParam;
1829         if (newSettings->bGroupButtons != g_TaskbarSettings.bGroupButtons)
1830         {
1831             g_TaskbarSettings.bGroupButtons = newSettings->bGroupButtons;
1832             m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
1833 
1834             /* Collapse or expand groups if necessary */
1835             RefreshWindowList();
1836             UpdateButtonsSize(FALSE);
1837         }
1838 
1839         return 0;
1840     }
1841 
1842     LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1843     {
1844         LRESULT Ret = 0;
1845         INT_PTR iBtn = -1;
1846 
1847         if (m_TaskBar.m_hWnd != NULL)
1848         {
1849             POINT pt;
1850 
1851             pt.x = GET_X_LPARAM(lParam);
1852             pt.y = GET_Y_LPARAM(lParam);
1853 
1854             ::ScreenToClient(m_TaskBar.m_hWnd, &pt);
1855 
1856             iBtn = m_TaskBar.HitTest(&pt);
1857             if (iBtn >= 0)
1858             {
1859                 HandleButtonRightClick(iBtn);
1860             }
1861         }
1862         if (iBtn < 0)
1863         {
1864             /* Not on a taskbar button, so forward message to tray */
1865             Ret = SendMessage(m_Tray->GetHWND(), uMsg, wParam, lParam);
1866         }
1867         return Ret;
1868     }
1869 
1870     LRESULT OnKludgeItemRect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1871     {
1872         PTASK_ITEM TaskItem = FindTaskItem((HWND) wParam);
1873         if (TaskItem)
1874         {
1875             RECT* prcMinRect = (RECT*) lParam;
1876             RECT rcItem, rcToolbar;
1877             m_TaskBar.GetItemRect(TaskItem->Index, &rcItem);
1878             m_TaskBar.GetWindowRect(&rcToolbar);
1879 
1880             OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
1881 
1882             *prcMinRect = rcItem;
1883             return TRUE;
1884         }
1885         return FALSE;
1886     }
1887 
1888     LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1889     {
1890         return MA_NOACTIVATE;
1891     }
1892 
1893     LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1894     {
1895 #if DUMP_TASKS != 0
1896         switch (wParam)
1897         {
1898         case 1:
1899             DumpTasks();
1900             break;
1901         }
1902 #endif
1903         return TRUE;
1904     }
1905 
1906     LRESULT OnSetFont(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1907     {
1908         return m_TaskBar.SendMessageW(uMsg, wParam, lParam);
1909     }
1910 
1911     LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1912     {
1913         if (wParam == SPI_SETNONCLIENTMETRICS)
1914         {
1915             /*  Don't update the font, this will be done when we get a WM_SETFONT from our parent */
1916             UpdateButtonsSize(FALSE);
1917         }
1918 
1919         return 0;
1920     }
1921 
1922     LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1923     {
1924         PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
1925         if (cpData->dwData == m_uHardErrorMsg)
1926         {
1927             /* A hard error balloon message */
1928             PBALLOON_HARD_ERROR_DATA pData = (PBALLOON_HARD_ERROR_DATA)cpData->lpData;
1929             ERR("Got balloon data 0x%x, 0x%x, '%S', '%S'\n", pData->Status, pData->dwType, (WCHAR*)((ULONG_PTR)pData + pData->TitleOffset), (WCHAR*)((ULONG_PTR)pData + pData->MessageOffset));
1930             if (pData->cbHeaderSize == sizeof(BALLOON_HARD_ERROR_DATA))
1931                 m_HardErrorThread.StartThread(pData);
1932             return TRUE;
1933         }
1934 
1935         return FALSE;
1936     }
1937 
1938     HRESULT Initialize(IN HWND hWndParent, IN OUT ITrayWindow *tray)
1939     {
1940         m_Tray = tray;
1941         m_IsGroupingEnabled = g_TaskbarSettings.bGroupButtons;
1942         Create(hWndParent, 0, szRunningApps, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP);
1943         if (!m_hWnd)
1944             return E_FAIL;
1945         return S_OK;
1946     }
1947 
1948     HRESULT WINAPI GetWindow(HWND* phwnd)
1949     {
1950         if (!phwnd)
1951             return E_INVALIDARG;
1952         *phwnd = m_hWnd;
1953         return S_OK;
1954     }
1955 
1956     HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
1957     {
1958         return E_NOTIMPL;
1959     }
1960 
1961     DECLARE_WND_CLASS_EX(szTaskSwitchWndClass, CS_DBLCLKS, COLOR_3DFACE)
1962 
1963     BEGIN_MSG_MAP(CTaskSwitchWnd)
1964         MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
1965         MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1966         MESSAGE_HANDLER(WM_SIZE, OnSize)
1967         MESSAGE_HANDLER(WM_CREATE, OnCreate)
1968         MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1969         MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
1970         MESSAGE_HANDLER(WM_COMMAND, OnCommand)
1971         MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
1972         MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos)
1973         MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
1974         MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
1975         MESSAGE_HANDLER(WM_TIMER, OnTimer)
1976         MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1977         MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
1978         MESSAGE_HANDLER(m_ShellHookMsg, OnShellHook)
1979         MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
1980         MESSAGE_HANDLER(WM_KLUDGEMINRECT, OnKludgeItemRect)
1981         MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
1982     END_MSG_MAP()
1983 
1984     DECLARE_NOT_AGGREGATABLE(CTaskSwitchWnd)
1985 
1986     DECLARE_PROTECT_FINAL_CONSTRUCT()
1987     BEGIN_COM_MAP(CTaskSwitchWnd)
1988         COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
1989     END_COM_MAP()
1990 };
1991 
1992 HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv)
1993 {
1994     return ShellObjectCreatorInit<CTaskSwitchWnd>(hWndParent, Tray, riid, ppv);
1995 }
1996