//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997. // // File: O N C O M M A N D . C P P // // Contents: Command handlers for the context menus, etc. // // Notes: // // Author: jeffspr 4 Nov 1997 // //---------------------------------------------------------------------------- #include "pch.h" #pragma hdrstop #include "upsres.h" #include "oncommand.h" #if DBG // Debug menu commands #include "oncommand_dbg.h" // #endif #include "shutil.h" #include #include "tfind.h" //---[ Externs ]-------------------------------------------------------------- extern const WCHAR c_szUPnPUIDll[]; extern const TCHAR c_sztUPnPUIDll[]; //---[ Prototypes ]----------------------------------------------------------- HRESULT HrCreateDevicePropertySheets( HWND hwndOwner, NewDeviceNode * pNDN); //---[ Constants ]------------------------------------------------------------ HRESULT HrCommandHandlerThread( FOLDERONCOMMANDPROC pfnCommandHandler, LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf) { HRESULT hr = S_OK; LPITEMIDLIST * apidlCopy = NULL; ULONG cidlCopy = 0; // If there are pidls to copy, copy them // if (apidl) { hr = HrCloneRgIDL((LPCITEMIDLIST *) apidl, cidl, &apidlCopy, &cidlCopy); } // If either there were no pidls, or the Clone succeeded, then we want to continue // if (SUCCEEDED(hr)) { PFOLDONCOMMANDPARAMS pfocp = new FOLDONCOMMANDPARAMS; if (pfocp) { pfocp->pfnfocp = pfnCommandHandler; pfocp->apidl = apidlCopy; pfocp->cidl = cidlCopy; pfocp->hwndOwner = hwndOwner; pfocp->psf = psf; pfocp->hInstFolder = NULL; // This should be Release'd in the thread called. // psf->AddRef(); // This will always succeed in retail, but will test the flag in debug // if (!FIsDebugFlagSet (dfidDisableShellThreading)) { // Run in a thread using the QueueUserWorkItem // HANDLE hthrd = NULL; HINSTANCE hInstFolder = LoadLibrary(c_sztUPnPUIDll); if (hInstFolder) { pfocp->hInstFolder = hInstFolder; DWORD dwThreadId; hthrd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FolderCommandHandlerThreadProc, (LPVOID)pfocp, 0, &dwThreadId); } if (NULL != hthrd) { CloseHandle(hthrd); } else { pfocp->hInstFolder = NULL; FolderCommandHandlerThreadProc(pfocp); } } else { // Run directly in this same thread // FolderCommandHandlerThreadProc((PVOID) pfocp); } } else { hr = E_OUTOFMEMORY; } } // Don't release the psf here. This should have been taken care of by the called ThreadProc // TraceError("HrCommandHandlerThread", hr); return hr; } DWORD WINAPI FolderCommandHandlerThreadProc(LPVOID lpParam) { HRESULT hr = S_OK; PFOLDONCOMMANDPARAMS pfocp = (PFOLDONCOMMANDPARAMS) lpParam; BOOL fCoInited = FALSE; Assert(pfocp); hr = CoInitializeEx (NULL, COINIT_DISABLE_OLE1DDE | COINIT_APARTMENTTHREADED); if (SUCCEEDED(hr)) { // We don't care if this is S_FALSE or not, since we'll soon // overwrite the hr. If it's already initialized, great... fCoInited = TRUE; // Call the specific handler // hr = pfocp->pfnfocp( pfocp->apidl, pfocp->cidl, pfocp->hwndOwner, pfocp->psf); } // Remove the ref that we have on this object. The thread handler would have addref'd // this before queueing our action // if (pfocp->psf) { ReleaseObj(pfocp->psf); } // Release apidl // if (pfocp->apidl) { FreeRgIDL(pfocp->cidl, pfocp->apidl); } // Remove this object. We're responsible for this now. // HINSTANCE hInstFolder = pfocp->hInstFolder; pfocp->hInstFolder = NULL; delete pfocp; if (fCoInited) { CoUninitialize(); } if (hInstFolder) { FreeLibraryAndExitThread(hInstFolder, hr); } return hr; } //+--------------------------------------------------------------------------- // // Function: HrFolderCommandHandler // // Purpose: Command handler switch -- all commands come through this // point. // // Arguments: // uiCommand [in] The command-id that's been invoked. // apidl [in] PIDL array (item 0 is our item to work on) // cidl [in] Size of the array // lpici [in] Command context info // hwndOwner [in] Owner hwnd // // Returns: // // Author: jeffspr 11 Feb 1998 // // Notes: // HRESULT HrFolderCommandHandler( UINT uiCommand, LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPCMINVOKECOMMANDINFO lpici, LPSHELLFOLDER psf) { HRESULT hr = S_OK; CWaitCursor wc; // Bring up wait cursor now. Remove when we go out of scope. #if 0 // refresh all permission so subsequent calls can use cached value RefreshAllPermission(); #endif switch(uiCommand) { case CMIDM_INVOKE: Assert(apidl); hr = HrCommandHandlerThread(HrOnCommandInvoke, apidl, cidl, hwndOwner, psf); break; case CMIDM_ARRANGE_BY_NAME: ShellFolderView_ReArrange(hwndOwner, ICOL_NAME); break; case CMIDM_ARRANGE_BY_URL: ShellFolderView_ReArrange(hwndOwner, ICOL_URL); break; case CMIDM_CREATE_SHORTCUT: Assert(apidl); hr = HrCommandHandlerThread(HrOnCommandCreateShortcut, apidl, cidl, hwndOwner, psf); break; case CMIDM_DELETE: Assert(apidl); hr = HrCommandHandlerThread(HrOnCommandDelete, apidl, cidl, hwndOwner, psf); break; case CMIDM_PROPERTIES: Assert(apidl); hr = HrCommandHandlerThread(HrOnCommandProperties, apidl, cidl, hwndOwner, psf); break; #if DBG case CMIDM_DEBUG_TRACING: hr = HrOnCommandDebugTracing(apidl, cidl, hwndOwner, psf); break; case CMIDM_DEBUG_REFRESH: hr = HrOnCommandDebugRefresh(apidl, cidl, hwndOwner, psf); break; case CMIDM_DEBUG_TESTASYNCFIND: hr = HrOnCommandDebugTestAsyncFind(apidl, cidl, hwndOwner, psf); break; #endif default: AssertSz(FALSE, "Unknown command in HrFolderCommandHandler"); hr = E_FAIL; } TraceHr(ttidError, FAL, hr, FALSE, "HrFolderCommandHandler"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrOnCommandInvoke // // Purpose: Command handler for CMIDM_INVOKE // // Arguments: // apidl [in] PIDL array (item 0 is our item to work on) // cidl [in] Size of the array // hwndOwner [in] Owner hwnd // psf [in] Our IShellFolder * // // Returns: // // Author: jeffspr 8 Sep 1999 // // Notes: // HRESULT HrOnCommandInvoke( LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf) { HRESULT hr = S_OK; BOOL fResult = FALSE; PUPNPDEVICEFOLDPIDL pudfp = ConvertToUPnPDevicePIDL(apidl[0]); IUPnPDeviceFinder * pdf = NULL; LPTSTR szUrl = NULL; Assert(pudfp); Assert(cidl > 0); hr = CoCreateInstance(CLSID_UPnPDeviceFinder, NULL, CLSCTX_INPROC_SERVER, IID_IUPnPDeviceFinder, (LPVOID *)&pdf); if (SUCCEEDED(hr)) { IUPnPDevice * pdev = NULL; CUPnPDeviceFoldPidl udfp; hr = udfp.HrInit(pudfp); if (SUCCEEDED(hr)) { PCWSTR szUDN = udfp.PszGetUDNPointer(); Assert(szUDN); BSTR bstrUDN = ::SysAllocString(szUDN); if (bstrUDN) { hr = pdf->FindByUDN(bstrUDN, &pdev); if (S_OK == hr) { BSTR bstrUrl; hr = pdev->get_PresentationURL(&bstrUrl); if (SUCCEEDED(hr)) { // Note: PresentationURL might be NULL if (S_OK == hr) { Assert(bstrUrl); szUrl = TszFromWsz(bstrUrl); } SysFreeString(bstrUrl); } ReleaseObj(pdev); } else if (hr == S_FALSE) { PTSTR pszText = NULL; PTSTR pszTitle = NULL; pszText = TszFromWsz(WszLoadIds(IDS_UPNPTRAYUI_DEVICE_OFFLINE_MSG)); pszTitle = TszFromWsz(WszLoadIds(IDS_UPNPTRAYUI_DEVICE_OFFLINE_TITLE)); if( pszText && pszTitle ) { MessageBox(hwndOwner, pszText, pszTitle, MB_OK | MB_ICONWARNING ); } delete pszText; delete pszTitle; TraceTag(ttidError, "Can not bring up control page for device UDN=%S because FindByUDN returns S_FALSE.", szUDN); } ::SysFreeString(bstrUDN); } else { hr = E_OUTOFMEMORY; TraceError("HrOnCommandInvoke: SysAllocString", hr); } } ReleaseObj(pdf); } if (szUrl) { SHELLEXECUTEINFO sei = {0}; // Check these masks if we ever need to use different icons and such // for the instances that we launch. SEE_MASK_ICON is a possibility. // SEE_MASK_FLAG_NO_UI can be used if we don't want to show an error // on failure. // sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.fMask = SEE_MASK_FLAG_DDEWAIT; sei.hwnd = hwndOwner; sei.lpFile = szUrl; sei.nShow = SW_SHOW; fResult = ShellExecuteEx(&sei); if (!fResult) { hr = HrFromLastWin32Error(); } delete [] szUrl; } TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrOnCommandInvoke"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrOnCommandProperties // // Purpose: Command handler for the CMIDM_PROPERTIES command // // Arguments: // apidl [in] PIDL array (item 0 is our item to work on) // cidl [in] Size of the array // hwndOwner [in] Owner hwnd // // Returns: // // Author: jeffspr 4 Nov 1997 // // Notes: // HRESULT HrOnCommandProperties( LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf) { HRESULT hr = S_OK; PUPNPDEVICEFOLDPIDL pudfp = ConvertToUPnPDevicePIDL(apidl[0]); IUPnPDeviceFinder * pdf = NULL; Assert(pudfp); Assert(cidl > 0); // instantiate device finder hr = CoCreateInstance( CLSID_UPnPDeviceFinder, NULL, CLSCTX_INPROC_SERVER, IID_IUPnPDeviceFinder, (LPVOID *)&pdf ); if (SUCCEEDED(hr)) { CUPnPDeviceFoldPidl udfp; hr = udfp.HrInit(pudfp); if (SUCCEEDED(hr)) { // retrieve the device object associated with UDN IUPnPDevice * pdev = NULL; PCWSTR szUDN = udfp.PszGetUDNPointer(); Assert(szUDN); BSTR bstrUDN = ::SysAllocString(szUDN); if (bstrUDN) { hr = pdf->FindByUDN(bstrUDN, &pdev); if (hr == S_OK) { NewDeviceNode * pNDN = NULL; // convert device object to device node hr = HrCreateDeviceNodeFromDevice(pdev, &pNDN); if (SUCCEEDED(hr)) { // display property pages for given device node hr = HrCreateDevicePropertySheets(hwndOwner, pNDN); // nuke device node delete pNDN; } ReleaseObj(pdev); } else if (hr == S_FALSE) { PTSTR pszText = NULL; PTSTR pszTitle = NULL; pszText = TszFromWsz(WszLoadIds(IDS_UPNPTRAYUI_DEVICE_OFFLINE_MSG)); pszTitle = TszFromWsz(WszLoadIds(IDS_UPNPTRAYUI_DEVICE_OFFLINE_TITLE)); if( pszText && pszTitle ) { MessageBox(hwndOwner, pszText, pszTitle, MB_OK | MB_ICONWARNING); } delete pszText; delete pszTitle; TraceTag(ttidError, "Can not bring up property for device UDN=%S because" " FindByUDN returns S_FALSE.", szUDN); } ::SysFreeString(bstrUDN); } else { hr = E_OUTOFMEMORY; TraceError("HrOnCommandInvoke: SysAllocString", hr); } } ReleaseObj(pdf); } TraceHr(ttidError, FAL, hr, FALSE, "HrOnCommandProperties"); return hr; } //+--------------------------------------------------------------------------- // // Function: HrOnCommandDelete // // Purpose: Command handler for the CMIDM_DELETE command // // Arguments: // apidl [in] PIDL array (item 0 is our item to work on) // cidl [in] Size of the array // hwndOwner [in] Owner hwnd // psf [in] Our folder // // Returns: // // Author: jeffspr 3 Dec 1997 // // Notes: // HRESULT HrOnCommandDelete( LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf) { HRESULT hr = S_OK; DWORD dwLoop = 0; INT iMBResult = 0; #if 0 // Bring up the prompt for the delete // if (cidl > 1) { WCHAR wszItemCount[8]; // Convert the item count to a string // _itow( cidl, wszItemCount, 10 ); // Bring up the message box // iMBResult = NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_DELETE_CONFIRM_MULTI_CAPTION, IDS_CONFOLD_DELETE_CONFIRM_MULTI, MB_YESNO | MB_ICONQUESTION, wszItemCount); } else if (cidl == 1) { PCCONFOLDENTRY pccfe = NULL; // Convert the pidl to a confoldentry, and use the name // to bring up the confirm message box // hr = HrPidlToCConFoldEntry(apidl[0], &pccfe); if (SUCCEEDED(hr)) { // Don't let them try to delete a wizard // if (pccfe->m_fWizard) { NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_ERROR_DELETE_CAPTION, IDS_CONFOLD_ERROR_DELETE_WIZARD, MB_ICONEXCLAMATION); delete pccfe; goto Exit; } else { // Check to see if this connection is in the process of activating. // If so, then we won't allow the delete. // hr = HrCheckForActivation(NULL, pccfe, &fActivating); if (S_OK == hr) { if (!fActivating) { if ((pccfe->m_ncs == NCS_CONNECTING) || (pccfe->m_ncs == NCS_CONNECTED) || (pccfe->m_ncs == NCS_DISCONNECTING)) { // You can't delete an active connection // NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_ERROR_DELETE_CAPTION, IDS_CONFOLD_ERROR_DELETE_ACTIVE, MB_ICONEXCLAMATION); delete pccfe; goto Exit; } else { // Ask for delete confirmation // iMBResult = NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_DELETE_CONFIRM_SINGLE_CAPTION, IDS_CONFOLD_DELETE_CONFIRM_SINGLE, MB_YESNO | MB_ICONQUESTION, pccfe->m_pszName); } } else { // Bring up the MB about "Hey, you can't delete while // the connection is activating." // iMBResult = NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_ERROR_DELETE_CAPTION, IDS_CONFOLD_ERROR_DELETE_ACTIVE, MB_ICONEXCLAMATION); delete pccfe; goto Exit; } } else { // If the connection wasn't found, then we should just drop out of here // because we sure can't delete it. // if (S_FALSE == hr) { delete pccfe; goto Exit; } } delete pccfe; pccfe = NULL; } } else { AssertSz(FALSE, "Couldn't get ConFoldEntry from pidl in HrOnCommandDelete"); goto Exit; } } else { // No connections were specified. Take a hike. // goto Exit; } // If the user said "Yes" to the prompt // if (iMBResult == IDYES) { CConnectionFolder * pcf = static_cast(psf); LPITEMIDLIST pidlFolder = pcf ? pcf->PidlGetFolderRoot() : NULL; BOOL fShowActivationWarning = FALSE; BOOL fShowNotDeletableWarning = FALSE; Assert(pidlFolder); for (dwLoop = 0; dwLoop < cidl; dwLoop++) { PCCONFOLDENTRY pccfe = NULL; hr = HrPidlToCConFoldEntry(apidl[dwLoop], &pccfe); if (SUCCEEDED(hr)) { // If this is a LAN connection the user doesn't have rights // if ((NCM_LAN == pccfe->m_ncm) || (pccfe->m_fWizard)) { fShowNotDeletableWarning = TRUE; continue; } // If this is a RAS connection and the user doesn't have rights // then skip // if (NCM_LAN != pccfe->m_ncm) { if ((!FHasPermission(NCPERM_DeleteConnection)) || ((pccfe->m_dwCharacteristics & NCCF_ALL_USERS) && !FHasPermission(NCPERM_DeleteAllUserConnection))) { fShowNotDeletableWarning = TRUE; continue; } } hr = HrCheckForActivation(NULL, pccfe, &fActivating); if (S_OK == hr) { // Only allow deletion if this connection is inactive and // it allows removal. // if (fActivating || (pccfe->m_ncs == NCS_CONNECTING) || (pccfe->m_ncs == NCS_CONNECTED) || (pccfe->m_ncs == NCS_DISCONNECTING)) { fShowActivationWarning = TRUE; } else if (pccfe->m_dwCharacteristics & NCCF_ALLOW_REMOVAL) { hr = HrNetConFromPidl(apidl[dwLoop], &pNetCon); if (SUCCEEDED(hr)) { hr = pNetCon->Delete(); if (SUCCEEDED(hr) && pcf) { hr = HrDeleteFromCclAndNotifyShell(pidlFolder, apidl[dwLoop], pccfe); } ReleaseObj(pNetCon); } } else { // The selected item is not deletable // fShowNotDeletableWarning = TRUE; } } } } if (fShowNotDeletableWarning) { // You can't delete an item that doesn't support it // NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_ERROR_DELETE_CAPTION, (1 == cidl) ? IDS_CONFOLD_ERROR_DELETE_NOSUPPORT : IDS_CONFOLD_ERROR_DELETE_NOSUPPORT_MULTI, MB_ICONEXCLAMATION); } else if (fShowActivationWarning) { // You can't delete an active connection. Note, if more // than one are being deleted, then we put up the warning // that says 'one or more are being ignored'. // NcMsgBox( _Module.GetResourceInstance(), NULL, IDS_CONFOLD_ERROR_DELETE_CAPTION, (1 == cidl) ? IDS_CONFOLD_ERROR_DELETE_ACTIVE : IDS_CONFOLD_ERROR_DELETE_ACTIVE_MULTI, MB_ICONEXCLAMATION); } } Exit: #endif TraceError("HrOnCommandDelete", hr); return hr; } HRESULT HrCreateShortcutWithPath( LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf, PCWSTR pszDir) { HRESULT hr = S_OK; LPDATAOBJECT pdtobj = NULL; LPITEMIDLIST * apidlInternal = NULL; ULONG cidlInternal = 0; DWORD dwLoop = 0; if (cidl > 0) { apidlInternal = new LPITEMIDLIST[cidl]; if (apidlInternal) { for (;dwLoop < cidl; dwLoop++) { apidlInternal[cidlInternal++] = apidl[dwLoop]; } hr = psf->GetUIObjectOf( hwndOwner, cidlInternal, (LPCITEMIDLIST *) apidlInternal, IID_IDataObject, NULL, (LPVOID *) &pdtobj); if (SUCCEEDED(hr)) { SHCreateLinks(hwndOwner, pszDir, pdtobj, SHCL_USEDESKTOP | SHCL_USETEMPLATE | SHCL_CONFIRM, NULL); ReleaseObj(pdtobj); } delete apidlInternal; } } TraceError("HrCreateShortcutWithPath", hr); return hr; } //+--------------------------------------------------------------------------- // // Function: HrOnCommandCreateShortcut // // Purpose: Command handler for the CMIDM_CREATE_SHORTCUT command. // // Arguments: // apidl [in] PIDL array (item 0 is our item to work on) // cidl [in] Size of the array // hwndOwner [in] Owner hwnd // // Returns: // // Author: jeffspr 13 Mar 1998 // // Notes: // HRESULT HrOnCommandCreateShortcut( LPITEMIDLIST * apidl, ULONG cidl, HWND hwndOwner, LPSHELLFOLDER psf) { HRESULT hr = S_OK; hr = HrCreateShortcutWithPath( apidl, cidl, hwndOwner, psf, NULL); TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrOnCommandCreateShortcut"); return hr; }