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