Click here to Skip to main content
15,891,529 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.1K   3.2K   71  
A simple Calendar utility that demonstrates basic Shell extensibility techniques: desk band, tray notification icon, locales.
#include "StdAfx.h"
#include "CalendarWindow.h"
#include "CalendarGuids.h"
#include "Utils.h"

/////////////////////////////////////////////////////////////////////////////
//
const LPCTSTR DESKBAND_BIN_NAME = TEXT("CalendarDeskBand.dll");

////////////////////////////////////////////////////////////////////////////////
// 
CString StringFromGUID2(REFGUID rguid)
{
    OLECHAR szGuid[MAX_GUID_STRING_LEN] = { 0 };
    ::StringFromGUID2(rguid, szGuid, MAX_GUID_STRING_LEN);

    return szGuid;
}

DWORD RunAsAdmin(HWND hWnd, LPCTSTR lpFile, LPCTSTR lpParameters)
{
    SHELLEXECUTEINFO sei = { 0 };

    const ULONG fMask =
        SEE_MASK_FLAG_DDEWAIT |
        SEE_MASK_FLAG_NO_UI |
        SEE_MASK_NOCLOSEPROCESS;

    sei.cbSize          = sizeof(SHELLEXECUTEINFOW);
    sei.hwnd            = hWnd;
    sei.fMask           = fMask;
    sei.lpVerb          = TEXT("runas");
    sei.lpFile          = lpFile;
    sei.lpParameters    = lpParameters;
    sei.nShow           = SW_SHOWNORMAL;

    if(::ShellExecuteEx(&sei))
    {
        CHandle handle(sei.hProcess);
        const DWORD dwWaitRes = ::WaitForSingleObject(handle, INFINITE);

        if(dwWaitRes == WAIT_OBJECT_0)
        {
            DWORD dwExitCode = ERROR_SUCCESS;
            const BOOL bRes = ::GetExitCodeProcess(handle, &dwExitCode);
            ATLASSERT(bRes);

            return dwExitCode;
        }
    }

    return ::GetLastError();
}

//////////////////////////////////////////////////////////////////////////////
// static
UINT CCalendarWindow::s_uTaskbarRestart =
    ::RegisterWindowMessage(_T("TaskbarCreated"));

//////////////////////////////////////////////////////////////////////////////
//
CCalendarWindow::CCalendarWindow() :
    m_ctrlMonthCal(MONTHCAL_CLASS, this, MONTHCAL_MSG_MAP_ID),
    m_hSysTrayIcon(NULL),
    m_hWndIconSmall(NULL),
    m_hWndIcon(NULL)
{
}

LRESULT CCalendarWindow::OnCreate(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM lParam,
    BOOL& /*bHandled*/)
{
    LPCREATESTRUCT pcs = reinterpret_cast<LPCREATESTRUCT>(lParam);

    if(!InitIcons())
        return -1;

    if(!CreateChildren(pcs))
        return -1;

    if(!InitMenu())
        return -1;

    if(!InitSettingsPath())
        return -1;

    m_dlgOptions.LoadSettings(m_pathSettings);

    if(!ApplySettings(/*Resize=*/true))
        return -1;

    if(!UpdateSysTrayIcon(NIM_ADD))
        return -1;

    if(SetUpdateTimer() == 0)
        return -1;

    return 0;
}

LRESULT CCalendarWindow::OnSize(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM lParam,
    BOOL& /*bHandled*/)
{
    m_ctrlMonthCal.MoveWindow(0, 0, LOWORD(lParam), HIWORD(lParam), FALSE);

    return 0;
}

LRESULT CCalendarWindow::OnSysCommand(
    UINT /*uMsg*/,
    WPARAM wParam,
    LPARAM /*lParam*/,
    BOOL& bHandled)
{
    switch(wParam)
    {
    case IDC_OPTIONS:
        ShowOptions();
        break;
    case IDC_ABOUT:
        ShowAboutBox();
        break;
    default:
        bHandled = FALSE;
    }

    return 0;
}

LRESULT CCalendarWindow::OnDestroy(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    KillTimer(UPDATE_TIMER_ID);

    if(m_hSysTrayIcon)
    {
        ATLVERIFY(UpdateSysTrayIcon(NIM_DELETE));
        ::DestroyIcon(m_hSysTrayIcon);
    }

    ATLASSERT(m_hWndIconSmall != NULL);
    ::DestroyIcon(m_hWndIconSmall);

    ATLASSERT(m_hWndIcon != NULL);
    ::DestroyIcon(m_hWndIcon);

    ::PostQuitMessage(0);

    return 0;
}

LRESULT CCalendarWindow::OnSysTrayNotify(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM lParam,
    BOOL& /*bHandled*/)
{
    switch(lParam)
    {
    case WM_LBUTTONUP:
        ShowAndActivate();
        break;
    case WM_RBUTTONUP:
        HandleContextCommand(ShowContextMenu());
        break;
    }

    return 0;
}

LRESULT CCalendarWindow::OnClose(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    ShowWindow(SW_HIDE);

    return 0;
}

LRESULT CCalendarWindow::OnTimeChange(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    SYSTEMTIME stNow = { 0 };
    ::GetLocalTime(&stNow);

    UpdateTodayDate(stNow);

    return 0;
}

LRESULT CCalendarWindow::OnTimer(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    SYSTEMTIME stNow = { 0 };
    ::GetLocalTime(&stNow);

    SYSTEMTIME stCtrlToday = { 0 };
    MonthCal_GetToday(m_ctrlMonthCal, &stCtrlToday);

    if(stNow.wDay != stCtrlToday.wDay)
        UpdateTodayDate(stNow);

    return 0;
}

LRESULT CCalendarWindow::OnEndSession(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    DestroyWindow();

    return TRUE; // system can continue to end session
}

LRESULT CCalendarWindow::OnPowerBroadcast(
    UINT /*uMsg*/,
    WPARAM wParam,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    if(wParam == PBT_APMRESUMECRITICAL ||
       wParam == PBT_APMRESUMESUSPEND ||
       wParam == PBT_APMRESUMESTANDBY)
    {
        // re-enable timer
        ATLVERIFY(SetUpdateTimer() != 0);

        // refresh tray icon (sometimes it is not refreshed after resume)
        ATLVERIFY(UpdateSysTrayIcon(NIM_MODIFY));
    }

    return 0;
}

LRESULT CCalendarWindow::OnSetFocus(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    m_ctrlMonthCal.SetFocus();

    return 0;
}

LRESULT CCalendarWindow::OnTaskbarRestart(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    return (UpdateSysTrayIcon(NIM_ADD) ? 0L : -1L);
}

bool CCalendarWindow::InitMenu(void)
{
    HMENU hSysMenu = GetSystemMenu(FALSE);
    ATLVERIFY(::AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL));

    CString strMenuText;
    ATLVERIFY(strMenuText.LoadString(IDS_OPTIONS));

    BOOL bAppend = ::AppendMenu(hSysMenu, MF_STRING,
        IDC_OPTIONS, strMenuText);
    ATLASSERT(bAppend);

    ATLVERIFY(strMenuText.LoadString(IDS_ABOUT));

    bAppend = ::AppendMenu(hSysMenu, MF_STRING,
        IDC_ABOUT, strMenuText);

    return (bAppend != FALSE);
}

bool CCalendarWindow::InitIcons()
{
    m_hWndIconSmall = (HICON)::LoadImage(
        _AtlBaseModule.GetResourceInstance(),
        MAKEINTRESOURCE(IDI_CALENDAR),
        IMAGE_ICON,
        ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON),
        LR_DEFAULTCOLOR);
    ATLASSERT(m_hWndIconSmall != NULL);

    SetIcon(m_hWndIconSmall, /*bBigIcon=*/FALSE);

    m_hWndIcon = (HICON)::LoadImage(
        _AtlBaseModule.GetResourceInstance(),
        MAKEINTRESOURCE(IDI_CALENDAR),
        IMAGE_ICON,
        ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON),
        LR_DEFAULTCOLOR);
    ATLASSERT(m_hWndIcon != NULL);

    SetIcon(m_hWndIcon, /*bBigIcon=*/TRUE);

    return (m_hWndIconSmall && m_hWndIcon);
}

bool CCalendarWindow::CreateChildren(LPCREATESTRUCT /*pcs*/)
{
    RECT rc = { 0 };
    GetClientRect(&rc);
    ATLVERIFY(m_ctrlMonthCal.Create(m_hWnd, rc) != NULL);
    
    return (m_ctrlMonthCal.IsWindow() != FALSE);
}

bool CCalendarWindow::InitSettingsPath()
{
    ATLASSERT(m_pathSettings.m_strPath.IsEmpty());

    // Try our folder
    TCHAR sz[MAX_PATH] = { TEXT('\0') };
    ::GetModuleFileName(NULL, sz, MAX_PATH);

    CPath pathFilename(sz);
    pathFilename.RenameExtension(TEXT(".ini"));

    if(pathFilename.FileExists())
        m_pathSettings = pathFilename;
    else
        ATLVERIFY(::MakeSettingsFilename(m_pathSettings));

    return (!m_pathSettings.m_strPath.IsEmpty());
}

bool CCalendarWindow::ApplySettings(bool bResize)
{
    // Transparency
    if(!ApplyTransparency()) return false;

    // Window size and pos (default - one month size)
    if(!ApplyWindowPos(bResize)) return false;

    // Update calendar control
    if(!ApplyCalendarProperties()) return false;

    // Update system tray icon properties
    if(!ApplySysTrayProperties()) return false;

    // Update deskband state
    if(!ApplyDeskbandProperties()) return false;

    return true;
}

bool CCalendarWindow::ApplyTransparency()
{
    const BOOL bRes = ::SetLayeredWindowAttributes(m_hWnd,
        0, m_dlgOptions.m_byteAlpha, LWA_ALPHA);
    ATLASSERT(bRes);

    return (bRes != FALSE);
}

bool CCalendarWindow::ApplyWindowPos(bool bResize)
{
    CRect rcWin;

    if(bResize)
    {
        GetWindowRect(&rcWin);

        CRect rcMin;
        MonthCal_GetMinReqRect(m_ctrlMonthCal, &rcMin);

        rcWin.right = rcWin.left + max(rcMin.Width(),
            (int)MonthCal_GetMaxTodayWidth(m_ctrlMonthCal));
        rcWin.bottom = rcWin.top + rcMin.Height();

        // Do some margins
        rcWin.InflateRect(0, 0,
            rcMin.Width() / 3, rcMin.Height() / 2);
    }

    // Apply changes
    HWND hWndInsertAfter = (m_dlgOptions.m_bTopmost ?
        HWND_TOPMOST : HWND_NOTOPMOST);
    UINT nFlags = SWP_DEFERERASE |
        (bResize ? 0 : (SWP_NOSIZE | SWP_NOMOVE));

    const BOOL bRes = SetWindowPos(hWndInsertAfter, &rcWin, nFlags);
    ATLASSERT(bRes);

    return (bRes != FALSE);
}

bool CCalendarWindow::ApplyCalendarProperties()
{
    BOOL bRes = TRUE;

    // Display week numbers
    const bool bWeekNumbersShown =
        ((m_ctrlMonthCal.GetStyle() & MCS_WEEKNUMBERS) != 0);

    if(bWeekNumbersShown != m_dlgOptions.m_bWeekNumbers)
    {
        DWORD dwRemove = 0;
        DWORD dwAdd = 0;

        if(m_dlgOptions.m_bWeekNumbers)
            dwAdd = MCS_WEEKNUMBERS;
        else
            dwRemove = MCS_WEEKNUMBERS;

        bRes = m_ctrlMonthCal.ModifyStyle(dwRemove, dwAdd);
        ATLASSERT(bRes);
    }

    return (bRes != FALSE);
}

bool CCalendarWindow::ApplySysTrayProperties()
{
    bool bRes = true;

    if(m_hSysTrayIcon)
    {
        bRes = UpdateSysTrayIcon(NIM_MODIFY);
        ATLASSERT(bRes);
    }

    return bRes;
}

bool CCalendarWindow::ApplyDeskbandProperties() const
{
    BOOL fWow64 = TRUE;
    ::IsWow64Process(::GetCurrentProcess(), &fWow64);

    if(fWow64) return true; // cannot install 32-bit deskband for Win64

    const CPath& deskbandPath = GetDeskbandModulePath();
    const bool bDeskbandInstalled = DeskbandInstalled();

    if(m_dlgOptions.m_bDeskband && !bDeskbandInstalled)
    {   // install and show
        if(!ExtractDeskbandBinary(deskbandPath))
            return false;

        if(!RegisterDeskband(deskbandPath, true))
            return false;

        // Show the desk band only if wasn't previously installed;
        // otherwise leave the choice to user.
        if(!ShowDeskband())
            return false;
    }
    else if(!m_dlgOptions.m_bDeskband && bDeskbandInstalled)
    {   // hide and uninstall

        // If there are leftovers of previous deskband objects that cannot
        // be uninstalled (missing files, etc), continue anyway.
        // No point to fail here.
        HideDeskband();
        RegisterDeskband(deskbandPath, false);
        DeleteDeskbandBinary(deskbandPath);
    }

    return true;
}

bool CCalendarWindow::DeskbandInstalled() const
{
    const CString& keyName = TEXT("CLSID\\") +
        ::StringFromGUID2(CLSID_CalendarDeskBand);

    CRegKey keyDeskband;
    const LONG lRes = keyDeskband.Open(HKEY_CLASSES_ROOT, keyName, KEY_READ);

    return (lRes == ERROR_SUCCESS);
}

CPath CCalendarWindow::GetDeskbandModulePath() const
{
    CPath deskbandPath = m_pathSettings;
    deskbandPath.RemoveFileSpec();
    deskbandPath.Append(DESKBAND_BIN_NAME);

    return deskbandPath;
}

bool CCalendarWindow::ExtractDeskbandBinary(const CPath& deskbandPath) const
{
    HRSRC hResInfo = reinterpret_cast<HRSRC>(
        ::FindResource(
            NULL,
            MAKEINTRESOURCE(IDR_DESKBAND_BIN),
            TEXT("DESKBAND")
        )
    );
    ATLASSERT(hResInfo);

    if(!hResInfo) return false;

    HGLOBAL hResData = ::LoadResource(NULL, hResInfo);
    ATLASSERT(hResData);

    if(!hResData) return false;

    LPVOID pDeskbandBinData = ::LockResource(hResData);
    ATLASSERT(pDeskbandBinData);

    if(!pDeskbandBinData) return false;

    const DWORD dwDeskbandBinSize = ::SizeofResource(NULL, hResInfo);
    ATLASSERT(dwDeskbandBinSize > 0);

    if(!dwDeskbandBinSize) return false;

    CAtlFile deskbandBin;
    HRESULT hr = deskbandBin.Create(deskbandPath,
        GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
    ATLASSERT(SUCCEEDED(hr));

    if(FAILED(hr)) return false;

    hr = deskbandBin.Write(pDeskbandBinData, dwDeskbandBinSize);
    ATLASSERT(SUCCEEDED(hr));
    if(FAILED(hr)) return false;

    return true;
}

bool CCalendarWindow::DeleteDeskbandBinary(const CPath& deskbandPath) const
{
    if(!deskbandPath.FileExists()) return true;

    CPath deskbandDir = deskbandPath;
    deskbandDir.RemoveFileSpec();

    TCHAR szTmpFile[MAX_PATH + 1] = { 0 };
    const UINT nRet = ::GetTempFileName(deskbandDir, TEXT("del"), 0, szTmpFile);
    ATLASSERT(nRet);

    if(!nRet) return false;

    BOOL bRes = ::MoveFileEx(deskbandPath, szTmpFile, MOVEFILE_REPLACE_EXISTING);
    ATLASSERT(bRes);

    if(!bRes) return false;

    // If user is an admin, then the file gets deleted on the next reboot;
    // for standard users the MOVEFILE_DELAY_UNTIL_REBOOT flag is unavailable,
    // so we delete with DeleteFile, which is likely to fail because
    // the deskband DLL will be in use by Explorer.exe. However, the file
    // will be deleted when last handle to it is closed.
    if(::IsUserAnAdmin())
    {
        bRes = ::MoveFileEx(szTmpFile, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
    }
    else
    {
        ::DeleteFile(szTmpFile);
        bRes = TRUE;
    }
    ATLASSERT(bRes);

    return (bRes != FALSE);
}

bool CCalendarWindow::RegisterDeskband(const CPath& deskbandPath, bool bRegister) const
{
    bool bRes = false;
    if(::IsVistaOrHigher())
    {
        bRes = (RegisterDeskbandSecure(deskbandPath, bRegister) == ERROR_SUCCESS);
    }
    else    // XP
    {
        bRes = SUCCEEDED(RegisterDeskbandXP(deskbandPath, bRegister));
    }

    return bRes;
}

struct LibraryLoader
{
    LibraryLoader(LPCTSTR pszPath) : hModule(::LoadLibrary(pszPath)) {}
    ~LibraryLoader() { if(hModule) ::FreeLibrary(hModule); }

    operator HMODULE() const { return hModule; }

    HMODULE hModule;
};

HRESULT CCalendarWindow::RegisterDeskbandXP(const CPath& deskbandPath, bool bRegister) const
{
    LibraryLoader deskbandLib(deskbandPath);

    HRESULT hr = E_FAIL;
    if(deskbandLib)
    {
        typedef HRESULT (STDAPICALLTYPE *REGISTERSERVERPROC)(void);

        const LPCSTR pszProcName = (bRegister ?
            "DllRegisterServer" : "DllUnregisterServer");

        REGISTERSERVERPROC DllRegisterProc =
            reinterpret_cast<REGISTERSERVERPROC>(
                ::GetProcAddress(deskbandLib, pszProcName));

        if(DllRegisterProc)
        {
            hr = DllRegisterProc();
            ATLASSERT(SUCCEEDED(hr));

            return hr;
        }
    }

    hr = HRESULT_FROM_WIN32(::GetLastError());
    ATLASSERT(FALSE);

    return hr;
}

DWORD CCalendarWindow::RegisterDeskbandSecure(CPath deskbandPath, bool bRegister) const
{
    deskbandPath.QuoteSpaces();
    const CString& parameters = (bRegister ? TEXT("") : TEXT("/u ")) + deskbandPath;

    const DWORD dwExitCode = ::RunAsAdmin(m_hWnd, TEXT("REGSVR32.EXE"), parameters);
    ATLASSERT(dwExitCode == ERROR_SUCCESS);

    return dwExitCode;
}

bool CCalendarWindow::ShowDeskband() const
{
    CComPtr<ITrayDeskBand> spTrayDeskBand;
    HRESULT hr = spTrayDeskBand.CoCreateInstance(CLSID_TrayDeskBand);

    if(SUCCEEDED(hr))   // Vista and higher
    {
        hr = spTrayDeskBand->DeskBandRegistrationChanged();
        ATLASSERT(SUCCEEDED(hr));

        if(SUCCEEDED(hr))
        {
            hr = spTrayDeskBand->IsDeskBandShown(CLSID_CalendarDeskBand);
            ATLASSERT(SUCCEEDED(hr));

            if(SUCCEEDED(hr) && hr == S_FALSE)
                hr = spTrayDeskBand->ShowDeskBand(CLSID_CalendarDeskBand);
        }
    }
    else    // WinXP workaround
    {
        const CString& sAtom = ::StringFromGUID2(CLSID_CalendarDeskBand);

        if(!::GlobalFindAtom(sAtom))
            ::GlobalAddAtom(sAtom);

        // Beware! SHLoadInProc is not implemented under Vista and higher.
        hr = ::SHLoadInProc(CLSID_CalendarDeskBand);
        ATLASSERT(SUCCEEDED(hr));
    }

    return SUCCEEDED(hr);
}

bool CCalendarWindow::HideDeskband() const
{
    CComPtr<ITrayDeskBand> spTrayDeskBand;
    HRESULT hr = spTrayDeskBand.CoCreateInstance(CLSID_TrayDeskBand);

    if(SUCCEEDED(hr))   // Vista and higher
    {
        hr = spTrayDeskBand->IsDeskBandShown(CLSID_CalendarDeskBand);

        if(hr == S_OK)
            hr = spTrayDeskBand->HideDeskBand(CLSID_CalendarDeskBand);
    }
    else    // WinXP
    {
        CComPtr<IBandSite> spBandSite;
        hr = spBandSite.CoCreateInstance(CLSID_TrayBandSiteService);

        if(SUCCEEDED(hr))
        {
            DWORD dwBandID = 0;
            const UINT nBandCount = spBandSite->EnumBands((UINT)-1, &dwBandID);

            for(UINT i = 0; i < nBandCount; ++i)
            {
                spBandSite->EnumBands(i, &dwBandID);

                CComPtr<IPersist> spPersist;
                hr = spBandSite->GetBandObject(dwBandID, IID_IPersist, (void**)&spPersist);
                if(SUCCEEDED(hr))
                {
                    CLSID clsid = CLSID_NULL;
                    hr = spPersist->GetClassID(&clsid);

                    if(SUCCEEDED(hr) && ::IsEqualCLSID(clsid, CLSID_CalendarDeskBand))
                    {
                        hr = spBandSite->RemoveBand(dwBandID);
                        break;
                    }
                }
            }
        }
    }

    return SUCCEEDED(hr);
}

bool CCalendarWindow::ShowOptions(void)
{
    if(!IsWindowVisible() || IsIconic())
        ShowAndActivate();

    if(IDOK == m_dlgOptions.DoModal(m_hWnd))
    {
        ATLVERIFY(m_dlgOptions.SaveSettings(m_pathSettings));
        ATLVERIFY(ApplySettings(/*Resize=*/FALSE));
    }

    return true;
}

bool CCalendarWindow::UpdateSysTrayIcon(DWORD dwAction)
{
    NOTIFYICONDATA nid = { 0 };
    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd = m_hWnd;

    HICON hIconPrev = NULL;

    switch(dwAction)
    {
    case NIM_MODIFY:
        hIconPrev = m_hSysTrayIcon;
        // fall through
    case NIM_ADD:
        {
            SYSTEMTIME stToday = { 0 };
            ::GetLocalTime(&stToday);
            const CString& strDateStr =
                m_dlgOptions.m_dateFormat.FormatDateString(stToday);

            m_hSysTrayIcon = LoadDayIcon(stToday);

            nid.uFlags |= NIF_TIP | NIF_ICON | NIF_MESSAGE;
            nid.uCallbackMessage = WM_SYSTRAYNOTIFY;
            lstrcpyn(nid.szTip, strDateStr, ARRAYSIZE(nid.szTip));
            nid.hIcon = m_hSysTrayIcon;
        }
        break;
    case NIM_DELETE:
        hIconPrev = m_hSysTrayIcon;
        break;
    }

    BOOL bRes = ::Shell_NotifyIcon(dwAction, &nid);
    ATLASSERT(bRes);

    if(hIconPrev != NULL)
        ::DestroyIcon(hIconPrev);

    return (bRes != FALSE);
}

// Note: This function relies on the fact that
//       daily icon ID's increase incrementally.
HICON CCalendarWindow::LoadDayIcon(const SYSTEMTIME& st)
{
    HICON hIcon = (HICON)::LoadImage(
        _AtlBaseModule.GetResourceInstance(),
        MAKEINTRESOURCE(IDI_DAY00 + st.wDay),
        IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);

    ATLASSERT(hIcon != NULL);

    return hIcon;
}

void CCalendarWindow::ShowAboutBox(void)
{
    MSGBOXPARAMS mbp = { 0 };
    mbp.cbSize      = sizeof(MSGBOXPARAMS);
    mbp.hwndOwner   = m_hWnd;
    mbp.hInstance   = _AtlBaseModule.GetResourceInstance();
    mbp.lpszText    = MAKEINTRESOURCE(IDS_ABOUTTEXT);
    mbp.lpszCaption = MAKEINTRESOURCE(IDS_ABOUTCAPTION);
    mbp.dwStyle     = MB_USERICON | MB_OK;
    mbp.lpszIcon    = MAKEINTRESOURCE(IDI_CALENDAR);
    mbp.dwLanguageId = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT);

    ::MessageBoxIndirect(&mbp);
}

int CCalendarWindow::ShowContextMenu(void)
{
    HMENU hMenu = ::LoadMenu(
        _AtlBaseModule.GetResourceInstance(),
        MAKEINTRESOURCE(IDM_CALENDAR));
    ATLASSERT(hMenu != NULL);

    HMENU hTrackPopup = ::GetSubMenu(hMenu, 0);
    ATLASSERT(hTrackPopup != NULL);

    const UINT nCheck = MF_BYCOMMAND |
        (IsWindowVisible() ? MF_CHECKED : MF_UNCHECKED);
    ::CheckMenuItem(hTrackPopup, IDC_CALENDAR, nCheck);

    POINT pt = { 0 };
    ::GetCursorPos(&pt);

    ::SetForegroundWindow(m_hWnd);

    int nCmd = ::TrackPopupMenu(hTrackPopup, TPM_RETURNCMD,
        pt.x, pt.y, 0, m_hWnd, NULL);

    PostMessage(WM_NULL);

    return nCmd;
}

void CCalendarWindow::HandleContextCommand(int nCmd)
{
    if(nCmd == 0) return;

    switch(nCmd)
    {
    case IDC_CALENDAR:
        if(IsWindowVisible())
            ShowWindow(SW_HIDE);
        else
            ShowAndActivate();
        break;
    case IDC_OPTIONS:
        ShowOptions();
        break;
    case IDC_ABOUT:
        ShowAboutBox();
        break;
    case IDC_EXIT:
        DestroyWindow();
        break;
    default:
        ATLASSERT(FALSE);
    }
}

UINT_PTR CCalendarWindow::SetUpdateTimer()
{
    // Wake up every 10 seconds.
    UINT_PTR nTimerId = SetTimer(UPDATE_TIMER_ID, 10 * 1000);

    ATLASSERT(nTimerId != 0);

    return nTimerId;
}

void CCalendarWindow::UpdateTodayDate(const SYSTEMTIME& stNow)
{
    MonthCal_SetToday(m_ctrlMonthCal, &stNow);

    ATLVERIFY(UpdateSysTrayIcon(NIM_MODIFY));
}

void CCalendarWindow::ShowAndActivate()
{
    if(!IsWindowVisible() || IsIconic())
        ShowWindow(SW_SHOWNORMAL);

    ::SetForegroundWindow(m_hWnd);
}

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

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