|
|
#include "pch.h"
#include <atlbase.h>
#pragma hdrstop
/*----------------------------------------------------------------------------
/ Static data for mapping verbs to intersting information /----------------------------------------------------------------------------*/
//
// Menu item stored in the DSA to map from external IDs to internal
//
typedef struct { INT iMenuItem; // index into menu_items array
} MENUITEM, * LPMENUITEM;
//
// This table maps classes to verbs that should be added to the menu
// we then add menu item data structures as required.
//
#define MENUCMD_INITITEM 0x0001 // called per menu item
#define MENUCMD_INVOKE 0x0002 // called to invoke the command
//
// Handlers
//
typedef struct { DWORD dwFlags; HDPA hdpaSelection; LPWSTR pszUserName; LPWSTR pszPassword; } VERBINFO, * LPVERBINFO; typedef HRESULT (*LPMENUITEMCB)(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
HRESULT _UserVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags); HRESULT _VolumeVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags); HRESULT _ComputerVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags); HRESULT _PrinterVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags);
struct { BOOL fNotValidInWAB:1; // =1 => verb is NOT valid when invoked from WAB
LPWSTR pObjectClass; // class name
UINT uID; // name to add for verb
UINT idsHelp; // help text for this verb
LPMENUITEMCB pItemCB; // menu item callback
} menu_items[] = { 0, L"user", IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB, 1, L"user", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB, 0, L"inetOrgPerson",IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB, 1, L"inetOrgPerson",IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB, 0, L"contact", IDC_USER_OPENHOMEPAGE, IDS_USER_OPENHOMEPAGE, _UserVerbCB, 1, L"contact", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB, 1, L"group", IDC_USER_MAILTO, IDS_USER_MAILTO, _UserVerbCB, 0, L"volume", IDC_VOLUME_OPEN, IDS_VOLUME_OPEN, _VolumeVerbCB, 0, L"volume", IDC_VOLUME_EXPLORE, IDS_VOLUME_EXPLORE, _VolumeVerbCB, 0, L"volume", IDC_VOLUME_FIND, IDS_VOLUME_FIND, _VolumeVerbCB, 0, L"volume", IDC_VOLUME_MAPNETDRIVE, IDS_VOLUME_MAPNETDRIVE, _VolumeVerbCB, 0, L"computer", IDC_COMPUTER_MANAGE, IDS_COMPUTER_MANAGE, _ComputerVerbCB, 0, L"printQueue", IDC_PRINTER_INSTALL, IDS_PRINTER_INSTALL, _PrinterVerbCB, 0, L"printQueue", IDC_PRINTER_OPEN, IDS_PRINTER_OPEN, _PrinterVerbCB, };
//
// Our class for implementing the standard verbs
//
class CDsVerbs : public IShellExtInit, IContextMenu { private: LONG _cRef; IDataObject* _pDataObject; HDSA _hdsaItems; // entry per verb on menu
VERBINFO _vi;
//
// This public data is used by the verb handlers, they are passed a CDsVerbs*
// as one of their parameters, so using this we then allow them to store what
// they need in here.
//
public: CDsVerbs(); ~CDsVerbs();
// IUnknown members
STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP QueryInterface(REFIID, LPVOID FAR*);
// IShellExtInit
STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
// IContextMenu
STDMETHODIMP QueryContextMenu(HMENU hMenu, UINT uIndex, UINT uIDFirst, UINT uIDLast, UINT uFlags); STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pCMI); STDMETHODIMP GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pName, UINT ccMax);
private: VOID FreeMenuStateData(VOID); };
static HRESULT _OpenObject(LPCWSTR pszPath, REFIID riid, void **ppv, LPVERBINFO pvi) { return OpenDsObject(pszPath, pvi->pszUserName, pvi->pszPassword, riid, ppv, (pvi->dwFlags & DSDSOF_SIMPLEAUTHENTICATE), (pvi->dwFlags & DSDSOF_DONTSIGNSEAL)); }
/*----------------------------------------------------------------------------
/ CDsVerbs implementation /----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
/ IUnknown /----------------------------------------------------------------------------*/
CDsVerbs::CDsVerbs() : _cRef(1), _pDataObject(NULL), _hdsaItems(NULL) { _vi.dwFlags = 0; _vi.hdpaSelection = NULL; _vi.pszUserName = NULL; _vi.pszPassword = NULL;
DllAddRef(); }
CDsVerbs::~CDsVerbs() { DoRelease(_pDataObject);
FreeMenuStateData();
SecureLocalFreeStringW(&_vi.pszUserName); SecureLocalFreeStringW(&_vi.pszPassword);
DllRelease(); }
// IUnknown bits
ULONG CDsVerbs::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CDsVerbs::Release() { Assert(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; }
HRESULT CDsVerbs::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDsVerbs, IShellExtInit), // IID_IShellExtInit
QITABENT(CDsVerbs, IContextMenu), // IID_IContextMenu
{0, 0 }, }; return QISearch(this, qit, riid, ppv); }
// handle create instance
STDAPI CDsVerbs_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi) { CDsVerbs *pdv = new CDsVerbs(); if (!pdv) return E_OUTOFMEMORY;
HRESULT hres = pdv->QueryInterface(IID_IUnknown, (void **)ppunk); pdv->Release(); return hres; }
/*----------------------------------------------------------------------------
/ IShellExtInit /----------------------------------------------------------------------------*/
STDMETHODIMP CDsVerbs::Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObject, HKEY hKeyID) { HRESULT hr;
TraceEnter(TRACE_VERBS, "CDsVerbs::Initialize");
// take a copy of the IDataObject if we are given one
if (!pDataObject) ExitGracefully(hr, E_FAIL, "No IDataObject to interact with");
DoRelease(_pDataObject);
_pDataObject = pDataObject; _pDataObject->AddRef();
hr = S_OK; // sucess
exit_gracefully:
TraceLeaveResult(hr); }
/*----------------------------------------------------------------------------
/ IContextMenu /----------------------------------------------------------------------------*/
STDMETHODIMP CDsVerbs::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags) { HRESULT hr; FORMATETC fmte = {(CLIPFORMAT)0, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM mediumDsObjects = { TYMED_NULL }; STGMEDIUM mediumDispSpecOptions = { TYMED_NULL }; LPDSOBJECTNAMES pDsObjectNames = NULL; LPDSDISPLAYSPECOPTIONS pDispSpecOptions = NULL; MENUITEM item; INT i, iVerb; TCHAR szBuffer[MAX_PATH]; BOOL fInWAB = FALSE;
TraceEnter(TRACE_VERBS, "CDsVerbs::QueryContextMenu");
FreeMenuStateData();
// Get the selection from the IDataObject we have been given. This structure
// contains the object class, ADsPath and other information.
if (!_pDataObject) ExitGracefully(hr, E_FAIL, "No IDataObject to use");
fmte.cfFormat = g_cfDsObjectNames; hr = _pDataObject->GetData(&fmte, &mediumDsObjects); FailGracefully(hr, "Failed to get the DSOBJECTNAMES from IDataObject");
pDsObjectNames = (LPDSOBJECTNAMES)GlobalLock(mediumDsObjects.hGlobal); TraceAssert(pDsObjectNames);
fmte.cfFormat = g_cfDsDispSpecOptions; if (SUCCEEDED(_pDataObject->GetData(&fmte, &mediumDispSpecOptions))) { pDispSpecOptions = (LPDSDISPLAYSPECOPTIONS)GlobalLock(mediumDispSpecOptions.hGlobal); TraceAssert(pDispSpecOptions);
TraceMsg("Retrieved the CF_DISPSPECOPTIONS from the IDataObject");
fInWAB = (pDispSpecOptions->dwFlags & DSDSOF_INVOKEDFROMWAB) == DSDSOF_INVOKEDFROMWAB; Trace(TEXT("Invoked from WAB == %d"), fInWAB);
// copy credential and other information for the verbs to invoke with
_vi.dwFlags = pDispSpecOptions->dwFlags;
if (_vi.dwFlags & DSDSOF_HASUSERANDSERVERINFO) { TraceMsg("Copying user and credential information from clipboard block");
if (pDispSpecOptions->offsetUserName) { LPWSTR pszUserName = (LPWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetUserName); Trace(TEXT("pszUserName: %s"), pszUserName);
hr = LocalAllocStringW(&_vi.pszUserName, pszUserName); FailGracefully(hr, "Failed to copy the user name"); }
if (pDispSpecOptions->offsetPassword) { LPWSTR pszPassword = (LPWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetPassword); Trace(TEXT("pszPassword: %s"), pszPassword);
hr = LocalAllocStringW(&_vi.pszPassword, pszPassword); FailGracefully(hr, "Failed to copy the password"); } } }
// Take the first item of the selection, compare all the objects in the
// rest of the DSOBJECTNAMES, all those who have the same class.
_hdsaItems = DSA_Create(SIZEOF(MENUITEM), 4); TraceAssert(_hdsaItems);
_vi.hdpaSelection = DPA_Create(4); TraceAssert(_vi.hdpaSelection);
if (!_vi.hdpaSelection || !_hdsaItems) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate the selection DPA"); for (i = 0 ; i < (INT)pDsObjectNames->cItems ; i++) { LPCWSTR pObjectClass0 = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass); LPWSTR pPath = (LPWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[i].offsetName); LPCWSTR pObjectClass = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[i].offsetClass);
Trace(TEXT("ADsPath of object %d is %s"), i, pPath); Trace(TEXT("objectClass of object %d is %s"), i, pObjectClass);
if (!StrCmpW(pObjectClass0, pObjectClass)) { Trace(TEXT("Adding item %d to the selection DPA"), i);
hr = StringDPA_AppendStringW(_vi.hdpaSelection, pPath, NULL); FailGracefully(hr, "Failed to copy selection to selection DPA"); } }
// Walk the list of menu items, lets see which ones we need to add to the
// menu.
if (DPA_GetPtrCount(_vi.hdpaSelection)) { LPCWSTR pObjectClass0 = (LPCWSTR)ByteOffset(pDsObjectNames, pDsObjectNames->aObjects[0].offsetClass);
for (i = 0 ; i < ARRAYSIZE(menu_items); i++) { if (menu_items[i].fNotValidInWAB && fInWAB) { TraceMsg("Skipping verb not valid for WAB"); continue; }
if (!StrCmpW(pObjectClass0, menu_items[i].pObjectClass)) { Trace(TEXT("Adding the verb at index %d to the menu"), i);
// now fill in the MENUITEM structure and add it to the DSA list,
// then add the menu item itself, calling the callback so it can
// enable/disable itself.
item.iMenuItem = i;
iVerb = DSA_AppendItem(_hdsaItems, &item); TraceAssert(iVerb != -1);
if (iVerb != -1) { Trace(TEXT("iVerb is %d"), iVerb); LoadString(GLOBAL_HINSTANCE, menu_items[i].uID, szBuffer, ARRAYSIZE(szBuffer)); InsertMenu(hMenu, iVerb+indexMenu, MF_BYPOSITION|MF_STRING, iVerb+idCmdFirst, szBuffer);
menu_items[i].pItemCB(MENUCMD_INITITEM, NULL, hMenu, MAKELPARAM(menu_items[i].uID, iVerb+idCmdFirst), &_vi, uFlags); } } } } hr = S_OK;
exit_gracefully:
if (SUCCEEDED(hr)) { Trace(TEXT("%d items added by QueryContextMenu"), DSA_GetItemCount(_hdsaItems)); hr = ResultFromShort(DSA_GetItemCount(_hdsaItems)); }
if (pDsObjectNames) GlobalUnlock(mediumDsObjects.hGlobal); if (pDispSpecOptions) GlobalUnlock(mediumDispSpecOptions.hGlobal); ReleaseStgMedium(&mediumDsObjects); ReleaseStgMedium(&mediumDispSpecOptions);
TraceLeaveResult(hr); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsVerbs::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) { HRESULT hr; UINT uID = LOWORD(lpcmi->lpVerb); LPMENUITEM pMenuItem;
TraceEnter(TRACE_VERBS, "CDsVerbs::InvokeCommand");
// Dreference the menu item to get the index's into both the item list and the
// menu table. With both of these we can then invoke the command.
Trace(TEXT("uID %d (DSA contains %d)"), uID, DSA_GetItemCount(_hdsaItems));
if (!_hdsaItems) ExitGracefully(hr, E_UNEXPECTED, "No _hdasItems");
pMenuItem = (LPMENUITEM)DSA_GetItemPtr(_hdsaItems, (UINT)uID); TraceAssert(pMenuItem);
if (!pMenuItem || !menu_items[pMenuItem->iMenuItem].pItemCB) ExitGracefully(hr, E_UNEXPECTED, "Failed because pItem == NULL");
hr = menu_items[pMenuItem->iMenuItem].pItemCB(MENUCMD_INVOKE, lpcmi->hwnd, NULL, MAKELPARAM(menu_items[pMenuItem->iMenuItem].uID, 0), &_vi, 0); exit_gracefully:
TraceLeaveResult(S_OK); }
/*---------------------------------------------------------------------------*/
STDMETHODIMP CDsVerbs::GetCommandString(UINT_PTR uID, UINT uFlags, UINT FAR* reserved, LPSTR pszName, UINT ccMax) { HRESULT hr = E_NOTIMPL; INT cc;
TraceEnter(TRACE_VERBS, "CDsVerbs::GetCommandString");
if (_hdsaItems) { LPMENUITEM pMenuItem = (LPMENUITEM)DSA_GetItemPtr(_hdsaItems, (INT)uID); TraceAssert(pMenuItem);
if (!pMenuItem) ExitGracefully(hr, E_FAIL, "Failed to get menu item");
if (uFlags == GCS_HELPTEXT) { // Get the menu item and look up the resource for this verb
// and return it into the callers buffer.
if (!LoadString(GLOBAL_HINSTANCE, menu_items[pMenuItem->iMenuItem].idsHelp, (LPTSTR)pszName, ccMax)) ExitGracefully(hr, E_FAIL, "Failed to load string for help text"); } else { ExitGracefully(hr, E_FAIL, "Failed to get command string"); } }
hr = S_OK;
exit_gracefully:
TraceLeaveResult(hr); }
/*-----------------------------------------------------------------------------
/ CDsVerbs::FreeMenuStateData / --------------------------- / Release the verb state data for the CDsVerbs class, this can be called / (and is) during the destructor and during the context menu construction / to ensure a consistent state. / / In: / Out: / HRESULT /----------------------------------------------------------------------------*/
VOID CDsVerbs::FreeMenuStateData(VOID) { TraceEnter(TRACE_VERBS, "CDsVerbs::FreeMenuStateData");
if (_hdsaItems) { DSA_Destroy(_hdsaItems); _hdsaItems = NULL; }
StringDPA_Destroy(&_vi.hdpaSelection); LocalFreeStringW(&_vi.pszUserName); LocalFreeStringW(&_vi.pszPassword);
TraceLeave(); }
/*----------------------------------------------------------------------------
/ User object verbs /----------------------------------------------------------------------------*/
HRESULT _UserVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags) { HRESULT hr; HDPA hdpaMailTo = NULL; LPTSTR pURL = NULL; IADs* pDsObject = NULL; VARIANT variant; SHELLEXECUTEINFO sei = { 0 }; INT i; DECLAREWAITCURSOR = GetCursor();
TraceEnter(TRACE_VERBS, "_UserVerbCB");
VariantInit(&variant);
switch (uCmd) { case MENUCMD_INITITEM: { // if this is a map network drive/find volume verb then lets ensure we only handle
// a single selection.
switch (LOWORD(uID)) { case IDC_USER_OPENHOMEPAGE: { if (DPA_GetPtrCount(pvi->hdpaSelection) != 1) { TraceMsg("Disabling as selection > 1"); EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED); }
break; } }
break; }
case MENUCMD_INVOKE: { // if we have a selection and the user has picked a verb then we
// need to get the UNC"s from the objects we are trying to invoke,
// therefore lets build a DPA containing them.
SetWaitCursor();
for (i = 0 ; i < DPA_GetPtrCount(pvi->hdpaSelection); i++) { LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, i); TraceAssert(pPath);
DoRelease(pDsObject); VariantClear(&variant);
Trace(TEXT("Binding to %s"), pPath);
if (FAILED(_OpenObject(pPath,IID_PPV_ARG(IADs, &pDsObject), pvi))) { TraceMsg("Failed to bind to the object"); continue; }
if (LOWORD(uID) == IDC_USER_OPENHOMEPAGE) { // get the web address of the object and store it, this should
// only happen once.
if (FAILED(pDsObject->Get(CComBSTR(L"wWWHomePage"), &variant))) continue;
if (V_VT(&variant) == VT_BSTR) { PARSEDURLW pu = {0}; pu.cbSize = sizeof(pu); if (SUCCEEDED(ParseURLW(variant.bstrVal, &pu))) { if ((pu.nScheme == URL_SCHEME_HTTP) || (pu.nScheme == URL_SCHEME_HTTPS)) { Trace(TEXT("Storing URL %s"), V_BSTR(&variant));
hr = LocalAllocStringW(&pURL, V_BSTR(&variant)); FailGracefully(hr, "Failed to store the URL"); } else { TraceMsg("URL scheme not HTTP/HTTPS so ignoring"); } } else { TraceMsg("URL wasn't even an URL"); } } } else { // ensure we have a DPA for storing the mail addresses of the
// objects we are invoked on.
if (!hdpaMailTo) { hdpaMailTo = DPA_Create(4); TraceAssert(hdpaMailTo);
if (!hdpaMailTo) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to create the DPA for mail addresses"); }
if (FAILED(pDsObject->Get(CComBSTR(L"mail"), &variant))) continue;
if (V_VT(&variant) == VT_BSTR) { Trace(TEXT("Adding mail address %s to DPA"), V_BSTR(&variant)); StringDPA_AppendString(hdpaMailTo, V_BSTR(&variant), NULL); } } }
// now process the argument list that we have built.
ResetWaitCursor();
sei.cbSize = SIZEOF(sei); sei.hwnd = hWnd; sei.nShow = SW_SHOWNORMAL;
switch (LOWORD(uID)) { case IDC_USER_OPENHOMEPAGE: { // if we have a URL then lets pass it to shell execute,
// otherwise report the failure to the user.
if (!pURL) { FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOHOMEPAGE, MB_OK|MB_ICONERROR); ExitGracefully(hr, E_FAIL, "No URL defined"); } Trace(TEXT("Executing URL %s"), pURL); sei.lpFile = pURL;
if (!ShellExecuteEx(&sei)) ExitGracefully(hr, E_UNEXPECTED, "Failed in ShellExecuteEx");
break; }
case IDC_USER_MAILTO: { // If every single bind operation failed above,
// hdpaMailTo didn't get defined, and we'll fault
// if we try to use it.
if (hdpaMailTo) { // build a command line we can use for the mail to verb.
if (DPA_GetPtrCount(hdpaMailTo) <= 0) { FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOMAILADDR, MB_OK|MB_ICONERROR); ExitGracefully(hr, E_FAIL, "No mail addresses defined"); }
TCHAR szMailTo[1800] = {0}; int cchMailTo = ARRAYSIZE(szMailTo)-8; // -8 for mailto: + terminator
StrCpyN(szMailTo, TEXT("mailto:"), ARRAYSIZE(szMailTo)); for (i = 0 ; (i < DPA_GetPtrCount(hdpaMailTo)) && (cchMailTo > 0); i++) { LPTSTR pszName = (LPTSTR)DPA_GetPtr(hdpaMailTo, i);
cchMailTo -= lstrlen(pszName) +1; // +1 for seperator
if (cchMailTo < 0) { LPTSTR pszFirstName = (LPTSTR)DPA_GetPtr(hdpaMailTo, 0); LPTSTR pszLastName = (LPTSTR)DPA_GetPtr(hdpaMailTo, max(0, i-1)); if (IDNO == FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_EMAILTOOLONG, MB_YESNO|MB_ICONERROR, pszFirstName, pszLastName)) { ExitGracefully(hr, E_UNEXPECTED, "mailto: line too long"); } } else { if (i > 0) StrCatBuff(szMailTo, TEXT(";"), ARRAYSIZE(szMailTo));
StrCatBuff(szMailTo, pszName, ARRAYSIZE(szMailTo)); } } sei.lpFile = szMailTo; if (!ShellExecuteEx(&sei)) ExitGracefully(hr, E_UNEXPECTED, "Failed in ShellExecuteEx"); } else { //FEATURE: We need an error message, here
// FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOMAILADDR, MB_OK|MB_ICONERROR);
ExitGracefully(hr, E_FAIL, "hdpaMailTo never initialized!"); } break; } } } }
hr = S_OK; // success
exit_gracefully:
DoRelease(pDsObject); VariantClear(&variant);
LocalFreeString(&pURL);
StringDPA_Destroy(&hdpaMailTo);
ResetWaitCursor();
TraceLeaveResult(hr); }
/*----------------------------------------------------------------------------
/ Volume object verbs /----------------------------------------------------------------------------*/
HRESULT _VolumeVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags) { HRESULT hr; HDPA hdpaUNC = NULL; IADs* pDsObject = NULL; VARIANT variant; INT i; LPITEMIDLIST pidl; DECLAREWAITCURSOR = GetCursor();
TraceEnter(TRACE_VERBS, "_VolumeVerbCB");
VariantInit(&variant);
switch (uCmd) { case MENUCMD_INITITEM: { // if this is a map network drive/find volume verb then lets ensure we only handle
// a single selection.
switch (LOWORD(uID)) { case IDC_VOLUME_FIND: case IDC_VOLUME_MAPNETDRIVE: { if (DPA_GetPtrCount(pvi->hdpaSelection) != 1) { TraceMsg("Disabling as selection > 1"); EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED); }
// we remove the find verb if we the restrictions apply to remove it.
if (LOWORD(uID) == IDC_VOLUME_FIND) { if (SHRestricted(REST_NOFIND)) { TraceMsg("Restriction says 'no find', so deleting the find verb"); DeleteMenu(hMenu, HIWORD(uID), MF_BYCOMMAND); } }
break; }
case IDC_VOLUME_OPEN: { if (!(uFlags & CMF_EXPLORE)) { TraceMsg("Not exploring, so making open the default verb"); SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND); }
break; }
case IDC_VOLUME_EXPLORE: { if (uFlags & CMF_EXPLORE) { TraceMsg("Exploring so making explore the default verb"); SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND); }
break; } }
break; }
case MENUCMD_INVOKE: { // if we have a selection and the user has picked a verb then we
// need to get the UNC"s from the objects we are trying to invoke,
// therefore lets build a DPA containing them.
SetWaitCursor();
hdpaUNC = DPA_Create(4); TraceAssert(hdpaUNC);
if (!hdpaUNC) ExitGracefully(hr, E_OUTOFMEMORY, "Failed to get UNC DPA");
for (i = 0 ; i < DPA_GetPtrCount(pvi->hdpaSelection); i++) { LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, i); TraceAssert(pPath);
DoRelease(pDsObject); VariantClear(&variant);
Trace(TEXT("Binding to %s"), pPath);
if (FAILED(_OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi))) { TraceMsg("Failed to bind to the object"); continue; }
if (FAILED(pDsObject->Get(CComBSTR(L"uNCName"), &variant))) continue; if (V_VT(&variant) == VT_BSTR) { Trace(TEXT("Adding UNC %s to DPA"), V_BSTR(&variant)); StringDPA_AppendString(hdpaUNC, V_BSTR(&variant), NULL); } }
ResetWaitCursor();
// we now have the selection stored in the DPA, so lets invoke the command
// by walking the list of UNC's and calling the relevant invoke logic.
Trace(TEXT("UNC DPA contains %d entries"), DPA_GetPtrCount(hdpaUNC));
if (!DPA_GetPtrCount(hdpaUNC)) { FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_NOUNC, MB_OK|MB_ICONERROR); ExitGracefully(hr, E_FAIL, "No UNC paths defined"); }
for (i = 0 ; i < DPA_GetPtrCount(hdpaUNC); i++) { LPTSTR pUNC = (LPTSTR)DPA_GetPtr(hdpaUNC, i); TraceAssert(pUNC);
Trace(TEXT("pUNC is %s"), pUNC);
switch (LOWORD(uID)) { // explore and open we pass onto the shell.
case IDC_VOLUME_OPEN: case IDC_VOLUME_EXPLORE: { SHELLEXECUTEINFO sei = { 0 }; // clears the structure
TraceMsg("Trying to open/explore to UNC");
sei.cbSize = SIZEOF(sei); sei.hwnd = hWnd; sei.lpFile = pUNC; sei.nShow = SW_SHOWNORMAL;
if (uID == IDC_VOLUME_EXPLORE) sei.lpVerb = TEXT("explore");
ShellExecuteEx(&sei); break; }
// find we show the find UI by building an ITEMIDLIST for the UNC we
// have and then call the shells find UI.
case IDC_VOLUME_FIND: { TraceMsg("Invoking find on the UNC");
if (SUCCEEDED(SHILCreateFromPath(pUNC, &pidl, NULL))) { SHFindFiles(pidl, NULL); ILFree(pidl); }
break; }
// lets get a net connection from SHStartNetConnection...
case IDC_VOLUME_MAPNETDRIVE: { Trace(TEXT("Invoking Map Network Drive for: %s"), pUNC); SHStartNetConnectionDialog(hWnd, pUNC, RESOURCETYPE_DISK); break; }
default: { TraceAssert(FALSE); ExitGracefully(hr, E_UNEXPECTED, "Failed to invoke, bad uID"); } } } } }
hr = S_OK; // success
exit_gracefully:
DoRelease(pDsObject); VariantClear(&variant);
StringDPA_Destroy(&hdpaUNC);
ResetWaitCursor();
TraceLeaveResult(hr); }
/*----------------------------------------------------------------------------
/ Computer object verbs /----------------------------------------------------------------------------*/
HRESULT _ComputerVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags) { HRESULT hr; IADs * pDsObject = NULL; LPTSTR pArguments = NULL; LPTSTR pComputer = NULL; TCHAR szBuffer[MAX_PATH]; DECLAREWAITCURSOR = GetCursor();
TraceEnter(TRACE_VERBS, "_ComputerVerbCB");
if (LOWORD(uID) != IDC_COMPUTER_MANAGE) ExitGracefully(hr, E_INVALIDARG, "Not computer manange, so bailing");
switch (uCmd) { case MENUCMD_INITITEM: { if (DPA_GetPtrCount(pvi->hdpaSelection) != 1) { TraceMsg("Selection is != 1, so disabling verb"); EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED); }
break; }
case MENUCMD_INVOKE: { LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, 0); // selection always 0
TraceAssert(pPath);
hr = _OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi); if(FAILED(hr)) { PWSTR pszError = NULL;
StringErrorFromHr(hr, &pszError, TRUE); if(pszError) { FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_COMPUTER_MANAGE, MB_OK|MB_ICONERROR, pszError); delete[] pszError; } FailGracefully(hr, "Failed to bind to computer object"); } VARIANT vNetAddr; hr = pDsObject->Get(CComBSTR(L"dNSHostName"), &vNetAddr); if (SUCCEEDED(hr)) { hr = LocalAllocString (&pComputer, vNetAddr.bstrVal); FailGracefully(hr, "Failed to copy computer address somewhere interesting"); } else { if (hr == E_ADS_PROPERTY_NOT_FOUND) { hr = pDsObject->Get(CComBSTR(L"sAMAccountName"), &vNetAddr); if (SUCCEEDED(hr)) { hr = LocalAllocString(&pComputer, vNetAddr.bstrVal); FailGracefully(hr, "Failed to copy SAM account name somewhere interesting"); // To make the computer name useful we must remove the trailing dollar if
// there is one. Therefore scan to the end of the string and nuke the
// last character.
INT i = lstrlen(pComputer); TraceAssert(i > 1); if ((i > 1) && (pComputer[i-1] == TEXT('$'))) { pComputer[i-1] = TEXT('\0'); Trace(TEXT("Fixed computer name: %s"), pComputer); } } else FailGracefully (hr, "Failed to find a usable machine address"); } } hr = FormatMsgResource(&pArguments, g_hInstance, IDS_COMPUTER_MANAGECMD, pComputer); FailGracefully(hr, "Failed to format MMC cmd line"); ExpandEnvironmentStrings(pArguments, szBuffer, ARRAYSIZE(szBuffer)); Trace(TEXT("MMC cmd line: mmc.exe %s"), szBuffer);
ResetWaitCursor();
SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_DOENVSUBST; sei.lpFile = TEXT("%SystemRoot%\\System32\\mmc.exe"); sei.lpParameters = szBuffer; sei.nShow = SW_SHOWNORMAL; ShellExecuteEx(&sei); } }
hr = S_OK; // success
exit_gracefully:
DoRelease(pDsObject);
LocalFreeString (&pComputer);
TraceLeaveResult(hr); }
/*----------------------------------------------------------------------------
/ printQueue object verb implementations /----------------------------------------------------------------------------*/
//
// Windows 2000 (and beyond) use a RunDll32 entry point.
//
#define PRINT_FMT TEXT("printui.dll,PrintUIEntry /n \"%s\" ")
#define PRINT_SWITCH_OPEN TEXT("/o ")
#define PRINT_SWITCH_INSTALL TEXT("/in ")
BOOL _PrinterCheckRestrictions(HWND hwnd, RESTRICTIONS rest) { if (SHRestricted(rest)) { FormatMsgBox(hwnd, GLOBAL_HINSTANCE, IDS_RESTRICTIONSTITLE, IDS_RESTRICTIONS, MB_OK|MB_ICONERROR); return TRUE; } return FALSE; }
HRESULT _PrinterRunDLLCountAtSymbols(LPCTSTR pszPrinterName, UINT *puCount) { HRESULT hr = E_FAIL;
if(pszPrinterName && puCount) { *puCount = 0;
while(*pszPrinterName) { if(TEXT('@') == *pszPrinterName++) { (*puCount) ++; } }
hr = S_OK; }
return hr; }
HRESULT _PrinterRunDLLFormatAtSymbols(LPTSTR pszBuffer, UINT uBufSize, LPCTSTR pszPrinterName) { HRESULT hr = E_FAIL;
if(pszPrinterName && pszBuffer && uBufSize) { // the buffer end - where we will put the zero terminator
LPTSTR pszBufEnd = pszBuffer + uBufSize - 1;
// format the printer name quoting the @ symbols
while(*pszPrinterName) { if(TEXT('@') == *pszPrinterName) { // check the buffer size
if((pszBuffer+1) >= pszBufEnd) break; // not enough space
// we have space in the buffer
*pszBuffer++ = TEXT('\\'); *pszBuffer++ = *pszPrinterName++; } else { // check the buffer size
if(pszBuffer >= pszBufEnd) break; // not enough space
// we have space in the buffer
*pszBuffer++ = *pszPrinterName++; } }
if(0 == *pszPrinterName) { // the buffer is long enough
hr = S_OK; } else { // we hit the insufficent buffer error
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); }
// put the zero terminator
*pszBuffer = 0; }
return hr; }
HRESULT _PrinterVerbCB(UINT uCmd, HWND hWnd, HMENU hMenu, LPARAM uID, LPVERBINFO pvi, UINT uFlags) { HRESULT hr; IADs* pDsObject = NULL; LPTSTR pPrinterUNC = NULL; LPTSTR pBuffer = NULL; LPTSTR pPrinterName = NULL; UINT uAtSymbolsCount, uBufSize; INT i; VARIANT variant; DECLAREWAITCURSOR = GetCursor();
TraceEnter(TRACE_VERBS, "_ComputerVerbCB");
VariantInit(&variant);
switch (uCmd) { case MENUCMD_INITITEM: { // printers want the open verb as their default.
if (LOWORD(uID) == IDC_PRINTER_INSTALL) { TraceMsg("Install should be the default verb for printQueue objects"); SetMenuDefaultItem(hMenu, HIWORD(uID), MF_BYCOMMAND); }
// printer verbs only work on a single selection.
if (DPA_GetPtrCount(pvi->hdpaSelection) != 1) { TraceMsg("Selection is != 1, so disabling verb"); EnableMenuItem(hMenu, HIWORD(uID), MF_BYCOMMAND|MF_GRAYED); }
break; }
case MENUCMD_INVOKE: { LPWSTR pPath = (LPWSTR)DPA_GetPtr(pvi->hdpaSelection, 0); // selection always 0
TraceAssert(pPath);
SetWaitCursor();
hr = _OpenObject(pPath, IID_PPV_ARG(IADs, &pDsObject), pvi); FailGracefully(hr, "Failed to get pDsObject");
// for Windows NT we can grab the UNC name and build a command line
// we invoke the printUI dll using.
hr = pDsObject->Get(CComBSTR(L"uNCName"), &variant); FailGracefully(hr, "Failed to get UNC from the printer object");
if (V_VT(&variant) != VT_BSTR) ExitGracefully(hr, E_FAIL, "UNC is not a BSTR - whats with that?");
hr = LocalAllocStringW(&pPrinterUNC, V_BSTR(&variant)); FailGracefully(hr, "Failed to copy the printerUNC");
Trace(TEXT("printQueue object UNC: %s"), pPrinterUNC);
// if this is the downlevel shell then load the PRINUI code and then
// invoke the handler accordingly.
hr = _PrinterRunDLLCountAtSymbols(pPrinterUNC, &uAtSymbolsCount); FailGracefully(hr, "Failed to count the @ symbols");
uBufSize = lstrlen(pPrinterUNC) + uAtSymbolsCount + 1; hr = LocalAllocStringLen(&pPrinterName, uBufSize); FailGracefully(hr, "Failed to copy the printerName");
hr = _PrinterRunDLLFormatAtSymbols(pPrinterName, uBufSize, pPrinterUNC); FailGracefully(hr, "Failed to format printerName @ symbols ");
// allocate the format buffer.
int cchBuffer = lstrlen(PRINT_FMT) + lstrlen(PRINT_SWITCH_OPEN) + lstrlen(PRINT_SWITCH_INSTALL) + lstrlen(pPrinterName) + 1; hr = LocalAllocStringLen(&pBuffer, cchBuffer); FailGracefully(hr, "Failed to allocate format buffer");
wnsprintf(pBuffer, cchBuffer, PRINT_FMT, pPrinterName); // now format the line...
switch (LOWORD(uID)) { case IDC_PRINTER_OPEN: StrCatBuff(pBuffer, PRINT_SWITCH_OPEN, cchBuffer); break;
case IDC_PRINTER_INSTALL: StrCatBuff(pBuffer, PRINT_SWITCH_INSTALL, cchBuffer); break; }
ResetWaitCursor();
BOOL bRunCommand = TRUE; if(IDC_PRINTER_INSTALL == LOWORD(uID) && _PrinterCheckRestrictions(hWnd, REST_NOPRINTERADD)) bRunCommand = FALSE;
if(bRunCommand) { Trace(TEXT("Invoking: rundll32.exe %s"), pBuffer);
SHELLEXECUTEINFO sei = {0}; sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_DOENVSUBST; sei.lpFile = TEXT("%SystemRoot%\\System32\\rundll32.exe"); sei.lpParameters = pBuffer; sei.nShow = SW_SHOWNORMAL; ShellExecuteEx(&sei); } } }
hr = S_OK; // success
exit_gracefully:
if (FAILED(hr)) { // we need to tell something to the user here.
FormatMsgBox(hWnd, GLOBAL_HINSTANCE, IDS_TITLE, IDS_ERR_DSOPENOBJECT, MB_OK|MB_ICONERROR); }
VariantClear(&variant);
DoRelease(pDsObject);
LocalFreeString(&pPrinterUNC); LocalFreeString(&pPrinterName); LocalFreeString(&pBuffer);
ResetWaitCursor();
TraceLeaveResult(hr); }
|