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