Platform SDK: Active Directory, ADSI, and Directory Services

Example Code for Implementation of the Context Menu COM Object

The following code fragment contains an implementation of the IShellExtInit and IContextMenu methods and the command handler methods for each menu command added by the IContextMenu::QueryContextMenu method. The context menu items display message boxes with various information (such as the contents of the two supported clipboard formats). For the full implementation of this sample, see the following MyContextMenu sample.

STDMETHODIMP CMyDsContextMenu::QueryContextMenu(HMENU hMenu,
                                         UINT indexMenu,
                                         UINT idCmdFirst,
                                         UINT idCmdLast,
                                         UINT uFlags)
{
    UINT idCmd = idCmdFirst;
    WCHAR szMenuText1[64];
    WCHAR szMenuText2[64];
    WCHAR szMenuText3[64];
    WCHAR szMenuText4[64];
    WCHAR *szMenuGetSupportedClipboardFormats = L"Get Supported Clipboard Formats";
    WCHAR *szMenuGetDsDisplaySpecOptions = L"Get Data Using DsDisplaySpecOptions Clipboard Format";
    WCHAR *szMenuGetDsObjectNames = L"Get Data Using DsObjectNames Clipboard Format";
    BOOL bAppendItems=TRUE;
 
    //Check the flags
    //Normal
    //Note that the administrative snap-ins will 
    //use only the CMF_NORMAL state. 
    //So CMF_EXPLORE will never be set for admin snap-ins.
    if ((uFlags & 0x000F) == CMF_NORMAL)  //Check == here, since CMF_NORMAL=0
    {
        wcscpy(szMenuText1, L"Do DoMenuCmd&1 (Normal)");
        wcscpy(szMenuText2, L"Do DoMenuCmd&2 (Normal)");
        wcscpy(szMenuText3, L"Do DoMenuCmd&3 (Normal)");
        wcscpy(szMenuText4, L"Do DoMenuCmd&4 (Normal)");
    }
    //Note that if the context menu is a shell extension,
    //the Windows shell may be displaying the object in Explorer view.
    //This can be handled differently based on what you need/want.
    else if (uFlags & CMF_EXPLORE)
    {
        wcscpy(szMenuText1, L"Do DoMenuCmd&1 (Explorer)");
        wcscpy(szMenuText2, L"Do DoMenuCmd&2 (Explorer)");
        wcscpy(szMenuText3, L"Do DoMenuCmd&3 (Explorer)");
        wcscpy(szMenuText4, L"Do DoMenuCmd&4 (Explorer)");
    }
    else if (uFlags & CMF_DEFAULTONLY)
    {
        bAppendItems = FALSE;
    }
    else
    {
        bAppendItems = FALSE;
    }
 
    //Insert the menu items.
    if (bAppendItems)
    {
        InsertMenuW(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
        
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText1);
 
        InsertMenuW(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText2);
 
        InsertMenuW(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText3);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuText4);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuGetSupportedClipboardFormats);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuGetDsDisplaySpecOptions);
 
        InsertMenuW(hMenu,
                   indexMenu++,
                   MF_STRING|MF_BYPOSITION,
                   idCmd++,
                   szMenuGetDsObjectNames);
 
 
        return ResultFromShort(idCmd-idCmdFirst); //Must return number of menu
        //items we added.
    }
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
{
 
    HRESULT hr = E_INVALIDARG;
 
 
    if (!HIWORD(lpcmi->lpVerb))
    {
        UINT idCmd = LOWORD(lpcmi->lpVerb);
 
        switch (idCmd)
        {
            case 0:
                hr = DoMenuCmd1(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;
 
            case 1:
                hr = DoMenuCmd2(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;
 
            case 2:
                hr = DoMenuCmd3(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
                break;
 
            case 3:
                hr = DoMenuCmd4(lpcmi->hwnd,
                                lpcmi->lpDirectory,
                                lpcmi->lpVerb,
                                lpcmi->lpParameters,
                                lpcmi->nShow);
            case 4:
                hr = GetSupportedClipboardFormats(lpcmi->hwnd,m_pDataObj);
                break;
 
            case 5:
                hr = GetDsDisplaySpecOptions(lpcmi->hwnd,m_pDataObj);
                break;
 
            case 6:
                hr = GetDsObjectNames(lpcmi->hwnd,m_pDataObj);
                break;
        }
    }
/*
    else if (HIWORD(lpcmi->lpVerb))
    {
    //Handle this for returning language independent command names.
 
    }
*/
    return hr;
}
 
 
//
//  FUNCTION: CMyDsContextMenu::GetCommandString
//
//  Called by the shell after the user has selected a 
//  menu item that was added in QueryContextMenu().
//
 
STDMETHODIMP CMyDsContextMenu::GetCommandString(UINT idCmd,
                                         UINT uFlags,
                                         UINT FAR *reserved,
                                         LPSTR pszName,
                                         UINT cchMax)
{
if ((uFlags & GCS_HELPTEXT) != 0)
{
    switch (idCmd)
    {
    //Need to cast pszName to a Unicode string.
    //TODO check pszName length against cchMax
        case 0:
            wcscpy((LPOLESTR)pszName, L"New menu item number 1");
            break;
 
        case 1:
            wcscpy((LPOLESTR)pszName, L"New menu item number 2");
            break;
 
        case 2:
            wcscpy((LPOLESTR)pszName, L"New menu item number 3");
            break;
 
        case 3:
            wcscpy((LPOLESTR)pszName, L"New menu item number 4");
            break;
        case 4:
            wcscpy((LPOLESTR)pszName, L"Get Supported Clipboard Formats");
        case 5:
            wcscpy((LPOLESTR)pszName, L"Get Data Using DsDisplaySpecOptions Clipboard Format");
        case 6:
            wcscpy((LPOLESTR)pszName, L"Get Data Using DsObjectNames Clipboard Format");
    }
    return S_OK;
}
/*
else if (uFlags == GCS_VERB)
{
    //Handle this flag for returning language independent command names
 
    }
*/
return E_FAIL;
}
 
//Command Handlers
 
STDMETHODIMP CMyDsContextMenu::DoMenuCmd1(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
 
    MessageBoxW(hParent, L"Menu item 1!", L"Shell Extension Sample", MB_OK);
 
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::DoMenuCmd2(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
 
    MessageBoxW(hParent, L"Menu item 2!", L"Shell Extension Sample", MB_OK);
 
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::DoMenuCmd3(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
 
    MessageBoxW(hParent, L"Menu item 3!", L"Shell Extension Sample", MB_OK);
 
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::DoMenuCmd4(HWND hParent,
                                   LPCSTR pszWorkingDir,
                                   LPCSTR pszCmd,
                                   LPCSTR pszParam,
                                   int iShowCmd)
{
 
    MessageBoxW(hParent, L"Menu item 4!", L"Shell Extension Sample", MB_OK);
 
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::Initialize(LPCITEMIDLIST pIDFolder,
                                   LPDATAOBJECT pDataObj,
                                   HKEY hRegKey)
{
 
    // Initialize can be called more than once
 
    if (m_pDataObj)
        m_pDataObj->Release();
 
    // Keep a pointer to the IDataObject
 
    if (pDataObj)
    {
        m_pDataObj = pDataObj;
        pDataObj->AddRef();
    }
 
    //Register the clipboard formats to use for getting info about
    //the selected DS object.
    m_cfDsDispSpecOptions = RegisterClipboardFormat(CFSTR_DS_DISPLAY_SPEC_OPTIONS);
    m_cfDsObjectNames = RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
 
    return NOERROR;
}
 
STDMETHODIMP CMyDsContextMenu::GetSupportedClipboardFormats(HWND hParent, IDataObject *pDO)
{
#define FORMATETC_MAX 20
    LPOLESTR szText = new OLECHAR[MAX_PATH*4];
    OLECHAR szCFName[MAX_PATH];
    HRESULT hr = E_FAIL;
    LPENUMFORMATETC lpEnumFmtEtc = NULL;
    FORMATETC rgfmtetc[FORMATETC_MAX];
    int iReturn = 0;
    ULONG j;
    ULONG cFetched = 0;
 
    hr = pDO->EnumFormatEtc(
        DATADIR_GET,
        (LPENUMFORMATETC FAR*)&lpEnumFmtEtc
        );
    if (FAILED(hr))
    {
    //Not implemented in Users and Computers snap-in.
    if (E_NOTIMPL==hr)
        swprintf(szText,L"EnumFormatEtc method is not implemented in the Users and Computers snap-in.");
    else
        swprintf(szText,L"EnumFormatEtc method failed with hr: %x",hr);
 
    MessageBoxW(hParent,szText,L"Clipboard formats",MB_OK);
        return hr;    // unable to get format enumerator
    }
    //Is implemented in the Windows shell.
    //Enumerate the formats offered by the source.
    // Loop over all formats offered by the source.
    memset(rgfmtetc,0,sizeof(rgfmtetc[FORMATETC_MAX]) );
    hr = lpEnumFmtEtc->Next(FORMATETC_MAX,
                    rgfmtetc,
                    &cFetched); 
    if ( SUCCEEDED(hr) || (cFetched > 0 && cFetched <= FORMATETC_MAX) )
    {
    wcscpy(szText,L"Supported clipboard formats:\n");
        for (j = 0; j < cFetched; j++)
        {
            iReturn = GetClipboardFormatNameW(
                rgfmtetc[j].cfFormat, //Clipboard format to retrieve.
                szCFName,             //Address of buffer for name.
                sizeof(szCFName)      //Length of string in characters.
                );
        if (iReturn)
        {
            wcscat(szText,szCFName );
            wcscat(szText,L"\n");
        }
        else
        {
            swprintf(szCFName,L"Unknown clipboard format %d\n",rgfmtetc[j].cfFormat);
            wcscat(szText,szCFName );
        }
    }
    MessageBoxW(hParent,szText,L"Clipboard formats",MB_OK);
}
    else
    {
    swprintf(szText,L"Next method failed with hr: %x",hr);
    MessageBoxW(hParent,szText,L"Clipboard formats",MB_OK);
    }
 
 
 
    //Clean up
    if (lpEnumFmtEtc)
        lpEnumFmtEtc->Release();
    return hr;
}
 
 
STDMETHODIMP CMyDsContextMenu::GetDsDisplaySpecOptions(HWND hParent,
                                        IDataObject *pDO)
{
 
LPOLESTR szText = new OLECHAR[MAX_PATH*4];
HRESULT hr = S_OK;
PDSDISPLAYSPECOPTIONS pDsSpecOptions = NULL;
LPOLESTR szPrefix = NULL;
 
//Need to get CFSTR_DS_DISPLAY_SPEC_OPTIONS as HGLOBAL.
STGMEDIUM stgmedium = {
    TYMED_HGLOBAL,
    NULL,
    NULL
};
 
//Initialize formatetc for CFSTR_DS_DISPLAY_SPEC_OPTIONS format.
FORMATETC formatetc = {
    m_cfDsDispSpecOptions,
    NULL,
    DVASPECT_CONTENT,
    -1,
    TYMED_HGLOBAL
};
 
//Get the global memory block containing the display option info for the selected objects.
hr = pDO->GetData(&formatetc, &stgmedium);
if (FAILED(hr))
{
    //Not implemented.
    if (E_NOTIMPL==hr)
        swprintf(szText,L"GetData method is not implemented.");
    else
        swprintf(szText,L"GetData method failed with hr: %x",hr);
 
    MessageBoxW(hParent,szText,L"GetData using DsDisplaySpecOptions",MB_OK);
    return hr;    //Unable to get data.
}
// Assign pointer to DSDISPLAYSPECOPTIONS structure.
pDsSpecOptions = (PDSDISPLAYSPECOPTIONS) GlobalLock(stgmedium.hGlobal);
if (pDsSpecOptions)
{
    //Use the ByteOffset macro that was defined 
    //to move to the appropriate offset based on offsetAttributePrefix.
    szPrefix = (LPOLESTR)ByteOffset(pDsSpecOptions,
                                   pDsSpecOptions->offsetAttribPrefix);
 
    if (0==wcscmp(DS_PROP_SHELL_PREFIX, szPrefix))
        swprintf(szText,L"Context menu was called from the Windows shell.\nThe offsetAttribPrefix member is %s.", szPrefix);
    else if (0==wcscmp(DS_PROP_ADMIN_PREFIX, szPrefix))
        swprintf(szText,L"Context menu was called from an Active Directory Administrative Snap-in.\nThe offsetAttribPrefix member is %s.", szPrefix);
    else
        swprintf(szText,L"Could not determine prefix. The offsetAttribPrefix member is %s.", szPrefix);
    MessageBoxW(hParent,szText,L"GetData using DsDisplaySpecOptions",MB_OK);
}
 
//Clean up
GlobalUnlock(stgmedium.hGlobal);
ReleaseStgMedium(&stgmedium);
 
return hr;
 
}