Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1752 lines
46 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: I S H E L L F . C P P
//
// Contents: IShellFolder implementation for CUPnPDeviceFolder
// Implemention of CUPnPDeviceFoldPidl
//
// Notes: The IShellFolder interface is used to manage folders within
// the namespace. Objects that support IShellFolder are
// usually created by other shell folder objects, with the root
// object (the Desktop shell folder) being returned from the
// SHGetDesktopFolder function.
//
// Author: jeffspr 22 Sep 1997
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include <upscmn.h>
#include "tfind.h"
#include "clist.h"
#include "clistndn.h"
#include "tconst.h"
CUPnPDeviceFoldPidl::CUPnPDeviceFoldPidl()
{
// if these change, the UPNPUI_PIDL_HEADER structure
// above needs to change to be the right size
// Why we used 4 different types, I don't know...
Assert(2 == sizeof(WORD));
Assert(2 == sizeof(USHORT));
Assert(4 == sizeof(DWORD));
Assert(4 == sizeof(ULONG));
m_pszName = NULL;
m_pszUrl = NULL;
m_pszUdn = NULL;
m_pszType = NULL;
m_pszDesc = NULL;
}
CUPnPDeviceFoldPidl::~CUPnPDeviceFoldPidl()
{
if (m_pszName)
{
delete [] m_pszName;
}
if (m_pszUrl)
{
delete [] m_pszUrl;
}
if (m_pszUdn)
{
delete [] m_pszUdn;
}
if (m_pszType)
{
delete [] m_pszType;
}
if (m_pszDesc)
{
delete [] m_pszDesc;
}
}
HRESULT
CUPnPDeviceFoldPidl::HrInit(FolderDeviceNode * pDeviceNode)
{
Assert(!m_pszName);
Assert(!m_pszUrl);
Assert(!m_pszUdn);
Assert(!m_pszType);
Assert(!m_pszDesc);
Assert(pDeviceNode);
Assert(pDeviceNode->pszDisplayName);
Assert(pDeviceNode->pszPresentationURL);
Assert(pDeviceNode->pszUDN);
Assert(pDeviceNode->pszType);
HRESULT hr;
LPWSTR pszName;
LPWSTR pszUrl;
LPWSTR pszUdn;
LPWSTR pszType;
LPWSTR pszDesc;
ULONG cchName;
ULONG cchUrl;
ULONG cchUdn;
ULONG cchType;
ULONG cchDesc;
hr = E_OUTOFMEMORY;
pszName = NULL;
pszUrl = NULL;
pszUdn = NULL;
pszType = NULL;
pszDesc = NULL;
// Get the size of the name, and tack on a trailing NULL (since we now
// have something else in the buffer behind it.
//
cchName = lstrlenW(pDeviceNode->pszDisplayName);
pszName = new WCHAR [cchName + 1];
if (!pszName)
{
goto Cleanup;
}
cchUrl = lstrlenW(pDeviceNode->pszPresentationURL);
pszUrl = new WCHAR [cchUrl + 1];
if (!pszUrl)
{
goto Cleanup;
}
cchUdn = lstrlenW(pDeviceNode->pszUDN);
pszUdn = new WCHAR [cchUdn + 1];
if (!pszUdn)
{
goto Cleanup;
}
cchType = lstrlenW(pDeviceNode->pszType);
pszType = new WCHAR [cchType + 1];
if (!pszType)
{
goto Cleanup;
}
cchDesc = lstrlenW(pDeviceNode->pszDescription);
pszDesc = new WCHAR [cchDesc + 1];
if (!pszDesc)
{
goto Cleanup;
}
// everything that can fail has succeeded.
hr = S_OK;
// We don't need to check these since we know there's
// enough room.
wcscpy(pszName, pDeviceNode->pszDisplayName);
wcscpy(pszUrl, pDeviceNode->pszPresentationURL);
wcscpy(pszUdn, pDeviceNode->pszUDN);
wcscpy(pszType, pDeviceNode->pszType);
wcscpy(pszDesc, pDeviceNode->pszDescription);
m_pszName = pszName;
m_pszUrl = pszUrl;
m_pszUdn = pszUdn;
m_pszType = pszType;
m_pszDesc = pszDesc;
Cleanup:
if (FAILED(hr))
{
if (pszName)
{
delete [] pszName;
}
if (pszUrl)
{
delete [] pszUrl;
}
if (pszUdn)
{
delete [] pszUdn;
}
if (pszType)
{
delete [] pszType;
}
if (pszDesc)
{
delete [] pszDesc;
}
}
Assert(FImplies(SUCCEEDED(hr), pszName));
Assert(FImplies(SUCCEEDED(hr), pszUrl));
Assert(FImplies(SUCCEEDED(hr), pszUdn));
Assert(FImplies(SUCCEEDED(hr), pszType));
Assert(FImplies(SUCCEEDED(hr), pszDesc));
return hr;
}
HRESULT
HrCopyUnalignedBytesToNewString(BYTE * pByteData,
ULONG cbData,
LPWSTR * ppszResult)
{
Assert(ppszResult);
HRESULT hr;
LPWSTR pszResult;
ULONG cchMax;
hr = S_OK;
pszResult = NULL;
{
BOOL fInvalid;
fInvalid = IsBadReadPtr(pByteData, cbData);
if (fInvalid)
{
hr = E_POINTER;
goto Cleanup;
}
}
if (!cbData || cbData % sizeof(WCHAR))
{
hr = E_INVALIDARG;
goto Cleanup;
}
cchMax = (cbData / sizeof(WCHAR)) - 1;
pszResult = new WCHAR [ cchMax + 1 ];
if (!pszResult)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
::CopyMemory(pszResult, pByteData, cbData);
// make sure that the data is null-terminated.
pszResult[cchMax] = UNICODE_NULL;
Cleanup:
if (FAILED(hr))
{
if (pszResult)
{
delete [] pszResult;
pszResult = NULL;
}
}
*ppszResult = pszResult;
TraceError("HrCopyUnalignedBytesToNewString", hr);
return hr;
}
HRESULT
CUPnPDeviceFoldPidl::HrInit(PUPNPDEVICEFOLDPIDL pidl)
{
Assert(!m_pszName);
Assert(!m_pszUrl);
Assert(!m_pszUdn);
Assert(!m_pszType);
Assert(!m_pszDesc);
HRESULT hr;
UNALIGNED UPNPUI_PIDL_HEADER * puph;
LPWSTR pszName;
LPWSTR pszUrl;
LPWSTR pszUdn;
LPWSTR pszType;
LPWSTR pszDesc;
hr = S_OK;
puph = (UPNPUI_PIDL_HEADER *) pidl;
pszName = NULL;
pszUrl = NULL;
pszUdn = NULL;
pszType = NULL;
pszDesc = NULL;
{
BOOL fInvalid;
fInvalid = IsBadReadPtr(pidl, sizeof(UPNPUI_PIDL_HEADER));
if (fInvalid)
{
hr = E_POINTER;
goto Cleanup;
}
}
// minimal version checking should have happened already,
// so we just assert that everything is ok here
//
Assert(UPNPDEVICEFOLDPIDL_LEADID == puph->uLeadId);
Assert(UPNPDEVICEFOLDPIDL_TRAILID == puph->uTrailId);
{
BYTE * pbString;
ULONG ulOffset;
ULONG cb;
pbString = (BYTE *)pidl;
ulOffset = puph->ulNameOffset;
ulOffset += sizeof(UPNPUI_PIDL_HEADER);
cb = puph->cbName;
pbString += ulOffset;
hr = HrCopyUnalignedBytesToNewString(pbString,
cb,
&pszName);
if (FAILED(hr))
{
goto Cleanup;
}
}
{
BYTE * pbString;
ULONG ulOffset;
ULONG cb;
pbString = (BYTE *)pidl;
ulOffset = puph->ulUrlOffset;
ulOffset += sizeof(UPNPUI_PIDL_HEADER);
cb = puph->cbUrl;
pbString += ulOffset;
hr = HrCopyUnalignedBytesToNewString(pbString,
cb,
&pszUrl);
if (FAILED(hr))
{
goto Cleanup;
}
}
{
BYTE * pbString;
ULONG ulOffset;
ULONG cb;
pbString = (BYTE *)pidl;
ulOffset = puph->ulUdnOffset;
ulOffset += sizeof(UPNPUI_PIDL_HEADER);
cb = puph->cbUdn;
pbString += ulOffset;
hr = HrCopyUnalignedBytesToNewString(pbString,
cb,
&pszUdn);
if (FAILED(hr))
{
goto Cleanup;
}
}
{
BYTE * pbString;
ULONG ulOffset;
ULONG cb;
pbString = (BYTE *)pidl;
ulOffset = puph->ulTypeOffset;
ulOffset += sizeof(UPNPUI_PIDL_HEADER);
cb = puph->cbType;
pbString += ulOffset;
hr = HrCopyUnalignedBytesToNewString(pbString,
cb,
&pszType);
if (FAILED(hr))
{
goto Cleanup;
}
}
{
BYTE * pbString;
ULONG ulOffset;
ULONG cb;
pbString = (BYTE *)pidl;
ulOffset = puph->ulDescOffset;
ulOffset += sizeof(UPNPUI_PIDL_HEADER);
cb = puph->cbDesc;
pbString += ulOffset;
hr = HrCopyUnalignedBytesToNewString(pbString,
cb,
&pszDesc);
if (FAILED(hr))
{
goto Cleanup;
}
}
m_pszName = pszName;
m_pszUrl = pszUrl;
m_pszUdn = pszUdn;
m_pszType = pszType;
m_pszDesc = pszDesc;
Cleanup:
if (FAILED(hr))
{
if (pszName)
{
delete [] pszName;
}
if (pszUrl)
{
delete [] pszUrl;
}
if (pszUdn)
{
delete [] pszUdn;
}
if (pszType)
{
delete [] pszType;
}
if (pszDesc)
{
delete [] pszDesc;
}
}
Assert(FImplies(SUCCEEDED(hr), pszName));
Assert(FImplies(SUCCEEDED(hr), pszUrl));
Assert(FImplies(SUCCEEDED(hr), pszUdn));
Assert(FImplies(SUCCEEDED(hr), pszType));
Assert(FImplies(SUCCEEDED(hr), pszDesc));
TraceError("CUPnPDeviceFoldPidl::HrInit", hr);
return hr;
}
HRESULT
CUPnPDeviceFoldPidl::HrPersist(IMalloc * pMalloc, LPITEMIDLIST * ppidl)
{
Assert(m_pszName);
Assert(m_pszUrl);
Assert(m_pszUdn);
Assert(m_pszType);
Assert(m_pszDesc);
Assert(pMalloc);
Assert(ppidl);
HRESULT hr;
ULONG cbTotalPidlSize;
ULONG cbName;
ULONG cbUrl;
ULONG cbUdn;
ULONG cbType;
ULONG cbDesc;
ULONG ulNameOffset;
ULONG ulUrlOffset;
ULONG ulUdnOffset;
ULONG ulTypeOffset;
ULONG ulDescOffset;
UNALIGNED UPNPUI_PIDL_HEADER * puph;
LPBYTE pbData;
hr = S_OK;
pbData = NULL;
cbName = wcslen(m_pszName);
cbName = (cbName + 1) * sizeof(WCHAR);
cbUrl = wcslen(m_pszUrl);
cbUrl = (cbUrl + 1) * sizeof(WCHAR);
cbUdn = wcslen(m_pszUdn);
cbUdn = (cbUdn + 1) * sizeof(WCHAR);
cbType = wcslen(m_pszType);
cbType = (cbType + 1) * sizeof(WCHAR);
cbDesc = wcslen(m_pszDesc);
cbDesc = (cbDesc + 1) * sizeof(WCHAR);
ulNameOffset = 0;
ulUrlOffset = ulNameOffset + cbName;
ulUdnOffset = ulUrlOffset + cbUrl;
ulTypeOffset = ulUdnOffset + cbUdn;
ulDescOffset = ulTypeOffset + cbType;
cbTotalPidlSize = sizeof(UPNPUI_PIDL_HEADER);
cbTotalPidlSize += cbName;
cbTotalPidlSize += cbUrl;
cbTotalPidlSize += cbUdn;
cbTotalPidlSize += cbType;
cbTotalPidlSize += cbDesc;
// don't count the PIDL-terminating bytes in the size
//
pbData = (BYTE *) pMalloc->Alloc(cbTotalPidlSize +
FIELD_OFFSET(ITEMIDLIST, mkid.cb) + sizeof(USHORT));
if (!pbData)
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPDeviceFoldPidl::HrPersist: Alloc()", hr);
goto Cleanup;
}
// delegate folder alert: since we're a delete folder, the Alloc() above
// doesn't just allocate the bytes we asked for, but rather a bunch for our
// delegate folder prefix, then the bytes we asked for. We need to skip
// the prefix bytes and just write to our own.
puph = (UPNPUI_PIDL_HEADER *)ConvertToUPnPDevicePIDL((ITEMIDLIST*)pbData);
puph->iCB = cbTotalPidlSize;
puph->uLeadId = UPNPDEVICEFOLDPIDL_LEADID;
puph->dwVersion = UP_DEVICE_FOLDER_IDL_VERSION;
puph->uTrailId = UPNPDEVICEFOLDPIDL_TRAILID;
puph->uVOID = 0;
puph->dwCharacteristics = 0;
puph->ulNameOffset = ulNameOffset;
puph->cbName = cbName;
puph->ulUrlOffset = ulUrlOffset;
puph->cbUrl = cbUrl;
puph->ulUdnOffset = ulUdnOffset;
puph->cbUdn = cbUdn;
puph->ulTypeOffset = ulTypeOffset;
puph->cbType = cbType;
puph->ulDescOffset = ulDescOffset;
puph->cbDesc = cbDesc;
{
LPBYTE pbDynamicField;
LPBYTE pbName;
LPBYTE pbUrl;
LPBYTE pbUdn;
LPBYTE pbType;
LPBYTE pbDesc;
// note: this has to be puph (not pbData) because we still
// have to skip the "delegate folder prefix" junk.
pbDynamicField = ((BYTE *)puph) + sizeof(UPNPUI_PIDL_HEADER);
pbName = pbDynamicField + ulNameOffset;
pbUrl = pbDynamicField + ulUrlOffset;
pbUdn = pbDynamicField + ulUdnOffset;
pbType = pbDynamicField + ulTypeOffset;
pbDesc = pbDynamicField + ulDescOffset;
::CopyMemory(pbName, m_pszName, cbName);
::CopyMemory(pbUrl, m_pszUrl, cbUrl);
::CopyMemory(pbUdn, m_pszUdn, cbUdn);
::CopyMemory(pbType, m_pszType, cbType);
::CopyMemory(pbDesc, m_pszDesc, cbDesc);
}
{
// terminate the PIDL
LPITEMIDLIST pidlNext;
pidlNext = ILNext((LPITEMIDLIST)puph);
Assert((FIELD_OFFSET(ITEMIDLIST, mkid.cb) +
sizeof(pidlNext->mkid.cb)) == sizeof(USHORT));
pidlNext->mkid.cb = 0;
}
Cleanup:
Assert(FImplies(FAILED(hr), !pbData));
Assert(FImplies(SUCCEEDED(hr), pbData));
*ppidl = (LPITEMIDLIST)pbData;
TraceError("CUPnPDeviceFoldPidl::HrPersist", hr);
return hr;
}
PCWSTR
CUPnPDeviceFoldPidl::PszGetNamePointer() const
{
return m_pszName;
}
PCWSTR
CUPnPDeviceFoldPidl::PszGetURLPointer() const
{
return m_pszUrl;
}
PCWSTR
CUPnPDeviceFoldPidl::PszGetUDNPointer() const
{
return m_pszUdn;
}
PCWSTR
CUPnPDeviceFoldPidl::PszGetTypePointer() const
{
return m_pszType;
}
PCWSTR
CUPnPDeviceFoldPidl::PszGetDescriptionPointer() const
{
return m_pszDesc;
}
HRESULT CUPnPDeviceFoldPidl::HrSetName(PCWSTR szName)
{
HRESULT hr = S_OK;
if (szName)
{
// Free old name
delete [] m_pszName;
// Copy in new name
m_pszName = WszDupWsz(szName);
if (!m_pszName)
{
hr = E_OUTOFMEMORY;
}
}
TraceError("CUPnPDeviceFoldPidl::HrSetName", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::HrMakeUPnPDevicePidl
//
// Purpose: Private function of the folder object that constructs the
// UPNP device pidl using the delegated allocator
//
// Arguments:
// FolderDeviceNode [in] Structure that contains all the
// strings we need for the pidl
// ppidl [out] The result pidl
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise
//
// Author: tongl 15 Feb 2000
//
// Notes:
//
HRESULT CUPnPDeviceFolder::HrMakeUPnPDevicePidl(FolderDeviceNode * pDeviceNode,
LPITEMIDLIST * ppidl)
{
HRESULT hr;
CUPnPDeviceFoldPidl udfp;
hr = udfp.HrInit(pDeviceNode);
if (SUCCEEDED(hr))
{
hr = udfp.HrPersist(m_pDelegateMalloc, ppidl);
}
return hr;
}
HRESULT CUPnPDeviceFolder::HrMakeUPnPDevicePidl(IUPnPDevice * pDevice,
LPITEMIDLIST * ppidl)
{
HRESULT hr = S_OK;
Assert(pDevice);
BSTR bstrUDN = NULL;
BSTR bstrDisplayName = NULL;
BSTR bstrType = NULL;
BSTR bstrPresentationURL = NULL;
BSTR bstrDescription = NULL;
Assert(pDevice);
pDevice->AddRef();
hr = pDevice->get_UniqueDeviceName(&bstrUDN);
if (SUCCEEDED(hr))
{
hr = pDevice->get_FriendlyName(&bstrDisplayName);
if (SUCCEEDED(hr))
{
hr = pDevice->get_Type(&bstrType);
if (SUCCEEDED(hr))
{
hr = pDevice->get_PresentationURL(&bstrPresentationURL);
if (SUCCEEDED(hr))
{
hr = pDevice->get_Description(&bstrDescription);
if (SUCCEEDED(hr))
{
FolderDeviceNode * pDevNode = new FolderDeviceNode;
if (pDevNode)
{
// the buffers in FolderDeviceNode are MAX_PATH
// wide, so we can only copy MAX_PATH - 1 chars
// and still have room for the terminating null
//
CONST SIZE_T cchMax = MAX_PATH - 1;
Assert(bstrUDN);
wcscpy(pDevNode->pszUDN, L"");
wcsncat(pDevNode->pszUDN,(PWSTR)bstrUDN,cchMax);
Assert(bstrDisplayName);
wcscpy(pDevNode->pszDisplayName, L"");
wcsncat(pDevNode->pszDisplayName,(PWSTR)bstrDisplayName,cchMax);
Assert(bstrType);
wcscpy(pDevNode->pszType, L"");
wcsncat(pDevNode->pszType,(PWSTR)bstrType,cchMax);
wcscpy(pDevNode->pszPresentationURL, L"");
if (bstrPresentationURL)
{
wcsncat(pDevNode->pszPresentationURL,
(PWSTR)bstrPresentationURL,
cchMax);
}
wcscpy(pDevNode->pszDescription, L"");
if (bstrDescription)
{
wcsncat(pDevNode->pszDescription,
(PWSTR)bstrDescription,
cchMax);
}
hr = HrMakeUPnPDevicePidl(pDevNode, ppidl);
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
TraceTag(ttidShellFolder, "Failed in pDevice->get_Description from HrMakeUPnPDevicePidl");
}
}
else
{
TraceTag(ttidShellFolder, "Failed in pDevice->get_PresentationURL from HrMakeUPnPDevicePidl");
}
}
else
{
TraceTag(ttidShellFolder, "Failed in pDevice->get_Type from HrMakeUPnPDevicePidl");
}
}
else
{
TraceTag(ttidShellFolder, "Failed in pDevice->get_FriendlyName from HrMakeUPnPDevicePidl");
}
}
else
{
TraceTag(ttidShellFolder, "Failed in pDevice->get_UniqueDeviceName from HrMakeUPnPDevicePidl");
}
SysFreeString(bstrUDN);
SysFreeString(bstrDisplayName);
SysFreeString(bstrPresentationURL);
SysFreeString(bstrType);
SysFreeString(bstrDescription);
ReleaseObj(pDevice);
TraceError("CUPnPDeviceFolder::HrMakeUPnPDevicePidl", hr);
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::ParseDisplayName
//
// Purpose: Translates a file object or folder's display name into an
// item identifier.
//
// Arguments:
// hwndOwner [in] Handle of owner window
// pbcReserved [in] Reserved
// lpszDisplayName [in] Pointer to diplay name
// pchEaten [out] Pointer to value for parsed characters
// ppidl [out] Pointer to new item identifier list
// pdwAttributes [out] Address receiving attributes of file object
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise
//
// Author: tongl 16 Feb 2000
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::ParseDisplayName(
HWND hwndOwner,
LPBC pbcReserved,
LPOLESTR lpszDisplayName,
ULONG * pchEaten,
LPITEMIDLIST * ppidl,
ULONG * pdwAttributes)
{
TraceTag(ttidShellFolderIface, "CUPnPDeviceFolder::ParseDisplayName");
HRESULT hr = S_OK;
// note: this is bogus, but we're doing this all over...
Assert(lpszDisplayName);
Assert(ppidl);
*ppidl = NULL;
// first, make sure that this is one of our display names:
// it must start with c_szDelegateFolderPrefix
//
int result;
result = wcsncmp(lpszDisplayName,
c_szDelegateFolderPrefix,
c_cchDelegateFolderPrefix);
if (0 == result)
{
LPCWSTR pszUdn = NULL;
LPITEMIDLIST pidlDevice = NULL;
FolderDeviceNode * pDeviceNode = NULL;
// this is OK since lpszDisplayName is an LPOLESTR
pszUdn = lpszDisplayName + c_cchDelegateFolderPrefix;
// search our list of devices and try to find a matching UDN
if (g_CListFolderDeviceNode.FFind(pszUdn, &pDeviceNode))
{
Assert(pDeviceNode);
// yes, this is one of our devices, construct the pidl using
// the allocator we are given
hr = HrMakeUPnPDevicePidl(pDeviceNode, &pidlDevice);
if (SUCCEEDED(hr))
{
Assert(pidlDevice);
*ppidl = pidlDevice;
if (pdwAttributes)
{
*pdwAttributes = 0;
}
}
}
else
{
// no, we don't have such a device in the list
// (tongl): try to do a SearchByUDN before we fail the call,
// as this may be a device discovered by the search from the
// tray icon, and we are asked for the pidl to create a shortcut.
BSTR bstrUdn = NULL;
IUPnPDeviceFinder * pdf = NULL;
bstrUdn = ::SysAllocString(pszUdn);
if (bstrUdn)
{
hr = CoCreateInstance(CLSID_UPnPDeviceFinder, NULL, CLSCTX_INPROC_SERVER,
IID_IUPnPDeviceFinder, (LPVOID *)&pdf);
if (SUCCEEDED(hr))
{
IUPnPDevice * pdev = NULL;
hr = pdf->FindByUDN(bstrUdn, &pdev);
if (S_OK == hr)
{
hr = HrMakeUPnPDevicePidl(pdev, &pidlDevice);
ReleaseObj(pdev);
}
ReleaseObj(pdf);
}
::SysFreeString(bstrUdn);
}
else
{
hr = E_OUTOFMEMORY;
TraceError("CUPnPDeviceFolder::ParseDisplayName: "
"SysAllocString", hr);
}
}
}
else
{
TraceTag(ttidShellFolderIface,
"CUPnPDeviceFolder::ParseDisplayName: "
"passed non-upnp display name");
hr = E_FAIL;
}
TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::ParseDisplayName");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::EnumObjects
//
// Purpose: Determines the contents of a folder by creating an item
// enumeration object (a set of item identifiers) that can be
// retrieved using the IEnumIDList interface.
//
// Arguments:
// hwndOwner [in] Handle of owner window
// grfFlags [in] Items to include in enumeration
// ppenumIDList [out] Pointer to IEnumIDList
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::EnumObjects(
HWND hwndOwner,
DWORD grfFlags,
LPENUMIDLIST * ppenumIDList)
{
HRESULT hr = NOERROR;
Assert(ppenumIDList);
*ppenumIDList = NULL;
if ((grfFlags & SHCONTF_FOLDERS) && !(grfFlags & SHCONTF_NONFOLDERS))
{
// if shell wants to enumerate only folders, we don't return anything
hr = E_NOTIMPL;
}
else
{
// Create the IEnumIDList object (CUPnPDeviceFolderEnum)
//
hr = CUPnPDeviceFolderEnum::CreateInstance (
IID_IEnumIDList,
reinterpret_cast<void**>(ppenumIDList));
if (SUCCEEDED(hr))
{
Assert(*ppenumIDList);
// Call the PidlInitialize function to allow the enumeration
// object to copy the list.
//
reinterpret_cast<CUPnPDeviceFolderEnum *>(*ppenumIDList)->Initialize(
m_pidlFolderRoot, this);
}
else
{
// On all failures, this should be NULL.
if (*ppenumIDList)
{
ReleaseObj(*ppenumIDList);
}
*ppenumIDList = NULL;
}
}
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::EnumObjects");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::BindToObject
//
// Purpose: Creates an IShellFolder object for a subfolder.
//
// Arguments:
// pidl [in] Pointer to an ITEMIDLIST
// pbcReserved [in] Reserved - specify NULL
// riid [in] Interface to return
// ppvOut [out] Address that receives interface pointer;
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise
//
// Author: jeffspr 18 Oct 1997
//
// Notes: We don't need this function, since we don't have subfolders.
//
STDMETHODIMP CUPnPDeviceFolder::BindToObject(
LPCITEMIDLIST pidl,
LPBC pbcReserved,
REFIID riid,
LPVOID * ppvOut)
{
HRESULT hr = E_NOTIMPL;
// Note - If we add code here, then we ought to param check pidl
//
Assert(pidl);
*ppvOut = NULL;
TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::BindToObject");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::BindToStorage
//
// Purpose: Reserved for a future use. This method should
// return E_NOTIMPL.
//
// Arguments:
// pidl [] Pointer to an ITEMIDLIST
// pbcReserved [] Reserved¾specify NULL
// riid [] Interface to return
// ppvObj [] Address that receives interface pointer);
//
// Returns: E_NOTIMPL always
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::BindToStorage(
LPCITEMIDLIST pidl,
LPBC pbcReserved,
REFIID riid,
LPVOID * ppvObj)
{
HRESULT hr = E_NOTIMPL;
// Note - If we add code here, then we ought to param check pidl
//
Assert(pidl);
*ppvObj = NULL;
TraceHr(ttidShellFolder, FAL, hr, FALSE, "CUPnPDeviceFolder::BindToStorage");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::CompareIDs
//
// Purpose: Determines the relative ordering of two file objects or
// folders, given their item identifier lists.
//
// Arguments:
// lParam [in] Type of comparison to perform
// pidl1 [in] Address of ITEMIDLIST structure
// pidl2 [in] Address of ITEMIDLIST structure
//
// Returns: Returns a handle to a result code. If this method is
// successful, the CODE field of the status code (SCODE) has
// the following meaning:
//
// CODE field Meaning
// ---------- -------
// Less than zero The first item should precede the second
// (pidl1 < pidl2).
// Greater than zero The first item should follow the second
// (pidl1 > pidl2)
// Zero The two items are the same (pidl1 = pidl2)
//
// Author: jeffspr 18 Oct 1997
//
// Notes: Passing 0 as the lParam indicates sort by name.
// 0x00000001-0x7fffffff are for folder specific sorting rules.
// 0x80000000-0xfffffff are used the system.
//
STDMETHODIMP CUPnPDeviceFolder::CompareIDs(
LPARAM lParam,
LPCITEMIDLIST pidl1,
LPCITEMIDLIST pidl2)
{
HRESULT hr = S_OK;
PUPNPDEVICEFOLDPIDL pupdfp1 = NULL;
PUPNPDEVICEFOLDPIDL pupdfp2 = NULL;
CUPnPDeviceFoldPidl udfp1;
CUPnPDeviceFoldPidl udfp2;
LPCWSTR psz1;
LPCWSTR psz2;
CONST INT ciTieBreaker = -1;
int iCompare = 0;
int result;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::CompareIDs");
// Make sure that the pidls passed in are our pidls.
//
if (!FIsUPnPDeviceFoldPidl(pidl1) || !FIsUPnPDeviceFoldPidl(pidl2))
{
hr = E_INVALIDARG;
goto Exit;
}
pupdfp1 = ConvertToUPnPDevicePIDL(pidl1);
pupdfp2 = ConvertToUPnPDevicePIDL(pidl2);
hr = udfp1.HrInit(pupdfp1);
if (FAILED(hr))
{
goto Exit;
}
hr = udfp2.HrInit(pupdfp2);
if (FAILED(hr))
{
goto Exit;
}
// We use the following procedure to compare PIDLs:
// 1. If two UDNs are the same, the PIDLs are the same
// 2. Otherwise, the PIDLs _must_ be different, and
// A. will be sorted based on the desired column
// B. If the column text matches, we'll return
// ciTieBreaker (-1), so that the PIDLs are
// distinguished.
//
psz1 = udfp1.PszGetUDNPointer();
psz2 = udfp2.PszGetUDNPointer();
Assert(psz1 && psz2);
result = wcscmp(psz1, psz2);
if (0 == result)
{
// The UDNs match, these are effectively the same PIDL
TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: "
"UDN equal, automatically returning equality");
iCompare = 0;
}
else
{
// Sort based on the desired column
switch(lParam & SHCIDS_COLUMNMASK)
{
case ICOL_NAME:
psz1 = udfp1.PszGetNamePointer();
psz2 = udfp2.PszGetNamePointer();
break;
case ICOL_URL:
psz1 = udfp1.PszGetURLPointer();
psz2 = udfp2.PszGetURLPointer();
break;
case ICOL_UDN:
psz1 = udfp1.PszGetUDNPointer();
psz2 = udfp2.PszGetUDNPointer();
break;
case ICOL_TYPE:
psz1 = udfp1.PszGetTypePointer();
psz2 = udfp2.PszGetTypePointer();
break;
default:
AssertSz(FALSE, "Sorting on unknown category");
break;
}
Assert(psz1 && psz2);
iCompare = _wcsicmp(psz1, psz2);
// Ensure that we don't return equality
if (0 == iCompare)
{
TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: "
"UDNs unequal but column-text equal, breaking tie");
iCompare = ciTieBreaker;
}
}
if (SUCCEEDED(hr))
{
hr = ResultFromShort(iCompare);
}
TraceTag(ttidShellFolder, "CUPnPDeviceFolder::CompareIDs: returning %d", iCompare);
Exit:
TraceHr(ttidError, FAL, hr, SUCCEEDED(hr), "CUPnPDeviceFolder::CompareIDs");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::CreateViewObject
//
// Purpose: Creates a view object of a folder.
//
// Arguments:
// hwndOwner [in] Handle of owner window
// riid [in] Interface identifier
// ppvOut [none] Reserved
//
// Returns: Returns NOERROR if successful or an OLE defined error
// value otherwise.
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::CreateViewObject(
HWND hwndOwner,
REFIID riid,
LPVOID * ppvOut)
{
HRESULT hr = E_NOINTERFACE;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::CreateViewObject");
Assert(ppvOut);
// Pre-initialize the out param, per OLE guidelines
//
*ppvOut = NULL;
// (tongl) We are a delegate folder now, CreateViewObject is never called.
return E_NOTIMPL;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::GetAttributesOf
//
// Purpose: Retrieves the attributes that all passed-in objects (file
// objects or subfolders) have in common.
//
// Arguments:
// cidl [in] Number of file objects
// apidl [in] Pointer to array of pointers to ITEMIDLIST structures
// rgfInOut [out] Address of value containing attributes of the
// file objects
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise.
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::GetAttributesOf(
UINT cidl,
LPCITEMIDLIST * apidl,
ULONG * rgfInOut)
{
HRESULT hr = S_OK;
ULONG rgfMask = 0;
PUPNPDEVICEFOLDPIDL pupdfp = NULL;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetAttributesOf");
if (cidl > 0)
{
// Prepopulate with all values (removed CANCOPY and CANMOVE)
//
rgfMask = /* SFGAO_CANDELETE | */ // Don't support delete
SFGAO_CANRENAME |
SFGAO_CANLINK |
SFGAO_HASPROPSHEET;
// Disable propsheets for > 1 object
//
if (cidl > 1)
{
rgfMask &= ~SFGAO_HASPROPSHEET;
}
}
else
{
// Apparently, we're called with 0 objects to indicate that we're
// supposed to return flags for the folder itself, not an individual
// object. Weird.
rgfMask = SFGAO_CANCOPY |
SFGAO_CANDELETE |
SFGAO_CANMOVE |
SFGAO_CANRENAME;
}
if (SUCCEEDED(hr))
{
*rgfInOut &= rgfMask;
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::GetUIObjectOf
//
// Purpose: Creates a COM object that can be used to carry out actions
// on the specified file objects or folders, typically, to
// create context menus or carry out drag-and-drop operations.
//
// Arguments:
// hwndOwner [in] Handle to owner window
// cidl [in] Number of objects specified in apidl
// apidl [in] Pointer to an array of pointers to an ITEMIDLIST
// riid [in] Interface to return
// prgfInOut [none] Reserved
// ppvOut [out] Address to receive interface pointer
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::GetUIObjectOf(
HWND hwndOwner,
UINT cidl,
LPCITEMIDLIST * apidl,
REFIID riid,
UINT * prgfInOut,
LPVOID * ppvOut)
{
HRESULT hr = E_NOINTERFACE;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetUIObjectOf");
if (cidl >= 1)
{
Assert(apidl);
Assert(apidl[0]);
Assert(ppvOut);
if (riid == IID_IDataObject)
{
// Need to initialize so the SUCCEEED check below doesn't fail.
//
hr = S_OK;
Assert(m_pidlFolderRoot);
if (SUCCEEDED(hr))
{
Assert(m_pidlFolderRoot);
// Internal IDataObject impl removed. Replaced with common
// shell code.
//
hr = CIDLData_CreateFromIDArray(m_pidlFolderRoot, cidl, apidl, (IDataObject **) ppvOut);
}
}
else if (riid == IID_IContextMenu)
{
// Create our context menu object if only one device is selected
//
hr = CUPnPDeviceFolderContextMenu::CreateInstance (
IID_IContextMenu,
reinterpret_cast<void**>(ppvOut),
CMT_OBJECT,
hwndOwner,
cidl,
apidl,
this);
if (SUCCEEDED(hr))
{
Assert(*ppvOut);
}
else
{
hr = E_NOINTERFACE;
}
}
else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
{
if (cidl == 1)
{
hr = CUPnPDeviceFolderExtractIcon::CreateInstance (
apidl[0],
riid,
reinterpret_cast<void**>(ppvOut));
if(SUCCEEDED(hr))
{
hr = reinterpret_cast<CUPnPDeviceFolderExtractIcon *>
(*ppvOut)->Initialize((LPITEMIDLIST)apidl[0]);
}
if (SUCCEEDED(hr))
{
Assert(*ppvOut);
}
}
else
{
hr = E_NOINTERFACE;
}
}
else if (riid == IID_IDropTarget)
{
// We don't support drag/drop
//
hr = E_NOINTERFACE;
}
else if (riid == IID_IQueryInfo)
{
if (cidl == 1)
{
// Create the IQueryInfo interface
hr = CUPnPDeviceFolderQueryInfo::CreateInstance (
IID_IQueryInfo,
reinterpret_cast<void**>(ppvOut));
if (SUCCEEDED(hr))
{
Assert(*ppvOut);
reinterpret_cast<CUPnPDeviceFolderQueryInfo *>
(*ppvOut)->PidlInitialize((LPITEMIDLIST)apidl[0]);
// Normalize return code
//
hr = NOERROR;
}
}
else
{
AssertSz(FALSE, "GetUIObjectOf asked for query info for more than one item!");
hr = E_NOINTERFACE;
}
}
else
{
TraceTag(ttidShellFolder, "CUPnPDeviceFolder::GetUIObjectOf asked for object "
"that it didn't know how to create. 0x%08x", riid.Data1);
hr = E_NOINTERFACE;
}
}
if (FAILED(hr))
{
*ppvOut = NULL;
}
TraceHr(ttidError, FAL, hr, (hr == E_NOINTERFACE), "CUPnPDeviceFolder::GetUIObjectOf");
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: HrTszToStrRet
//
// Purpose: Convert a TCHAR string to a STRRET, depending on the platform
//
// Arguments:
// pszName [in] input string
// pStrRet [in/out] output STRRET
//
// Returns:
//
// Author: jeffspr 25 Jan 2000
//
// Notes:
//
HRESULT HrTszToStrRet(LPCTSTR pszName, STRRET *pStrRet)
{
HRESULT hr = S_OK;
#ifdef UNICODE
pStrRet->uType = STRRET_WSTR;
// Allocate a new POLESTR block, which the shell can then free,
// and copy the displayable portion to it.
//
hr = HrDupeShellString(
pszName,
&pStrRet->pOleStr);
#else
pStrRet->uType = STRRET_CSTR;
lstrcpyn(pStrRet->cStr, pszName, celems(pStrRet->cStr));
hr = S_OK;
#endif
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::GetDisplayNameOf
//
// Purpose: Retrieves the display name for the specified file object or
// subfolder, returning it in a STRRET structure.
//
// Arguments:
// pidl [in] Pointer to an ITEMIDLIST
// uFlags [in] Type of display to return
// lpName [out] Pointer to a STRRET structure
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise.
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::GetDisplayNameOf(
LPCITEMIDLIST pidl,
DWORD uFlags,
LPSTRRET lpName)
{
HRESULT hr = S_OK;
PCWSTR pwszStrToCopy = NULL;
PTSTR pszTemp = NULL;
PUPNPDEVICEFOLDPIDL pupdfp = NULL;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::GetDisplayNameOf");
Assert(pidl);
Assert(lpName);
if (!pidl || !lpName)
{
hr = E_INVALIDARG;
}
else if (FIsUPnPDeviceFoldPidl(pidl))
{
CUPnPDeviceFoldPidl udfp;
pupdfp = ConvertToUPnPDevicePIDL(pidl);
#ifdef DBG
// Throw these in here just so I can quickly peek at the values
// set while I'm dorking around in the debugger.
//
DWORD dwInFolder = (uFlags & SHGDN_INFOLDER);
DWORD dwForAddressBar = (uFlags & SHGDN_FORADDRESSBAR);
DWORD dwForParsing = (uFlags & SHGDN_FORPARSING);
#endif
if (uFlags & SHGDN_FORPARSING)
{
#if 0
AssertSz(FALSE, "SHGDN_FORPARSING support NYI in GetDisplayNameOf");
#endif
}
hr = udfp.HrInit(pupdfp);
if (SUCCEEDED(hr))
{
pwszStrToCopy = udfp.PszGetNamePointer();
Assert(pwszStrToCopy);
pszTemp = TszFromWsz(pwszStrToCopy);
if (!pszTemp)
{
hr = E_OUTOFMEMORY;
}
else
{
hr = HrTszToStrRet(pszTemp, lpName);
free((PVOID) pszTemp);
}
}
}
else
{
// not a valid connections pidl (neither Win2K nor Win98).
//
hr = E_INVALIDARG;
}
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::GetDisplayNameOf");
return hr;
}
//+---------------------------------------------------------------------------
//
// Member: CUPnPDeviceFolder::SetNameOf
//
// Purpose: Changes the name of a file object or subfolder, changing its
// item identifier in the process.
//
// Arguments:
// hwndOwner [in] Handle of owner window
// pidl [in] Pointer to an ITEMIDLIST structure
// lpszName [in] Pointer to string specifying new display name
// uFlags [in] Type of name specified in lpszName
// ppidlOut [out] Pointer to new ITEMIDLIST
//
// Returns: Returns NOERROR if successful or an OLE-defined error
// value otherwise.
//
// Author: jeffspr 18 Oct 1997
//
// Notes:
//
STDMETHODIMP CUPnPDeviceFolder::SetNameOf(
HWND hwndOwner,
LPCITEMIDLIST pidl,
LPCOLESTR lpszName,
DWORD uFlags,
LPITEMIDLIST * ppidlOut)
{
HRESULT hr = S_OK;
PUPNPDEVICEFOLDPIDL pupdfp = NULL;
TraceTag(ttidShellFolderIface, "OBJ: CUPnPDeviceFolder::SetNameOf");
Assert(hwndOwner);
Assert(pidl);
Assert(lpszName);
if (!pidl && !lpszName)
{
hr = E_INVALIDARG;
}
else if (!*lpszName)
{
hr = S_OK;
}
else if (FIsUPnPDeviceFoldPidl(pidl))
{
CUPnPDeviceFoldPidl udfp;
pupdfp = ConvertToUPnPDevicePIDL(pidl);
hr = udfp.HrInit(pupdfp);
if (SUCCEEDED(hr))
{
// Change the name of the item in the PIDL
hr = udfp.HrSetName(lpszName);
}
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidlOut;
// Persist the PIDL data to a new PIDL so we can generate an event
// for it
hr = udfp.HrPersist(m_pDelegateMalloc, &pidlOut);
if (SUCCEEDED(hr))
{
NAME_MAP * pnm;
pnm = new NAME_MAP;
if (pnm)
{
// Copy the name and UDN to a struct to keep in a list
// of mapped UDNs to friendly names.
//
pnm->szName = TszDupTsz(udfp.PszGetNamePointer());
if (!pnm->szName)
{
hr = E_OUTOFMEMORY;
}
else
{
pnm->szUdn = TszDupTsz(udfp.PszGetUDNPointer());
if (!pnm->szUdn)
{
hr = E_OUTOFMEMORY;
}
else
{
// Delete the item and re-add it again
g_CListNameMap.FDelete(udfp.PszGetUDNPointer());
g_CListNameMap.FAdd(pnm);
// Notify the shell of the rename
GenerateEvent(SHCNE_RENAMEITEM, m_pidlFolderRoot,
pidl, pidlOut);
FolderDeviceNode * pDeviceNode;
if (g_CListFolderDeviceNode.FFind(pnm->szUdn,
&pDeviceNode))
{
// Delete the node's old name and give it the new
// one
//
wcscpy(pDeviceNode->pszDisplayName,
pnm->szName);
}
}
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (ppidlOut)
{
*ppidlOut = pidlOut;
}
else
{
FreeIDL(pidlOut);
}
}
}
}
else
{
// not a valid UPnP pidl
//
hr = E_INVALIDARG;
}
TraceHr(ttidError, FAL, hr, FALSE, "CUPnPDeviceFolder::SetNameOf");
return hr;
}