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