1 /*
2  * PROJECT:     ReactOS shell extensions
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/shellext/qcklnch/CISFBand.cpp
5  * PURPOSE:     Quick Launch Toolbar (Taskbar Shell Extension)
6  * PROGRAMMERS: Shriraj Sawant a.k.a SR13 <sr.official@hotmail.com>
7  */
8 
9 #include "shellbars.h"
10 
11 #include <commoncontrols.h>
12 #include <shellapi.h>
13 
14 /*
15 TODO:
16     ** drag and drop support
17     ** tooltips
18     ** handle change notifications
19     ** Fix position of the items context menu
20     ** Implement responding to theme change
21 */
22 
23 //*****************************************************************************************
24 // *** CISFBand ***
25 
26 CISFBand::CISFBand() :
27     m_BandID(0),
28     m_pidl(NULL),
29     m_textFlag(true),
30     m_iconFlag(true),
31     m_QLaunch(false)
32 {
33 }
34 
35 CISFBand::~CISFBand()
36 {
37     CloseDW(0);
38 }
39 
40 // Toolbar
41 /*++
42 * @name CreateSimpleToolbar
43 *
44 * Creates a toolbar and fills it up with buttons for enumerated objects.
45 *
46 * @param hWndParent
47 *        Handle to the parent window, which receives the appropriate messages from child toolbar.
48 *
49 * @return The error code.
50 *
51 *--*/
52 HRESULT CISFBand::CreateSimpleToolbar(HWND hWndParent)
53 {
54     // Declare and initialize local constants.
55     const DWORD buttonStyles = BTNS_AUTOSIZE;
56 
57     // Create the toolbar.
58     m_hWnd = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
59         WS_CHILD | TBSTYLE_FLAT | TBSTYLE_LIST | CCS_NORESIZE | CCS_NODIVIDER, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
60         hWndParent, NULL, 0, NULL);
61     if (m_hWnd == NULL)
62         return E_FAIL;
63 
64     if (!m_textFlag)
65         SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
66 
67     // Set the image list.
68     HIMAGELIST* piml;
69     HRESULT hr = SHGetImageList(SHIL_SMALL, IID_IImageList, (void**)&piml);
70     if (FAILED_UNEXPECTEDLY(hr))
71     {
72         DestroyWindow();
73         return hr;
74     }
75     SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
76 
77     // Enumerate objects
78     CComPtr<IEnumIDList> pEndl;
79     LPITEMIDLIST pidl;
80     STRRET stret;
81     hr = m_pISF->EnumObjects(0, SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &pEndl);
82     if (FAILED_UNEXPECTEDLY(hr))
83     {
84         DestroyWindow();
85         return hr;
86     }
87 
88     for (int i=0; pEndl->Next(1, &pidl, NULL) != S_FALSE; i++)
89     {
90          WCHAR sz[MAX_PATH];
91          int index = SHMapPIDLToSystemImageListIndex(m_pISF, pidl, NULL);
92          hr = m_pISF->GetDisplayNameOf(pidl, SHGDN_NORMAL, &stret);
93          if (FAILED_UNEXPECTEDLY(hr))
94          {
95              StringCchCopyW(sz, MAX_PATH, L"<Unknown-Name>");
96          }
97          else
98              StrRetToBuf(&stret, pidl, sz, _countof(sz));
99 
100          TBBUTTON tb = { MAKELONG(index, 0), i, TBSTATE_ENABLED, buttonStyles,{ 0 }, (DWORD_PTR)pidl, (INT_PTR)sz };
101          SendMessage(m_hWnd, TB_INSERTBUTTONW, i, (LPARAM)&tb);
102     }
103 
104     // Resize the toolbar, and then show it.
105     SendMessage(m_hWnd, TB_AUTOSIZE, 0, 0);
106 
107     return hr;
108 }
109 
110 /*****************************************************************************/
111 
112 // *** IObjectWithSite ***
113     STDMETHODIMP CISFBand::SetSite(IUnknown *pUnkSite)
114     {
115         HRESULT hr;
116         HWND hwndParent;
117 
118         TRACE("CISFBand::SetSite(0x%p)\n", pUnkSite);
119 
120         hr = IUnknown_GetWindow(pUnkSite, &hwndParent);
121         if (FAILED(hr))
122         {
123             TRACE("Querying site window failed: 0x%x\n", hr);
124             return hr;
125         }
126         m_Site = pUnkSite;
127 
128         hr = CreateSimpleToolbar(hwndParent);
129         if (FAILED_UNEXPECTEDLY(hr))
130             return hr;
131 
132         return S_OK;
133     }
134 
135     STDMETHODIMP CISFBand::GetSite(IN REFIID riid, OUT VOID **ppvSite)
136     {
137         TRACE("CISFBand::GetSite(0x%p,0x%p)\n", riid, ppvSite);
138 
139         HRESULT hr;
140         if (m_Site != NULL)
141         {
142             hr = m_Site->QueryInterface(riid, ppvSite);
143             if (FAILED(hr)) return hr;
144         }
145 
146         *ppvSite = NULL;
147         return E_FAIL;
148     }
149 
150 /*****************************************************************************/
151 // *** IDeskBand ***
152     STDMETHODIMP CISFBand::GetWindow(OUT HWND *phwnd)
153     {
154         if (!m_hWnd)
155             return E_FAIL;
156         if (!phwnd)
157             return E_POINTER;
158         *phwnd = m_hWnd;
159 
160         return S_OK;
161     }
162 
163     STDMETHODIMP CISFBand::ContextSensitiveHelp(IN BOOL fEnterMode)
164     {
165         /* FIXME: Implement */
166         return E_NOTIMPL;
167     }
168 
169     STDMETHODIMP CISFBand::ShowDW(IN BOOL bShow)
170     {
171         if (m_hWnd)
172         {
173             ShowWindow(bShow ? SW_SHOW : SW_HIDE);
174             return S_OK;
175         }
176 
177         return E_FAIL;
178     }
179 
180     STDMETHODIMP CISFBand::CloseDW(IN DWORD dwReserved)
181     {
182         if (m_hWnd)
183         {
184             ShowWindow(SW_HIDE);
185 
186             TBBUTTON tb;
187             for (int i = 0; SendMessage(m_hWnd, TB_GETBUTTON, i, (LPARAM)&tb); i++)
188             {
189                 CoTaskMemFree((LPITEMIDLIST)tb.dwData);
190             }
191 
192             DestroyWindow();
193             m_hWnd = NULL;
194             return S_OK;
195         }
196 
197         return E_FAIL;
198     }
199 
200     STDMETHODIMP CISFBand::ResizeBorderDW(LPCRECT prcBorder, IUnknown *punkToolbarSite, BOOL fReserved)
201     {
202         /* No need to implement this method */
203 
204         return E_NOTIMPL;
205     }
206 
207     STDMETHODIMP CISFBand::GetBandInfo(IN DWORD dwBandID, IN DWORD dwViewMode, IN OUT DESKBANDINFO *pdbi)
208     {
209         TRACE("CTaskBand::GetBandInfo(0x%x,0x%x,0x%p) hWnd=0x%p\n", dwBandID, dwViewMode, pdbi, m_hWnd);
210 
211         if (m_hWnd && pdbi)
212         {
213             m_BandID = dwBandID;
214 
215             RECT actualRect;
216             POINTL actualSize;
217             POINTL idealSize;
218             POINTL maxSize;
219             POINTL itemSize;
220 
221             GetWindowRect(&actualRect);
222             actualSize.x = actualRect.right - actualRect.left;
223             actualSize.y = actualRect.bottom - actualRect.top;
224 
225             // Obtain the ideal size, to be used as min and max
226             SendMessageW(m_hWnd, TB_AUTOSIZE, 0, 0);
227             SendMessageW(m_hWnd, TB_GETMAXSIZE, 0, reinterpret_cast<LPARAM>(&maxSize));
228 
229             idealSize = maxSize;
230             SendMessageW(m_hWnd, TB_GETIDEALSIZE, FALSE, reinterpret_cast<LPARAM>(&idealSize));
231 
232             // Obtain the button size, to be used as the integral size
233             DWORD size = SendMessageW(m_hWnd, TB_GETBUTTONSIZE, 0, 0);
234             itemSize.x = GET_X_LPARAM(size);
235             itemSize.y = GET_Y_LPARAM(size);
236 
237             if (pdbi->dwMask & DBIM_MINSIZE)
238             {
239                 if (m_QLaunch)
240                     pdbi->ptMinSize.x = idealSize.x;
241                 else
242                     pdbi->ptMinSize.x = -1;
243                 pdbi->ptMinSize.y = idealSize.y;
244             }
245             if (pdbi->dwMask & DBIM_MAXSIZE)
246             {
247                 pdbi->ptMaxSize = maxSize;
248             }
249             if (pdbi->dwMask & DBIM_INTEGRAL)
250             {
251                 pdbi->ptIntegral = itemSize;
252             }
253             if (pdbi->dwMask & DBIM_ACTUAL)
254             {
255                 pdbi->ptActual = actualSize;
256             }
257             if (pdbi->dwMask & DBIM_TITLE)
258             {
259                 if (m_QLaunch || !ILGetDisplayNameEx(NULL, m_pidl, pdbi->wszTitle, ILGDN_INFOLDER))
260                 {
261                     pdbi->dwMask &= ~DBIM_TITLE;
262                 }
263             }
264             if (pdbi->dwMask & DBIM_MODEFLAGS)
265             {
266                 pdbi->dwModeFlags = DBIMF_NORMAL | DBIMF_VARIABLEHEIGHT | DBIMF_USECHEVRON | DBIMF_NOMARGINS | DBIMF_BKCOLOR;
267                 if (m_QLaunch)
268                 {
269                     pdbi->dwModeFlags |= DBIMF_ADDTOFRONT;
270                 }
271             }
272             if (pdbi->dwMask & DBIM_BKCOLOR)
273                 pdbi->dwMask &= ~DBIM_BKCOLOR;
274 
275             return S_OK;
276         }
277 
278         return E_FAIL;
279     }
280 
281 /*****************************************************************************/
282 // *** IPersistStream ***
283     STDMETHODIMP CISFBand::GetClassID(OUT CLSID *pClassID)
284     {
285         *pClassID = CLSID_ISFBand;
286 
287         return S_OK;
288     }
289 
290     STDMETHODIMP CISFBand::IsDirty()
291     {
292         /* The object hasn't changed since the last save! */
293 
294         return S_FALSE;
295     }
296 
297     STDMETHODIMP CISFBand::Load(IN IStream *pStm)
298     {
299         TRACE("CISFBand::Load called\n");
300         /* Nothing to do */
301 
302         return S_OK;
303     }
304 
305     STDMETHODIMP CISFBand::Save(IN IStream *pStm, IN BOOL fClearDirty)
306     {
307         /* Nothing to do */
308 
309         return S_OK;
310     }
311 
312     STDMETHODIMP CISFBand::GetSizeMax(OUT ULARGE_INTEGER *pcbSize)
313     {
314         TRACE("CISFBand::GetSizeMax called\n");
315 
316         return S_OK;
317     }
318 
319 /*****************************************************************************/
320 // *** IWinEventHandler ***
321     STDMETHODIMP CISFBand::ContainsWindow(IN HWND hWnd)
322     {
323         if (hWnd == m_hWnd || IsChild(hWnd))
324         {
325             TRACE("CISFBand::ContainsWindow(0x%p) returns S_OK\n", hWnd);
326             return S_OK;
327         }
328 
329         return S_FALSE;
330     }
331 
332     STDMETHODIMP CISFBand::OnWinEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *theResult)
333     {
334         switch (uMsg)
335         {
336             case WM_COMMAND:
337             {
338                 TBBUTTON tb;
339                 bool chk = SendMessage(m_hWnd, TB_GETBUTTON, LOWORD(wParam), (LPARAM)&tb);
340                 if (chk)
341                     SHInvokeDefaultCommand(m_hWnd, m_pISF, (LPITEMIDLIST)tb.dwData);
342 
343                 *theResult = TRUE;
344                 break;
345             }
346             case WM_NOTIFY:
347             {
348                 switch (((LPNMHDR)lParam)->code)
349                 {
350                     case NM_RCLICK:
351                     {
352                         HRESULT hr;
353                         POINT pt = ((LPNMMOUSE)lParam)->pt;
354                         CComPtr<IContextMenu> picm;
355                         HMENU fmenu = CreatePopupMenu();
356                         TBBUTTON tb;
357 
358                         bool chk = SendMessage(m_hWnd, TB_GETBUTTON, ((LPNMMOUSE)lParam)->dwItemSpec, (LPARAM)&tb);
359                         LPITEMIDLIST pidl = (LPITEMIDLIST)tb.dwData;
360 
361                         if (chk)
362                         {
363                             ClientToScreen(&pt);
364                             hr = m_pISF->GetUIObjectOf(m_hWnd, 1, &pidl, IID_NULL_PPV_ARG(IContextMenu, &picm));
365                             if (FAILED_UNEXPECTEDLY(hr))
366                                 return hr;
367 
368                             hr = picm->QueryContextMenu(fmenu, 0, 1, 0x7FFF, CMF_DEFAULTONLY);
369                             if (FAILED_UNEXPECTEDLY(hr))
370                                 return hr;
371 
372                             int id = TrackPopupMenuEx(fmenu, TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_RETURNCMD, pt.x, pt.y, m_hWnd, 0);
373                             if (id > 0)
374                             {
375                                 CMINVOKECOMMANDINFOEX info = { 0 };
376                                 info.cbSize = sizeof(info);
377                                 info.fMask = CMIC_MASK_PTINVOKE;
378                                 if (GetKeyState(VK_CONTROL) < 0)
379                                 {
380                                     info.fMask |= CMIC_MASK_CONTROL_DOWN;
381                                 }
382                                 if (GetKeyState(VK_SHIFT) < 0)
383                                 {
384                                     info.fMask |= CMIC_MASK_SHIFT_DOWN;
385                                 }
386                                 info.hwnd = m_hWnd;
387                                 info.lpVerb = MAKEINTRESOURCEA(id - 1);
388                                 info.nShow = SW_SHOWNORMAL;
389                                 info.ptInvoke = pt;
390                                 picm->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
391                             }
392                         }
393                         DestroyMenu(fmenu);
394 
395                         *theResult = TRUE;
396                         break;
397                     }
398                     default:
399                         *theResult = FALSE;
400                 }
401 
402                 break;
403             }
404             default:
405                 *theResult = FALSE;
406         }
407 
408         return S_OK;
409     }
410 
411     STDMETHODIMP CISFBand::IsWindowOwner(HWND hWnd)
412     {
413         return (hWnd == m_hWnd) ? S_OK : S_FALSE;
414     }
415 
416 /*****************************************************************************/
417 // *** IOleCommandTarget methods ***
418     STDMETHODIMP CISFBand::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds [], OLECMDTEXT *pCmdText)
419     {
420         UNIMPLEMENTED;
421 
422         return E_NOTIMPL;
423     }
424 
425     STDMETHODIMP CISFBand::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
426     {
427         if (IsEqualIID(*pguidCmdGroup, IID_IBandSite))
428         {
429             return S_OK;
430         }
431 
432         if (IsEqualIID(*pguidCmdGroup, IID_IDeskBand))
433         {
434             return S_OK;
435         }
436 
437         UNIMPLEMENTED;
438 
439         return E_NOTIMPL;
440     }
441 
442 /*****************************************************************************/
443 // *** IShellFolderBand ***
444     STDMETHODIMP CISFBand::GetBandInfoSFB(PBANDINFOSFB pbi)
445     {
446         if (pbi->dwMask == ISFB_MASK_IDLIST)
447         {
448             pbi->pidl = ILClone(m_pidl);
449             if (!pbi->pidl)
450                 return E_OUTOFMEMORY;
451             return S_OK;
452         }
453 
454         return E_NOTIMPL;
455     }
456 
457     STDMETHODIMP CISFBand::InitializeSFB(IShellFolder *psf, PCIDLIST_ABSOLUTE pidl)
458     {
459         HRESULT hr;
460 
461         if (!psf && !pidl)
462             return E_INVALIDARG;
463 
464         if (psf && pidl)
465             return E_INVALIDARG;
466 
467         if (pidl != NULL)
468         {
469             CComPtr<IShellFolder> psfDesktop;
470             hr = SHGetDesktopFolder(&psfDesktop);
471             if (FAILED_UNEXPECTEDLY(hr))
472                 return hr;
473 
474             if (_ILIsDesktop(pidl))
475             {
476                 m_pISF = psfDesktop;
477             }
478             else
479             {
480                 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &m_pISF));
481                 if (FAILED_UNEXPECTEDLY(hr))
482                     return hr;
483             }
484 
485             m_pidl = ILClone(pidl);
486         }
487 
488         if (psf != NULL)
489         {
490             CComPtr<IPersistFolder2> ppf2;
491             hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
492             if (FAILED_UNEXPECTEDLY(hr))
493                 return hr;
494 
495             hr = ppf2->GetCurFolder(&m_pidl);
496             if (FAILED_UNEXPECTEDLY(hr))
497                 return hr;
498 
499             m_pISF = psf;
500         }
501 
502         return S_OK;
503     }
504 
505     STDMETHODIMP CISFBand::SetBandInfoSFB( PBANDINFOSFB pbi)
506     {
507         if ((pbi->dwMask & ISFB_MASK_STATE) &&
508             (pbi->dwState & ISFB_STATE_QLINKSMODE) &&
509             (pbi->dwStateMask & ISFB_STATE_QLINKSMODE))
510         {
511             m_QLaunch = true;
512             m_textFlag = false;
513             if (m_hWnd)
514                 SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
515         }
516 
517         return E_NOTIMPL;
518     }
519 
520 /*****************************************************************************/
521 // *** IContextMenu ***
522     STDMETHODIMP CISFBand::GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
523     {
524         /*HRESULT hr = E_INVALIDARG;
525 
526         if (idCmd == IDM_DISPLAY)
527         {
528             switch (uFlags)
529             {
530             case GCS_HELPTEXTW:
531                 // Only useful for pre-Vista versions of Windows that
532                 // have a Status bar.
533                 hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
534                     cchMax,
535                     L"Display File Name");
536                 break;
537 
538             case GCS_VERBW:
539                 // GCS_VERBW is an optional feature that enables a caller
540                 // to discover the canonical name for the verb that is passed in
541                 // through idCommand.
542                 hr = StringCchCopyW(reinterpret_cast<PWSTR>(pszName),
543                     cchMax,
544                     L"DisplayFileName");
545                 break;
546             }
547         }
548         return hr;  */
549 
550         return S_OK;
551     }
552 
553     STDMETHODIMP CISFBand::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
554     {
555         if (!HIWORD(pici->lpVerb))
556         {
557             switch (LOWORD(pici->lpVerb))
558             {
559                 case IDM_LARGE_ICONS:
560                 {
561                     m_iconFlag = false;
562 
563                     HIMAGELIST* piml = (HIMAGELIST*) SendMessage(m_hWnd, TB_GETIMAGELIST, 0, 0);
564                     HRESULT hr = SHGetImageList(SHIL_LARGE, IID_IImageList, (void**)&piml);
565                     if (FAILED_UNEXPECTEDLY(hr)) return hr;
566                     SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
567                     hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
568                     if (FAILED_UNEXPECTEDLY(hr)) return hr;
569                     break;
570                 }
571                 case IDM_SMALL_ICONS:
572                 {
573                     m_iconFlag = true;
574 
575                     HIMAGELIST* piml = (HIMAGELIST*)SendMessage(m_hWnd, TB_GETIMAGELIST, 0, 0);
576                     HRESULT hr = SHGetImageList(SHIL_SMALL, IID_IImageList, (void**)&piml);
577                     if (FAILED_UNEXPECTEDLY(hr)) return hr;
578                     SendMessage(m_hWnd, TB_SETIMAGELIST, 0, (LPARAM)piml);
579                     hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
580                     if (FAILED_UNEXPECTEDLY(hr)) return hr;
581                     break;
582                 }
583                 case IDM_OPEN_FOLDER:
584                 {
585                     SHELLEXECUTEINFO shexinfo;
586 
587                     memset(&shexinfo, 0x0, sizeof(shexinfo));
588 
589                     shexinfo.cbSize = sizeof(shexinfo);
590                     shexinfo.fMask = SEE_MASK_IDLIST;
591                     shexinfo.lpVerb = _T("open");
592                     shexinfo.lpIDList = m_pidl;
593                     shexinfo.nShow = SW_SHOW;
594 
595                     if (!ShellExecuteEx(&shexinfo))
596                         return E_FAIL;
597 
598                     break;
599                 }
600                 case IDM_SHOW_TEXT:
601                 {
602                     if (m_textFlag)
603                     {
604                         m_textFlag = false;
605                         SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
606                         HRESULT hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
607                         if (FAILED_UNEXPECTEDLY(hr)) return hr;
608                     }
609                     else
610                     {
611                         m_textFlag = true;
612                         SendMessage(m_hWnd, TB_SETEXTENDEDSTYLE, 0, 0);
613                         HRESULT hr = IUnknown_Exec(m_Site, IID_IDeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL);
614                         if (FAILED_UNEXPECTEDLY(hr)) return hr;
615                     }
616                     break;
617                 }
618                 default:
619                     return E_FAIL;
620             }
621         }
622 
623         return S_OK;
624     }
625 
626     STDMETHODIMP CISFBand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
627     {
628         HMENU qMenu = LoadMenu(GetModuleHandleW(L"browseui.dll"), MAKEINTRESOURCE(IDM_POPUPMENU));
629 
630         if(m_textFlag)
631             CheckMenuItem(qMenu, IDM_SHOW_TEXT, MF_CHECKED);
632         else
633             CheckMenuItem(qMenu, IDM_SHOW_TEXT, MF_UNCHECKED);
634 
635         if (m_iconFlag)
636         {
637             CheckMenuItem(qMenu, IDM_SMALL_ICONS, MF_CHECKED);
638             CheckMenuItem(qMenu, IDM_LARGE_ICONS, MF_UNCHECKED);
639         }
640         else
641         {
642             CheckMenuItem(qMenu, IDM_LARGE_ICONS, MF_CHECKED);
643             CheckMenuItem(qMenu, IDM_SMALL_ICONS, MF_UNCHECKED);
644         }
645 
646         if (_ILIsDesktop(m_pidl))
647             DeleteMenu(qMenu, IDM_OPEN_FOLDER, MF_BYCOMMAND);
648 
649         UINT idMax = Shell_MergeMenus(hmenu, GetSubMenu(qMenu, 0), indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS);
650         DestroyMenu(qMenu);
651         return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(idMax - idCmdFirst +1));
652     }
653 
654 /*****************************************************************************/
655 // C Constructor
656     extern "C"
657     HRESULT WINAPI RSHELL_CISFBand_CreateInstance(REFIID riid, void** ppv)
658     {
659         return ShellObjectCreator<CISFBand>(riid, ppv);
660     }
661 
662