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