Click here to Skip to main content
15,886,519 members
Articles / Programming Languages / C++

The Complete Idiot's Guide to Writing Shell Extensions - Part VII

Rate me:
Please Sign up or sign in to vote.
5.00/5 (31 votes)
30 May 2006 515.8K   5.1K   128  
A tutorial on using owner-drawn menus in a context menu shell extensions, and on making a context menu extension that responds to a right-click in a directory background.
// BkgndCtxMenuExt.cpp : Implementation of CBkgndCtxMenuExt

#include "stdafx.h"
#include "resource.h"
#include "DirBkgndExt.h"
#include "BkgndCtxMenuExt.h"

/////////////////////////////////////////////////////////////////////////////
// CBkgndCtxMenuExt

STDMETHODIMP CBkgndCtxMenuExt::Initialize (
    LPCITEMIDLIST pidlFolder, LPDATAOBJECT  pDO, HKEY hkeyProgID )
{
    // pidlFolder is the PIDL of the directory that was clicked in.  pDO is
    // NULL, since there is no selection to operate on.
    // We get the conventional path of the directory from its PIDL with the
    // SHGetPathFromIDList() API.
    return SHGetPathFromIDList ( pidlFolder, m_szDirClickedIn ) ? S_OK : E_INVALIDARG;
}

STDMETHODIMP CBkgndCtxMenuExt::QueryContextMenu (
    HMENU hmenu, UINT uIndex, UINT uidCmdFirst,
    UINT uidCmdLast, UINT uFlags )
{
UINT uCmdID = uidCmdFirst;

    // First insert item 0 - the English version.
    InsertMenu ( hmenu, uIndex, MF_BYPOSITION, uCmdID++, _T("CLICK HERE!") );

    // uIndex seems to be -1 all the time (on 98 and 2K).  If it _is_ -1,
    // we can't increment it, 'cause it would become 0, which would make our
    // second item appear at the _top_ of the menu.
    if ( 0xFFFFFFFF != uIndex )
        uIndex++;

    // Then insert item 1 - the French version.
    InsertMenu ( hmenu, uIndex, MF_BYPOSITION, uCmdID, _T("CLIQUEZ ICI!") );

    return MAKE_HRESULT ( SEVERITY_SUCCESS, FACILITY_NULL, 2 );
}

STDMETHODIMP CBkgndCtxMenuExt::GetCommandString (
    UINT uCmd, UINT uFlags, UINT* puReserved,
    LPSTR pszName, UINT cchMax )
{
USES_CONVERSION;
static LPCTSTR szEnglishHelpString = _T("This is our extension that doesn't do much.");
static LPCTSTR szFrenchHelpString = _T("Voici notre extension qui ne fait rien d'important.");
static LPCTSTR szItem0Verb = _T("CPBkgndExt0");
static LPCTSTR szItem1Verb = _T("CPBkgndExt1");

    // Check idCmd, it must be 0 or 1 since we have two menu items.
    if ( uCmd > 1 )
        return E_INVALIDARG;

    // If Explorer is asking for a help string, copy our string into the
    // supplied buffer.
    if ( uFlags & GCS_HELPTEXT )
        {
        if ( uFlags & GCS_UNICODE )
            {
            // We need to cast pszName to a Unicode string, and then use the
            // Unicode string copy API.
            lstrcpynW ( (LPWSTR) pszName, 
                        T2CW((0 == uCmd) ? szEnglishHelpString : szFrenchHelpString),
                        cchMax );
            }
        else
            {
            // Use the ANSI string copy API to return the help string.
            lstrcpynA ( pszName, 
                        T2CA((0 == uCmd) ? szEnglishHelpString : szFrenchHelpString),
                        cchMax );
            }
        }

    // XP requires that all background ctx menu items have unique verbs, so we
    // need to return verb names for our two items.
    if ( GCS_VERBA == uFlags )
        lstrcpynA ( pszName, T2CA((0 == uCmd) ? szItem0Verb : szItem1Verb), cchMax );
    else if ( GCS_VERBW == uFlags )
        lstrcpynW ( (LPWSTR) pszName, T2CW((0 == uCmd) ? szItem0Verb : szItem1Verb), cchMax );

    return S_OK;
}

STDMETHODIMP CBkgndCtxMenuExt::InvokeCommand ( LPCMINVOKECOMMANDINFO pInfo )
{
WORD wCmd = LOWORD(pInfo->lpVerb);

    // If lpVerb really points to a string, ignore this function call and bail out.
    if ( 0 != HIWORD(pInfo->lpVerb) )
        return E_INVALIDARG;

    // The command ID must be 0 or 1 since we have two menu items.
    if ( wCmd > 1 )
        return E_INVALIDARG;

TCHAR szMessage[MAX_PATH+32];

    if ( 0 == wCmd )
        {
        // Item 0 is the English one...
        wsprintf ( szMessage, _T("Thanks for clicking in\n%s"), m_szDirClickedIn );
        }
    else
        {
        // and item 1 is the French one.
        wsprintf ( szMessage, _T("Merci d'avoir cliqu� dans\n%s"), m_szDirClickedIn );
        }

    MessageBox ( pInfo->hwnd, szMessage, _T("Dir Background Extension"),
                 MB_ICONINFORMATION );

    return S_OK;
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) VMware
United States United States
Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.

Mike was a VC MVP from 2005 to 2009.

Comments and Discussions