Click here to Skip to main content
15,893,814 members
Articles / Desktop Programming / ATL

Shell Extensibility - Explorer Desk Band, Tray Notification Icon et al.

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
28 Aug 2009CPOL12 min read 139.3K   3.2K   71  
A simple Calendar utility that demonstrates basic Shell extensibility techniques: desk band, tray notification icon, locales.
// CalendarDeskBand.cpp : Implementation of CCalendarDeskBand

#include "StdAfx.h"
#include "CalendarDeskBand.h"
#include "DateFormatSettings.h"
#include "Utils.h"

////////////////////////////////////////////////////////////////////////////////
// 
const UINT IDM_SEPARATOR_OFFSET = 0;
const UINT IDM_SETTINGS_OFFSET = 1;

const LPCTSTR SETTINGS_SECTION_DATEFORMAT = TEXT("DateFormatDB");

////////////////////////////////////////////////////////////////////////////////
// CCalendarDeskBand

CCalendarDeskBand::CCalendarDeskBand()
{
}

////////////////////////////////////////////////////////////////////////////////
// 
HRESULT CCalendarDeskBand::FinalConstruct()
{
    m_nBandID = 0;
    m_dwViewMode = DBIF_VIEWMODE_NORMAL;
    m_bRequiresSave = false;
    m_bCompositionEnabled = TRUE;
    ::MakeSettingsFilename(m_settingsPath);

    if(m_settingsPath.FileExists())
        m_dateFormat.Load(m_settingsPath, SETTINGS_SECTION_DATEFORMAT);

    return HandleShowRequest();
}

void CCalendarDeskBand::FinalRelease()
{
    if(m_settingsPath.FileExists())
        m_dateFormat.Save(m_settingsPath, SETTINGS_SECTION_DATEFORMAT);
}

HRESULT CCalendarDeskBand::HandleShowRequest()
{
    OLECHAR szAtom[MAX_GUID_STRING_LEN] = { 0 };
    ::StringFromGUID2(CLSID_CalendarDeskBand, szAtom, MAX_GUID_STRING_LEN);

    HRESULT hr = S_OK;
    const ATOM show = ::GlobalFindAtomW(szAtom);

    if(show)
    {
        CComPtr<IBandSite> spBandSite;
        hr = spBandSite.CoCreateInstance(CLSID_TrayBandSiteService);

        if(SUCCEEDED(hr))
        {
            LPUNKNOWN lpUnk = static_cast<IOleWindow*>(this);
            hr = spBandSite->AddBand(lpUnk);
        }

        ::GlobalDeleteAtom(show);
    }

    return hr;
}

HRESULT CCalendarDeskBand::UpdateDeskband()
{
    CComPtr<IInputObjectSite> spInputSite;
    HRESULT hr = GetSite(IID_IInputObjectSite,
        reinterpret_cast<void**>(&spInputSite));

    if(SUCCEEDED(hr))
    {
        CComQIPtr<IOleCommandTarget> spOleCmdTarget = spInputSite;

        if(spOleCmdTarget)
        {
            // m_nBandID must be `int' or bandID variant must be explicitly
            // set to VT_I4, otherwise IDeskBand::GetBandInfo won't
            // be called by the system.
            CComVariant bandID(m_nBandID);

            hr = spOleCmdTarget->Exec(&CGID_DeskBand,
                DBID_BANDINFOCHANGED, OLECMDEXECOPT_DODEFAULT, &bandID, NULL);
            ATLASSERT(SUCCEEDED(hr));

            if(SUCCEEDED(hr))
                m_wndCalendar.UpdateCaption();
        }
    }

    return hr;
}

////////////////////////////////////////////////////////////////////////////////
// IObjectWithSite

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::SetSite( 
    /* [in] */ IUnknown *pUnkSite)
{
    HRESULT hr = __super::SetSite(pUnkSite);

    if(SUCCEEDED(hr) && pUnkSite) // pUnkSite is NULL when band is being destroyed
    {
        CComQIPtr<IOleWindow> spOleWindow = pUnkSite;

        if(spOleWindow)
        {
            HWND hwndParent = NULL;
            hr = spOleWindow->GetWindow(&hwndParent);

            if(SUCCEEDED(hr))
            {
                m_wndCalendar.Create(hwndParent,
                    static_cast<IDeskBand*>(this), pUnkSite, &m_dateFormat);

                if(!m_wndCalendar.IsWindow())
                    hr = E_FAIL;
            }
        }
    }

    return hr;
}

////////////////////////////////////////////////////////////////////////////////
// IInputObject

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::UIActivateIO(
    /* [in] */ BOOL fActivate,
    /* [unique][in] */ PMSG /*pMsg*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IInputObject::UIActivateIO (%s)\n"),
        (fActivate ? _T("TRUE") : _T("FALSE")));

    if(fActivate)
        m_wndCalendar.SetFocus();

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::HasFocusIO()
{
    ATLTRACE(atlTraceCOM, 2, _T("IInputObject::HasFocusIO\n"));

    return (m_wndCalendar.HasFocus() ? S_OK : S_FALSE);
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::TranslateAcceleratorIO(
    /* [in] */ PMSG /*pMsg*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IInputObject::TranslateAcceleratorIO\n"));

    return S_FALSE;
}

////////////////////////////////////////////////////////////////////////////////
// IContextMenu

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::QueryContextMenu(
    /* [in] */ HMENU hMenu,
    /* [in] */ UINT indexMenu,
    /* [in] */ UINT idCmdFirst,
    /* [in] */ UINT /*idCmdLast*/,
    /* [in] */ UINT uFlags)
{
    ATLTRACE(atlTraceCOM, 2, _T("IContextMenu::QueryContextMenu\n"));

    if(CMF_DEFAULTONLY & uFlags)
        return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);

    // Add a seperator
    ::InsertMenu(hMenu, 
        indexMenu,
        MF_SEPARATOR | MF_BYPOSITION,
        idCmdFirst + IDM_SEPARATOR_OFFSET, 0);

    // Add the new menu item
    CString sCaption;
    sCaption.LoadString(IDS_DESKBANDSETTINGS);

    ::InsertMenu(hMenu, 
        indexMenu, 
        MF_STRING | MF_BYPOSITION,
        idCmdFirst + IDM_SETTINGS_OFFSET,
        sCaption);

    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, IDM_SETTINGS_OFFSET + 1);
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::InvokeCommand( 
    /* [in] */ LPCMINVOKECOMMANDINFO pici)
{
    ATLTRACE(atlTraceCOM, 2, _T("IContextMenu::InvokeCommand\n"));

    if(!pici) return E_INVALIDARG;

    if(LOWORD(pici->lpVerb) == IDM_SETTINGS_OFFSET)
    {
        ATLASSERT(m_wndCalendar.IsWindow());
        CDateFormatSettings dlgSettings;
        const INT_PTR res = dlgSettings.DoModal(m_wndCalendar,
            reinterpret_cast<LPARAM>(&m_dateFormat));

        if(res == IDOK)
        {
            m_dateFormat = dlgSettings.m_dateFormat;
            m_bRequiresSave = true;
            
            const HRESULT hr = UpdateDeskband();
            ATLASSERT(SUCCEEDED(hr));
        }
    }

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::GetCommandString( 
    /* [in] */ UINT_PTR /*idCmd*/,
    /* [in] */ UINT /*uType*/,
    /* [in] */ UINT* /*pReserved*/,
    /* [out] */ LPSTR /*pszName*/,
    /* [in] */ UINT /*cchMax*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IContextMenu::GetCommandString\n"));

    return S_OK;
}

////////////////////////////////////////////////////////////////////////////////
// IDeskBand

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::GetBandInfo( 
    /* [in] */ DWORD dwBandID,
    /* [in] */ DWORD dwViewMode,
    /* [out][in] */ DESKBANDINFO *pdbi)
{
    ATLTRACE(atlTraceCOM, 2, _T("IDeskBand::GetBandInfo\n"));

    if(!pdbi) return E_INVALIDARG;

    m_nBandID = dwBandID;
    m_dwViewMode = dwViewMode;

    if(pdbi->dwMask & DBIM_MODEFLAGS)
    {
	    pdbi->dwModeFlags = DBIMF_VARIABLEHEIGHT;
    }

    if(pdbi->dwMask & DBIM_MINSIZE)
    {
        pdbi->ptMinSize = m_wndCalendar.CalcIdealSize();
    }

    if(pdbi->dwMask & DBIM_MAXSIZE)
    {
        // the band object has no limit for its maximum height
        pdbi->ptMaxSize.x = -1;
        pdbi->ptMaxSize.y = -1;
    }

    if(pdbi->dwMask & DBIM_INTEGRAL)
    {
        pdbi->ptIntegral.x = 1;
        pdbi->ptIntegral.y = 1;
    }

    if(pdbi->dwMask & DBIM_ACTUAL)
    {
        pdbi->ptActual = m_wndCalendar.CalcIdealSize();
    }

    if(pdbi->dwMask & DBIM_TITLE)
    {
        if(dwViewMode == DBIF_VIEWMODE_FLOATING)
        {
            CString sDate;
            sDate.LoadString(IDS_DATE);
            lstrcpynW(pdbi->wszTitle, sDate, ARRAYSIZE(pdbi->wszTitle));
        }
        else pdbi->dwMask &= ~DBIM_TITLE; // do not show title
    }

    if(pdbi->dwMask & DBIM_BKCOLOR)
	{
		//Use the default background color by removing this flag.
		pdbi->dwMask &= ~DBIM_BKCOLOR;
	}

    return S_OK;
}

////////////////////////////////////////////////////////////////////////////////
// IDeskBand2
HRESULT STDMETHODCALLTYPE CCalendarDeskBand::CanRenderComposited( 
    /* [out] */ BOOL *pfCanRenderComposited)
{
    if(pfCanRenderComposited)
        *pfCanRenderComposited = TRUE;

    return S_OK;
}
        
HRESULT STDMETHODCALLTYPE CCalendarDeskBand::SetCompositionState( 
    /* [in] */ BOOL fCompositionEnabled)
{
    m_bCompositionEnabled = fCompositionEnabled;
    m_wndCalendar.Invalidate();

    return S_OK;
}
        
HRESULT STDMETHODCALLTYPE CCalendarDeskBand::GetCompositionState( 
    /* [out] */ BOOL *pfCompositionEnabled)
{
    if(pfCompositionEnabled)
        *pfCompositionEnabled = m_bCompositionEnabled;

    return S_OK;
}

////////////////////////////////////////////////////////////////////////////////
// IOleWindow

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::GetWindow( 
    /* [out] */ HWND *phwnd)
{
    ATLTRACE(atlTraceCOM, 2, _T("IOleWindow::GetWindow\n"));

    if(phwnd) *phwnd = m_wndCalendar;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::ContextSensitiveHelp( 
    /* [in] */ BOOL /*fEnterMode*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IOleWindow::ContextSensitiveHelp\n"));

    ATLTRACENOTIMPL("IOleWindow::ContextSensitiveHelp");
}

////////////////////////////////////////////////////////////////////////////////
// IDockingWindow

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::ShowDW( 
    /* [in] */ BOOL fShow)
{
    ATLTRACE(atlTraceCOM, 2, _T("IDockingWindow::ShowDW\n"));

    if(m_wndCalendar)
        m_wndCalendar.ShowWindow(fShow ? SW_SHOW : SW_HIDE);

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::CloseDW( 
    /* [in] */ DWORD /*dwReserved*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IDockingWindow::CloseDW\n"));

    if(m_wndCalendar)
    {
        m_wndCalendar.ShowWindow(SW_HIDE);
        m_wndCalendar.DestroyWindow();
    }

    return S_OK;
}

HRESULT STDMETHODCALLTYPE CCalendarDeskBand::ResizeBorderDW( 
    /* [in] */ LPCRECT prcBorder,
    /* [in] */ IUnknown *punkToolbarSite,
    /* [in] */ BOOL /*fReserved*/)
{
    ATLTRACE(atlTraceCOM, 2, _T("IDockingWindow::ResizeBorderDW\n"));

    if(!m_wndCalendar) return S_OK;

    CComQIPtr<IDockingWindowSite> spDockingWindowSite = punkToolbarSite;

    if(spDockingWindowSite)
    {
        BORDERWIDTHS bw = { 0 };
        bw.top = bw.bottom = ::GetSystemMetrics(SM_CYBORDER);
        bw.left = bw.right = ::GetSystemMetrics(SM_CXBORDER);

        HRESULT hr = spDockingWindowSite->RequestBorderSpaceDW(
            static_cast<IDeskBand*>(this), &bw);

        if(SUCCEEDED(hr))
        {
            HRESULT hr = spDockingWindowSite->SetBorderSpaceDW(
                static_cast<IDeskBand*>(this), &bw);

            if(SUCCEEDED(hr))
            {
                m_wndCalendar.MoveWindow(prcBorder);
                return S_OK;
            }
        }
    }

    return E_FAIL;
}

////////////////////////////////////////////////////////////////////////////////
// IPersistStreamImpl

HRESULT CCalendarDeskBand::IPersistStreamInit_Load(
    LPSTREAM pStm,
    const ATL_PROPMAP_ENTRY* pMap)
{
    if(m_bstrDateFormat)
        m_bstrDateFormat.Empty();

    const HRESULT hr = __super::IPersistStreamInit_Load(pStm, pMap);

    if(SUCCEEDED(hr))
        m_dateFormat.dateFormat = m_bstrDateFormat;

    return hr;
}

HRESULT CCalendarDeskBand::IPersistStreamInit_Save(
    LPSTREAM pStm,
    BOOL fClearDirty,
    const ATL_PROPMAP_ENTRY* pMap)
{
    m_bstrDateFormat = m_dateFormat.dateFormat;

    return __super::IPersistStreamInit_Save(pStm, fClearDirty, pMap);
}

////////////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
More than ten years of C++ native development, and counting.

Smile | :)

Comments and Discussions