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