2025-11-28 00:35:46 +09:00

399 lines
14 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Module Name:
// MyDeviceContextMenu.cpp
//
// Abstract:
// Implementation of the context menu extension for the My Device Sync
// Center handler collection.
//
//////////////////////////////////////////////////////////////////////////////
#include "Pch.h"
#include "MyDeviceContextMenu.h"
#include "Resources.h"
#include <propidl.h> // For property system interfaces
#include <propkey.h> // For property key definitions
//----------------------------------------------------------------------------
// Static Global Variables
//----------------------------------------------------------------------------
static const char g_aszDisplayInfoVerb[] = "displayinfo";
static const WCHAR g_wszDisplayInfoVerb[] = L"displayinfo";
//////////////////////////////////////////////////////////////////////////////
// class CMyDeviceContextMenu
//////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Description:
// Create an instance of the context menu class.
//
// Parameters:
// punkOuter - Outer IUnknown for aggregation.
// ppunk - IUnknown interface pointer returned to caller.
//
// Return Values:
// S_OK - Operation completed successfully.
// E_OUTOFMEMORY - Error allocating the object.
// Other HRESULTs - Error querying for requested interface.
//
//----------------------------------------------------------------------------
HRESULT CMyDeviceContextMenu_CreateInstance(
__in_opt IUnknown *punkOuter,
__deref_out IUnknown **ppunk)
{
*ppunk = NULL;
UNREFERENCED_PARAMETER(punkOuter);
HRESULT hr = E_OUTOFMEMORY;
CMyDeviceContextMenu *pcm = new CMyDeviceContextMenu();
if (pcm != NULL)
{
hr = pcm->QueryInterface(IID_PPV_ARGS(ppunk));
pcm->Release();
}
return hr;
} //*** CMyDeviceContextMenu_CreateInstance
//----------------------------------------------------------------------------
// IUnknown (CMyDeviceContextMenu)
//----------------------------------------------------------------------------
STDMETHODIMP CMyDeviceContextMenu::QueryInterface(__in REFIID riid, __deref_out void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CMyDeviceContextMenu, IShellExtInit),
QITABENT(CMyDeviceContextMenu, IContextMenu),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
} //*** CMyDeviceContextMenu::QueryInterface
//----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CMyDeviceContextMenu::Release()
{
ULONG cRef = InterlockedDecrement(&_cRef);
if (cRef == 0)
{
delete this;
}
return cRef;
} //*** CMyDeviceContextMenu::Release
//----------------------------------------------------------------------------
// IShellExtInit (CMyDeviceContextMenu)
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//
// Description:
// Initialize the object for the Shell extension operation.
//
// Parameters:
// pidlFolder - Structure that identifies the folder being extended.
// pdtobj - IDataObject providing info about selected items.
// hkeyProgID - Registry key for the folder.
//
// Return Values:
// S_OK - Operation completed successfully.
//
//----------------------------------------------------------------------------
STDMETHODIMP CMyDeviceContextMenu::Initialize(
__in PCIDLIST_ABSOLUTE pidlFolder,
__in IDataObject *pdtobj,
__in HKEY hkeyProgID)
{
UNREFERENCED_PARAMETER(pidlFolder);
UNREFERENCED_PARAMETER(hkeyProgID);
HRESULT hr = S_OK;
// Hold our IDataObject. We can use this to determine
// which items are currently selected in the view.
_pDataObject = pdtobj;
_pDataObject->AddRef();
return hr;
} //*** CMyDeviceContextMenu::Initialize
//----------------------------------------------------------------------------
// IContextMenu (CMyDeviceContextMenu)
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//
// Description:
// Called by the Shell to allow the context menu handler to add items
// to the context menu.
//
// Parameters:
// hMenu - Handler to the menu to extend.
// indexMenu - Zero-based position at which to insert the first menu item.
// idCmdFirst - Minimum value that the handler can specify for a menu item identifier.
// idCmdLast - Maximum value that the handler can specify for a menu item identifier.
// uFlags - Flags specifying how the menu can be changed.
//
// Return Values:
// HRESULT value that has its severity value set to SEVERITY_SUCCESS and
// its code value set to the largest command identifier that was
// assigned, plus one.
// HRESULT value - An error occurred.
//
//----------------------------------------------------------------------------
STDMETHODIMP CMyDeviceContextMenu::QueryContextMenu(
__in HMENU hMenu,
__in UINT indexMenu,
__in UINT idCmdFirst,
__in UINT idCmdLast,
__in UINT uFlags)
{
HRESULT hr = S_OK;
UNREFERENCED_PARAMETER(idCmdLast);
// CMF_DEFAULTONLY is only relevant if you are trying
// to change the default (double-click) action for the
// selected item. When the user double-clicks your item,
// the shell will call QueryContextMenu with this flag
// specified.
if (!(uFlags & CMF_DEFAULTONLY))
{
WCHAR szMenuString[128];
LoadStringW(g_hmodThisDll, IDS_MENU_DISPLAY_INFO, szMenuString, ARRAYSIZE(szMenuString));
if (!InsertMenuW(hMenu, indexMenu, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_DISPLAY_INFO, szMenuString))
{
DWORD sc = GetLastError();
hr = HRESULT_FROM_WIN32(sc);
}
else
{
hr = MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY_INFO + 1));
}
} // if: not just executing default action
return hr;
} //*** CMyDeviceContextMenu::QueryContextMenu
//----------------------------------------------------------------------------
//
// Description:
// Return information about the items this handler adds to the menu.
//
// Parameters:
// idCommand - Menu command identifier offset.
// uFlags - Flags specifying the information to return.
// lpReserved - Reserved.
// pszName - Address of the buffer to receive the string being requested.
// uMaxNameLen - Size of the pszName buffer in characters.
//
// Return Values:
// S_OK - Operation completed successfully.
// E_INVALIDARG - Command ID was unknown.
// Other HRESULTS - Operation failed.
//
//----------------------------------------------------------------------------
STDMETHODIMP CMyDeviceContextMenu::GetCommandString(
__in UINT_PTR idCommand,
__in UINT uFlags,
__reserved LPUINT lpReserved,
__out_awcount(!(uFlags & GCS_UNICODE), uMaxNameLen) LPSTR pszName,
__in UINT uMaxNameLen)
{
HRESULT hr = E_INVALIDARG;
UNREFERENCED_PARAMETER(lpReserved);
if (idCommand != IDM_DISPLAY_INFO)
{
hr = E_INVALIDARG;
}
else
{
switch (uFlags)
{
case GCS_HELPTEXTA:
{
int cchLoaded = LoadStringA(g_hmodThisDll, IDS_MENU_DESC_DISPLAY_INFO, pszName, uMaxNameLen);
DWORD sc = GetLastError();
hr = (cchLoaded == 0) ? HRESULT_FROM_WIN32(sc) : S_OK;
break;
}
case GCS_HELPTEXTW:
{
int cchLoaded = LoadStringW(g_hmodThisDll, IDS_MENU_DESC_DISPLAY_INFO, (LPWSTR) pszName, uMaxNameLen);
DWORD sc = GetLastError();
hr = (cchLoaded == 0) ? HRESULT_FROM_WIN32(sc) : S_OK;
break;
}
case GCS_VERBA:
hr = StringCchCopyNA(pszName, uMaxNameLen, g_aszDisplayInfoVerb, ARRAYSIZE(g_aszDisplayInfoVerb));
break;
case GCS_VERBW:
hr = StringCchCopyNW((LPWSTR) pszName, uMaxNameLen, g_wszDisplayInfoVerb, ARRAYSIZE(g_wszDisplayInfoVerb));
break;
case GCS_VALIDATEA:
case GCS_VALIDATEW:
default:
hr = S_OK;
break;
} // switch: info requested
} // else: command ID was recognized
return hr;
} //*** CMyDeviceContextMenu::GetCommandString
//----------------------------------------------------------------------------
//
// Description:
// Carries out the command associated with a menu item added by this
// context menu handler.
//
// Parameters:
// pici - Structure containing information about the command.
//
// Return Values:
// S_OK - Operation completed successfully.
// Other HRESULTS - Operation failed.
//
//----------------------------------------------------------------------------
STDMETHODIMP CMyDeviceContextMenu::InvokeCommand(__in LPCMINVOKECOMMANDINFO pici)
{
HRESULT hr = S_OK;
BOOL fUnicode = FALSE;
CMINVOKECOMMANDINFOEX *picie = (CMINVOKECOMMANDINFOEX *) pici;
// Determine if unicode was specified.
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
{
if ((pici->fMask & CMIC_MASK_UNICODE) != 0)
{
fUnicode = TRUE;
}
}
// Handle a verb.
if (!fUnicode && (HIWORD(pici->lpVerb) != 0))
{
if (StrCmpIA(pici->lpVerb, g_aszDisplayInfoVerb) != 0)
{
hr = E_FAIL;
}
}
else if (fUnicode && (HIWORD(picie->lpVerbW) != 0))
{
if (StrCmpIW(picie->lpVerbW, g_wszDisplayInfoVerb) != 0)
{
hr = E_FAIL;
}
}
else if (LOWORD(pici->lpVerb) != IDM_DISPLAY_INFO)
{
hr = E_FAIL;
}
// Handle the command. For demonstration purposes,
// we'll just display a message box.
if (SUCCEEDED(hr))
{
// We can retrieve properties for the selected handler/item
// using the PKEYs specified in propkey.h.
// Other keys that may be useful:
// PKEY_Sync_HandlerCollectionID -- The Handler Collection ID
// PKEY_Sync_HandlerID -- The Handler ID
// PKEY_Sync_ItemID -- The Item ID
PROPVARIANT propvar = {0};
hr = _GetSelectedItemProperty(PKEY_ItemNameDisplay, &propvar);
if (SUCCEEDED(hr))
{
WCHAR szMessage[128];
hr = StringCchPrintfW(szMessage, ARRAYSIZE(szMessage), L"Extended Details For %s", propvar.pwszVal);
if (SUCCEEDED(hr))
{
MessageBoxW(pici->hwnd, L"Partnership Info", szMessage, MB_OK | MB_ICONINFORMATION);
}
PropVariantClear(&propvar);
}
}
return hr;
} //*** CMyDeviceContextMenu::InvokeCommand
//----------------------------------------------------------------------------
//
// Description:
// Gets the specified property for the items that the context menu
// is currently being invoked on.
//
// Parameters:
// pkey - Key for the property we are looking for.
// pPropVar - Address of the propvariant that recieves the property.
//
// Return Values:
// S_OK - Operation completed successfully.
// Other HRESULTS - Operation failed.
//
//----------------------------------------------------------------------------
HRESULT CMyDeviceContextMenu::_GetSelectedItemProperty(__in REFPROPERTYKEY pkey, __out PROPVARIANT *pPropVar)
{
// Determine exactly what object the context menu is being
// displayed for.
IShellItemArray *pItemArray = NULL;
HRESULT hr = SHCreateShellItemArrayFromDataObject(_pDataObject, IID_PPV_ARGS(&pItemArray));
if (SUCCEEDED(hr))
{
// Sync Center will only ask for the context menu
// for a single item. We can just get item 0 in this case.
IShellItem *psi = NULL;
hr = pItemArray->GetItemAt(0, &psi);
if (SUCCEEDED(hr))
{
// IShellItem2 allows us to retrieve the property
// store for the item. From here, we can query
// for properties that appear in the view.
IShellItem2 *psi2 = NULL;
hr = psi->QueryInterface(IID_PPV_ARGS(&psi2));
if (SUCCEEDED(hr))
{
IPropertyStore *pps = NULL;
hr = psi2->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr))
{
hr = pps->GetValue(pkey, pPropVar);
pps->Release();
}
psi2->Release();
}
psi->Release();
}
pItemArray->Release();
}
return hr;
} //*** CMyDeviceContextMenu::_GetSelectedItemProperty