windows-nt/Source/XPSP1/NT/net/upnp/upnpui/dll/oncommand.cpp
2020-09-26 16:20:57 +08:00

874 lines
26 KiB
C++

//+---------------------------------------------------------------------------
//
// 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 <upsres.h>
#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<CConnectionFolder *>(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;
}