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