#include "cabinet.h" #include "rcids.h" #include #include "bandsite.h" #include "shellp.h" #include "shdguid.h" #include "taskband.h" #include "taskbar.h" #include #include "util.h" #include "strsafe.h" extern IStream *GetDesktopViewStream(DWORD grfMode, LPCTSTR pszName); HRESULT PersistStreamLoad(IStream *pstm, IUnknown *punk); HRESULT PersistStreamSave(IStream *pstm, BOOL fClearDirty, IUnknown *punk); const TCHAR c_szTaskbar[] = TEXT("Taskbar"); // {69B3F106-0F04-11d3-AE2E-00C04F8EEA99} static const GUID CLSID_TrayBandSite = { 0x69b3f106, 0xf04, 0x11d3, { 0xae, 0x2e, 0x0, 0xc0, 0x4f, 0x8e, 0xea, 0x99 } }; // {8B4A02DB-97BB-4C1B-BE75-8827A7358CD0} static const GUID CLSID_TipBand = { 0x8B4A02DB, 0x97BB, 0x4C1B, { 0xBE, 0x75, 0x88, 0x27, 0xA7, 0x35, 0x8C, 0xD0 } }; class CTrayBandSite : public IBandSite , public IClassFactory { public: // *** IUnknown *** virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(void) ; virtual STDMETHODIMP_(ULONG) Release(void); // *** IBandSite methods *** STDMETHOD(AddBand) (THIS_ IUnknown* punk); STDMETHOD(EnumBands) (THIS_ UINT uBand, DWORD* pdwBandID); STDMETHOD(QueryBand) (THIS_ DWORD dwBandID, IDeskBand** ppstb, DWORD* pdwState, LPWSTR pszName, int cchName) ; STDMETHOD(SetBandState) (THIS_ DWORD dwBandID, DWORD dwMask, DWORD dwState) ; STDMETHOD(RemoveBand) (THIS_ DWORD dwBandID); STDMETHOD(GetBandObject) (THIS_ DWORD dwBandID, REFIID riid, void ** ppvObj); STDMETHOD(SetBandSiteInfo) (THIS_ const BANDSITEINFO * pbsinfo); STDMETHOD(GetBandSiteInfo) (THIS_ BANDSITEINFO * pbsinfo); // *** IClassFactory methods *** HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) { if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION; return QueryInterface(riid, ppvObj); } HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) { return S_OK; } IContextMenu3* GetContextMenu(); void SetInner(IUnknown* punk); void SetLoaded(BOOL fLoaded) {_fLoaded = fLoaded;} BOOL HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres); protected: CTrayBandSite(); virtual ~CTrayBandSite(); BOOL _CreateBandSiteMenu(IUnknown* punk); HRESULT _AddRequiredBands(); void _BroadcastExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut); HRESULT _SetWindowTheme(LPWSTR pwzTheme); friend IBandSite* BandSite_CreateView(); friend void BandSite_HandleDelayBootStuff(IUnknown* punk); friend void BandSite_Load(); friend HRESULT BandSite_SetWindowTheme(IBandSite* pbs, LPWSTR pwzTheme); LONG _cRef; IUnknown *_punkInner; IBandSite *_pbsInner; // bandsite context menu IContextMenu3* _pcm; HWND _hwnd; BOOL _fLoaded; BOOL _fDelayBootStuffHandled; DWORD _dwClassObject; WCHAR* _pwzTheme; }; CTrayBandSite* IUnknownToCTrayBandSite(IUnknown* punk) { CTrayBandSite* ptbs; punk->QueryInterface(CLSID_TrayBandSite, (void **)&ptbs); ASSERT(ptbs); punk->Release(); return ptbs; } CTrayBandSite::CTrayBandSite() : _cRef(1) { } CTrayBandSite::~CTrayBandSite() { if (_pcm) _pcm->Release(); if (_pwzTheme) delete[] _pwzTheme; return; } ULONG CTrayBandSite::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTrayBandSite::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { _cRef = 1000; // guard against recursion if (_pbsInner) { AddRef(); _pbsInner->Release(); } // this must come last if (_punkInner) _punkInner->Release(); // paired w/ CCI aggregation ASSERT(_cRef == 1000); delete this; } return cRef; } HRESULT CTrayBandSite::QueryInterface(REFIID riid, LPVOID * ppvObj) { static const QITAB qit[] = { QITABENT(CTrayBandSite, IBandSite), QITABENT(CTrayBandSite, IClassFactory), { 0 }, }; HRESULT hr = QISearch(this, qit, riid, ppvObj); if (FAILED(hr) && IsEqualIID(riid, CLSID_TrayBandSite)) { *ppvObj = this; AddRef(); hr = S_OK; } if (FAILED(hr) && _punkInner) { hr = _punkInner->QueryInterface(riid, ppvObj); } return hr; } static BOOL CALLBACK SetTransparency(HWND hwnd, LPARAM lParam) { SetWindowStyleEx(hwnd, WS_EX_TRANSPARENT, (BOOL)lParam); return TRUE; } // *** IBandSite methods *** HRESULT CTrayBandSite::AddBand(IUnknown* punk) { CLSID clsid; HRESULT hr = S_OK; if (!_fDelayBootStuffHandled) { // // Tell the band to go into "delay init" mode. When the tray // timer goes off we'll tell the band to finish up. (See // BandSite_HandleDelayBootStuff). // IUnknown_Exec(punk, &CGID_DeskBand, DBID_DELAYINIT, 0, NULL, NULL); } if (c_tray.GetIsNoToolbarsOnTaskbarPolicyEnabled()) { hr = IUnknown_GetClassID(punk, &clsid); if (SUCCEEDED(hr)) { hr = IsEqualGUID(clsid, CLSID_TaskBand) ? S_OK : E_FAIL; } } if (SUCCEEDED(hr)) { hr = _pbsInner->AddBand(punk); if (SUCCEEDED(hr)) { IShellFolderBand *pisfBand; HRESULT hrInner = punk->QueryInterface(IID_PPV_ARG(IShellFolderBand, &pisfBand)); if (SUCCEEDED(hrInner)) { BANDINFOSFB bi; bi.dwMask = ISFB_MASK_STATE; hrInner = pisfBand->GetBandInfoSFB(&bi); if (SUCCEEDED(hrInner)) { bi.dwState |= ISFB_STATE_BTNMINSIZE; hrInner = pisfBand->SetBandInfoSFB(&bi); } pisfBand->Release(); } // tell the band to use the taskbar theme if (_pwzTheme) { VARIANTARG var; var.vt = VT_BSTR; var.bstrVal = _pwzTheme; IUnknown_Exec(punk, &CGID_DeskBand, DBID_SETWINDOWTHEME, 0, &var, NULL); } if (GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TRANSPARENT) { EnumChildWindows(_hwnd, SetTransparency, (LPARAM)TRUE); } } } return hr; } HRESULT CTrayBandSite::EnumBands(UINT uBand, DWORD* pdwBandID) { return _pbsInner->EnumBands(uBand, pdwBandID); } HRESULT CTrayBandSite::QueryBand(DWORD dwBandID, IDeskBand** ppstb, DWORD* pdwState, LPWSTR pszName, int cchName) { return _pbsInner->QueryBand(dwBandID, ppstb, pdwState, pszName, cchName); } HRESULT CTrayBandSite::SetBandState(DWORD dwBandID, DWORD dwMask, DWORD dwState) { return _pbsInner->SetBandState(dwBandID, dwMask, dwState); } HRESULT CTrayBandSite::RemoveBand(DWORD dwBandID) { return _pbsInner->RemoveBand(dwBandID); } HRESULT CTrayBandSite::GetBandObject(DWORD dwBandID, REFIID riid, void ** ppvObj) { return _pbsInner->GetBandObject(dwBandID, riid, ppvObj); } HRESULT CTrayBandSite::SetBandSiteInfo (const BANDSITEINFO * pbsinfo) { return _pbsInner->SetBandSiteInfo(pbsinfo); } HRESULT CTrayBandSite::GetBandSiteInfo (BANDSITEINFO * pbsinfo) { return _pbsInner->GetBandSiteInfo(pbsinfo); } HRESULT CTrayBandSite::_AddRequiredBands() { IDeskBand* pdb; HRESULT hr = CoCreateInstance(CLSID_TaskBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDeskBand, &pdb)); if (SUCCEEDED(hr)) { hr = AddBand(pdb); pdb->Release(); } return hr; } HRESULT CTrayBandSite::_SetWindowTheme(LPWSTR pwzTheme) { if (_pwzTheme) { delete[] _pwzTheme; _pwzTheme = NULL; } if (pwzTheme) { int cchLen = lstrlen(pwzTheme) + 1; _pwzTheme = new WCHAR[cchLen]; if (_pwzTheme) { StringCchCopy(_pwzTheme, cchLen, pwzTheme); } } return S_OK; } HRESULT BandSite_TestBandCLSID(IBandSite *pbs, DWORD idBand, REFIID riid) { HRESULT hr = E_FAIL; IPersist *pp; if (pbs) { hr = pbs->GetBandObject(idBand, IID_PPV_ARG(IPersist, &pp)); if (SUCCEEDED(hr)) { CLSID clsid; hr = pp->GetClassID(&clsid); if (SUCCEEDED(hr)) { hr = IsEqualGUID(clsid, riid) ? S_OK : S_FALSE; } pp->Release(); } } return hr; } HRESULT BandSite_SetWindowTheme(IBandSite* pbs, LPWSTR pwzTheme) { HRESULT hr = E_FAIL; if (pbs) { CTrayBandSite* ptbs = IUnknownToCTrayBandSite(pbs); if (ptbs) { ptbs->_SetWindowTheme(pwzTheme); } DWORD dwBandID; BOOL fFound = FALSE; for (int i = 0; !fFound && SUCCEEDED(pbs->EnumBands(i, &dwBandID)); i++) { IUnknown* punk; HRESULT hrInner = pbs->GetBandObject(dwBandID, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hrInner)) { VARIANTARG var; var.vt = VT_BSTR; var.bstrVal = pwzTheme; IUnknown_Exec(punk, &CGID_DeskBand, DBID_SETWINDOWTHEME, 0, &var, NULL); } } } return hr; } HRESULT BandSite_FindBand(IBandSite* pbs, REFCLSID rclsid, REFIID riid, void **ppv, int *piCount, DWORD* pdwBandID) { HRESULT hr = E_FAIL; int iCount = 0; if (pbs) { DWORD dwBandID; for (int i = 0; SUCCEEDED(pbs->EnumBands(i, &dwBandID)); i++) { if (BandSite_TestBandCLSID(pbs, dwBandID, rclsid) == S_OK) { iCount++; if (pdwBandID) { *pdwBandID = dwBandID; } if (ppv) hr = pbs->GetBandObject(dwBandID, riid, ppv); else hr = S_OK; } } } if (piCount) { *piCount = iCount; } return hr; } void BandSite_Initialize(IBandSite* pbs) { HWND hwnd = v_hwndTray; CTaskBar *pow = new CTaskBar(); if (pow) { IDeskBarClient* pdbc; if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pdbc)))) { // we need to set a dummy tray IOleWindow pdbc->SetDeskBarSite(SAFECAST(pow, IOleWindow*)); pdbc->GetWindow(&hwnd); if (hwnd) { // taskbar windows are themed under Taskbar subapp name SendMessage(hwnd, RB_SETWINDOWTHEME, 0, (LPARAM)c_wzTaskbarTheme); pow->_hwndRebar = hwnd; } pdbc->Release(); } pow->Release(); } } IContextMenu3* CTrayBandSite::GetContextMenu() { if (!_pcm) { if (SUCCEEDED(CoCreateInstance(CLSID_BandSiteMenu, NULL,CLSCTX_INPROC_SERVER, IID_PPV_ARG(IContextMenu3, &_pcm)))) { IShellService* pss; if (SUCCEEDED(_pcm->QueryInterface(IID_PPV_ARG(IShellService, &pss)))) { pss->SetOwner(SAFECAST(this, IBandSite*)); pss->Release(); } } } if (_pcm) _pcm->AddRef(); return _pcm; } HRESULT BandSite_AddMenus(IUnknown* punk, HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast) { HRESULT hr = E_FAIL; CTrayBandSite* ptbs = IUnknownToCTrayBandSite(punk); IContextMenu3* pcm = ptbs->GetContextMenu(); if (pcm) { hr = pcm->QueryContextMenu(hmenu, indexMenu, idCmdFirst, idCmdLast, CMF_ICM3); pcm->Release(); } return hr; } void BandSite_HandleMenuCommand(IUnknown* punk, UINT idCmd) { CTrayBandSite* ptbs = IUnknownToCTrayBandSite(punk); IContextMenu3* pcm = ptbs->GetContextMenu(); if (pcm) { CMINVOKECOMMANDINFOEX ici = { sizeof(CMINVOKECOMMANDINFOEX), 0L, NULL, (LPSTR)MAKEINTRESOURCE(idCmd), NULL, NULL, SW_NORMAL, }; pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); pcm->Release(); } } void CTrayBandSite::SetInner(IUnknown* punk) { _punkInner = punk; _punkInner->QueryInterface(IID_PPV_ARG(IBandSite, &_pbsInner)); Release(); ASSERT(_pbsInner); } IBandSite* BandSite_CreateView() { IUnknown *punk; HRESULT hr = E_FAIL; // aggregate a TrayBandSite (from a RebarBandSite) CTrayBandSite *ptbs = new CTrayBandSite; if (ptbs) { hr = CoCreateInstance(CLSID_RebarBandSite, (IBandSite*)ptbs, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk)); if (SUCCEEDED(hr)) { ptbs->SetInner(punk); // paired w/ Release in outer (TBS::Release) BandSite_Initialize(ptbs); return SAFECAST(ptbs, IBandSite*); } else { delete ptbs; return NULL; } } return NULL; } HRESULT BandSite_SaveView(IUnknown *pbs) { HRESULT hr = E_FAIL; IStream *pstm = GetDesktopViewStream(STGM_WRITE, c_szTaskbar); if (pstm) { hr = PersistStreamSave(pstm, TRUE, pbs); pstm->Release(); } return hr; } BOOL CTrayBandSite::HandleMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { if (!_hwnd) { IUnknown_GetWindow(SAFECAST(this, IBandSite*), &_hwnd); } switch (uMsg) { case WM_INITMENUPOPUP: case WM_MEASUREITEM: case WM_DRAWITEM: case WM_MENUCHAR: if (_pcm) { _pcm->HandleMenuMsg2(uMsg, wParam, lParam, plres); return TRUE; } break; case WM_NOTIFY: switch (((LPNMHDR)lParam)->code) { case NM_NCHITTEST: { NMMOUSE *pnm = (LPNMMOUSE)lParam; if (_hwnd == pnm->hdr.hwndFrom) { if (pnm->dwHitInfo == RBHT_CLIENT || pnm->dwItemSpec == -1) { if (plres) *plres = HTTRANSPARENT; } return TRUE; } } break; case RBN_MINMAX: *plres = SHRestricted(REST_NOMOVINGBAND); return TRUE; } break; } IWinEventHandler *pweh; if (SUCCEEDED(QueryInterface(IID_PPV_ARG(IWinEventHandler, &pweh)))) { HRESULT hr = pweh->OnWinEvent(hwnd, uMsg, wParam, lParam, plres); pweh->Release(); return SUCCEEDED(hr); } ASSERT(0); // we know we support IWinEventHandler return FALSE; } void CTrayBandSite::_BroadcastExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { // Broadcast an Exec to all child bands DWORD dwBandID; UINT uBand = 0; while (SUCCEEDED(EnumBands(uBand, &dwBandID))) { IOleCommandTarget* pct; if (SUCCEEDED(GetBandObject(dwBandID, IID_PPV_ARG(IOleCommandTarget, &pct)))) { pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); pct->Release(); } uBand++; } } void BandSite_HandleDelayBootStuff(IUnknown* punk) { if (punk) { CTrayBandSite* pbs = IUnknownToCTrayBandSite(punk); pbs->_fDelayBootStuffHandled = TRUE; pbs->_BroadcastExec(&CGID_DeskBand, DBID_FINISHINIT, 0, NULL, NULL); } } // returns true or false whether it handled it BOOL BandSite_HandleMessage(IUnknown *punk, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plres) { if (punk) { CTrayBandSite* pbs = IUnknownToCTrayBandSite(punk); return pbs->HandleMessage(hwnd, uMsg, wParam, lParam, plres); } return FALSE; } void BandSite_SetMode(IUnknown *punk, DWORD dwMode) { IBandSite* pbs = (IBandSite*)punk; if (pbs) { IDeskBarClient *pdbc; if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pdbc)))) { pdbc->SetModeDBC(dwMode); pdbc->Release(); } } } void BandSite_Update(IUnknown *punk) { IBandSite* pbs = (IBandSite*)punk; if (pbs) { IOleCommandTarget *pct; if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pct)))) { pct->Exec(&CGID_DeskBand, DBID_BANDINFOCHANGED, 0, NULL, NULL); pct->Release(); } } } void BandSite_UIActivateDBC(IUnknown *punk, DWORD dwState) { IBandSite* pbs = (IBandSite*)punk; if (pbs) { IDeskBarClient *pdbc; if (SUCCEEDED(pbs->QueryInterface(IID_PPV_ARG(IDeskBarClient, &pdbc)))) { pdbc->UIActivateDBC(dwState); pdbc->Release(); } } } //*** PersistStreamLoad, PersistStreamSave // NOTES // we don't insist on finding IPersistStream iface; absence of it is // assumed to mean there's nothing to init. HRESULT PersistStreamLoad(IStream *pstm, IUnknown *punk) { IPersistStream *pps; HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IPersistStream, &pps)); if (SUCCEEDED(hr)) { hr = pps->Load(pstm); pps->Release(); } else hr = S_OK; // n.b. S_OK not hr (don't insist on IID_IPS) return hr; } HRESULT PersistStreamSave(IStream *pstm, BOOL fClearDirty, IUnknown *punk) { HRESULT hr = E_FAIL; if (punk) { hr = S_OK;// n.b. S_OK not hr (don't insist on IID_IPS) IPersistStream *pps; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IPersistStream, &pps)))) { hr = pps->Save(pstm, fClearDirty); pps->Release(); } } return hr; } HRESULT IUnknown_SimulateDrop(IUnknown* punk, IDataObject* pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { HRESULT hr = E_FAIL; if (punk) { IDropTarget* pdt; hr = punk->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)); if (SUCCEEDED(hr)) { hr = pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect); if (*pdwEffect) { hr = pdt->Drop(pdtobj, grfKeyState, pt, pdwEffect); } else { pdt->DragLeave(); } pdt->Release(); } } return hr; } LRESULT BandSite_OnMarshallBS(WPARAM wParam, LPARAM lParam) { GUID *riid = (GUID *) wParam; IStream *pstm = NULL; // paired w/ matching Unmarshal in shdocvw (TM_MARSHALBS) HRESULT hr = CoMarshalInterThreadInterfaceInStream(*riid, c_tray._ptbs, &pstm); ASSERT(SUCCEEDED(hr)); return (LRESULT) pstm; } IStream *GetDesktopViewStream(DWORD grfMode, LPCTSTR pszName) { HKEY hkStreams; ASSERT(g_hkeyExplorer); if (RegCreateKey(g_hkeyExplorer, TEXT("Streams"), &hkStreams) == ERROR_SUCCESS) { IStream *pstm = OpenRegStream(hkStreams, TEXT("Desktop"), pszName, grfMode); RegCloseKey(hkStreams); return pstm; } return NULL; } BOOL Reg_GetString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPTSTR psz, DWORD cb) { BOOL fRet = FALSE; if (!g_fCleanBoot) { fRet = ERROR_SUCCESS == SHGetValue(hkey, pszSubKey, pszValue, NULL, psz, &cb); } return fRet; } void BandSite_Load() { CTrayBandSite* ptbs = IUnknownToCTrayBandSite(c_tray._ptbs); HRESULT hr = E_FAIL; // 1st, try persisted state IStream *pstm = GetDesktopViewStream(STGM_READ, c_szTaskbar); if (pstm) { hr = PersistStreamLoad(pstm, (IBandSite*)ptbs); pstm->Release(); } // 2nd, if there is none (or if version mismatch or other failure), // try settings from setup if (FAILED(hr)) { LPTSTR pszValue; if (IsOS(OS_PERSONAL) || IsOS(OS_PROFESSIONAL) || SHRestricted(REST_CLASSICSHELL)) { // use the no-quick-launch stream pszValue = TEXT("Default Taskbar (Personal)"); } else { pszValue = TEXT("Default Taskbar"); } // n.b. HKLM not HKCU // like GetDesktopViewStream but for HKLM pstm = OpenRegStream(HKEY_LOCAL_MACHINE, REGSTR_PATH_EXPLORER TEXT("\\Streams\\Desktop"), pszValue, STGM_READ); if (pstm) { hr = PersistStreamLoad(pstm, (IBandSite *)ptbs); pstm->Release(); } } // o.w., throw up our hands and force some hard-coded defaults // this is needed for a) unexpected failures; b) debug bootstrap; int iCount = 0; DWORD dwBandID; if (FAILED(hr) || FAILED(BandSite_FindBand(ptbs, CLSID_TaskBand, CLSID_NULL, NULL, &iCount, &dwBandID))) { // // note that for the CheckBands case, we're assuming that // a) AddBands adds only the missing guys (for now there's // only 1 [TaskBand] so we're ok); and b) AddBands doesn't // create dups if only some are missing (again for now there's // only 1 so no pblm) ptbs->_AddRequiredBands(); } hr = BandSite_FindBand(ptbs, CLSID_TaskBand, CLSID_NULL, NULL, &iCount, &dwBandID); while ((iCount > 1) && SUCCEEDED(hr)) { ptbs->RemoveBand(dwBandID); hr = BandSite_FindBand(ptbs, CLSID_TaskBand, CLSID_NULL, NULL, &iCount, &dwBandID); } // And one more: this is needed for the TipBand deskband for the TabletPC. iCount = 0; if (FAILED(hr) || FAILED(BandSite_FindBand(ptbs, CLSID_TipBand, CLSID_NULL, NULL, &iCount, &dwBandID))) { IDeskBand* pdb; HRESULT hr = CoCreateInstance(CLSID_TipBand, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IDeskBand, &pdb)); if (SUCCEEDED(hr)) { hr = ptbs->AddBand(pdb); pdb->Release(); } } hr = BandSite_FindBand(ptbs, CLSID_TipBand, CLSID_NULL, NULL, &iCount, &dwBandID); while ((iCount > 1) && SUCCEEDED(hr)) { ptbs->RemoveBand(dwBandID); hr = BandSite_FindBand(ptbs, CLSID_TipBand, CLSID_NULL, NULL, &iCount, &dwBandID); } ptbs->SetLoaded(TRUE); } HRESULT CTrayBandSiteService_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk) { if (punkOuter) return CLASS_E_NOAGGREGATION; if (c_tray._ptbs) { *ppunk = c_tray._ptbs; c_tray._ptbs->AddRef(); return S_OK; } return E_OUTOFMEMORY; }